Vocal Implants (#32837)

* Vocal Implants

* Thing itself

* Attempt at NTSL compiler/interpereter refactor, revert commit if goes awry

* I always forget this

* Moves this here

* Removing this var, redundant

* Skeleton code for this

* Super call

* File itself, again

* Option to go into this, no code yet

* Dots, not commas

* Start on window and editor itself

* Start on window and editor itself

* Some vars for this, don't even know if I'll need this proc

* In action here too, after any word filters

* Vars and procs beginning work

* Broadcast function

* Way to test it

* Some error feedback

* This should be an alert

* Some admin logging

* Clarity

Co-authored-by: kanef <kanef9x@protonmail.com>
This commit is contained in:
kane-f
2022-07-10 17:37:14 +01:00
committed by GitHub
parent 45c8af82d1
commit fc276c5c09
16 changed files with 893 additions and 376 deletions

View File

@@ -860,6 +860,15 @@ var/list/uplink_items = list()
discounted_cost = 6
jobs_with_discount = list("Mechanic")
// A telecomms technician traitor item
/datum/uplink_item/jobspecific/engineering/vocal
name = "Vocal Implant"
desc = "An implant usable after being injected into one's body. Settings can be input to modify speech patterns in the affected's voice once implanted."
item = /obj/item/weapon/storage/box/syndie_kit/imp_vocal
cost = 8
discounted_cost = 6
jobs_with_discount = list("Mechanic", "Chief Engineer")
/datum/uplink_item/jobspecific/cargo
category = "Cargo and Mining Specials"

View File

@@ -313,7 +313,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
/obj/machinery/telecomms/receiver/Destroy()
if(blackout_active)
malf_radio_blackout = FALSE
malf_radio_blackout = FALSE
..()
/obj/machinery/telecomms/receiver/receive_signal(datum/signal/signal)
@@ -642,7 +642,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
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/datum/n_Compiler/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"

View File

@@ -142,3 +142,37 @@
c.scanned = I
c.scanned.forceMove(c)
update()
/obj/item/weapon/implanter/vocal
name = "implanter (V)"
desc = "A small device used to apply implants to people."
imp_type = /obj/item/weapon/implant/vocal
var/storedcode = "" // code stored
/obj/item/weapon/implanter/vocal/attack_self(mob/user)
if(istype(imp,imp_type))
var/uselevel = alert(user, "Which level of complexity do you want to work with? Basic is a simple word replacement with regex, advanced is an implementation of NTSL as found in telecomms servers.", "Level of vocal manipulation", "Basic", "Advanced")
if(uselevel == "Basic")
var/obj/item/weapon/implant/vocal/V = imp
var/input = input(user, "Enter an input phrase, regex works here:", "Input phrase") as text
if(!input)
return
var/keepgoing = FALSE
var/list/outputs = list()
do
var/output = input(user, "Enter an output phrase:", "Output phrase") as text
if(!output)
return
outputs.Add(output)
keepgoing = alert(user, "Add another output?", "Output phrase", "Yes", "No") == "Yes"
while(keepgoing)
var/casesense = alert(user, "Case sensitive?", "Case sensitivity","Yes","No") == "Yes"
if(input && outputs.len)
V.filter.addPickReplacement(input,outputs,casesense)
else if(uselevel == "Advanced")
winshow(user, "Vocal Implant IDE", 1) // show the IDE
winset(user, "vicode", "is-disabled=false")
winset(user, "vicode", "text=\"\"")
var/showcode = replacetext(storedcode, "\\\"", "\\\\\"")
showcode = replacetext(storedcode, "\"", "\\\"")
winset(user, "vicode", "text=\"[showcode]\"")

View File

@@ -0,0 +1,53 @@
/obj/item/weapon/implant/vocal
name = "vocal implant"
icon_state = "implant_evil"
var/datum/speech_filter/filter
var/list/memory = list() // stored memory
var/rawcode = "" // the code to compile (raw text)
var/datum/n_Compiler/vocal_implant/Compiler // the compiler that compiles and runs the code
/obj/item/weapon/implant/vocal/New()
..()
filter = new
Compiler = new
Compiler.Holder = src
/obj/item/weapon/implant/vocal/Destroy()
// Garbage collects all the NTSL datums.
if(Compiler)
Compiler.GC()
Compiler = null
..()
/obj/item/weapon/implant/vocal/proc/setcode(var/t)
if(t)
if(istext(t))
rawcode = t
/obj/item/weapon/implant/vocal/proc/compile(var/mob/user)
if(Compiler)
admin_log(user)
return Compiler.Compile(rawcode)
/obj/item/weapon/implant/vocal/proc/admin_log(var/mob/mob)
var/msg = "[key_name(mob)] has compiled a script to a vocal implant"
diary << msg
diary << rawcode
investigation_log(I_NTSL, "[msg]<br /><pre>[rawcode]</pre>")
if (length(rawcode)) // Let's not bother the admins for empty code.
message_admins("[msg] ([formatJumpTo(mob)])", 0, 1)
/obj/item/weapon/implant/vocal/get_data()
return {"
<b>Implant Specifications:</b><BR>
<b>Name:</b> Chameleon Voice-Changing Implant<BR>
<b>Life:</b> ??? <BR>
<b>Important Notes:</b> Any humanoid injected with this implant will have their vocal chords muted and replaced with a replica of their own voice on the reception of key phrases.<BR>
<HR>
<b>Implant Details:</b><BR>
<b>Function:</b> Contains a small pod of nanobots that manipulate the host's mental speech functions and processing.<BR>
<b>Special Features:</b> Vocal manipulation.<BR>
<b>Integrity:</b> Implant will last so long as the subject is speaking."}

View File

@@ -260,6 +260,10 @@
name = "box (E)"
items_to_spawn = list(/obj/item/weapon/implanter/explosive)
/obj/item/weapon/storage/box/syndie_kit/imp_vocal
name = "box (V)"
items_to_spawn = list(/obj/item/weapon/implanter/vocal)
/obj/item/weapon/storage/box/syndie_kit/imp_uplink
name = "Uplink Implant (with injector)"
items_to_spawn = list(/obj/item/weapon/implanter/uplink)

View File

@@ -218,7 +218,9 @@ var/list/headset_modes = list(
var/message_range = 7
treat_speech(speech)
if(!speech.message)
qdel(speech)
return
var/radio_return = get_speech_flags(message_mode)
if (speech_was_spoken_into_radio(message_mode))
speech.wrapper_classes.Add("spoken_into_radio")
@@ -513,6 +515,20 @@ var/list/headset_modes = list(
speech.message = replacetext(speech.message,";","") // motor mouth
speech.message = replacetext(speech.message,"-","") // motor mouth
for(var/obj/item/weapon/implant/vocal/VI in src)
if(VI.imp_in == src)
var/original_message = speech.message
speech.message = VI.filter.FilterSpeech(speech.message)
var/datum/signal/signal = new /datum/signal
signal.data["message"] = speech.message
signal.data["reject"] = 0
signal.data["mob"] = src
signal.data["implant"] = VI
VI.Compiler.Run(signal)
speech.message = signal.data["reject"] ? null : signal.data["message"]
if(speech.message != original_message)
message_admins("The [VI] in [src] made \him say \"[speech.message]\" instead of \"[original_message]\" [formatJumpTo(src)]")
/mob/living/proc/get_speech_flags(var/message_mode)
switch(message_mode)
if(MODE_WHISPER, SPEECH_MODE_FINAL)
@@ -702,6 +718,9 @@ var/list/headset_modes = list(
log_whisper("[key_name(src)] ([formatLocation(src)]): [message]")
treat_speech(speech)
if(!speech.message)
qdel(speech)
return
// If whispering your last words, limit the whisper based on how close you are to death.
if(critical && !said_last_words)
@@ -714,6 +733,9 @@ var/list/headset_modes = list(
whispers = "whispers with their final breath"
said_last_words = src.stat
treat_speech(speech)
if(!speech.message)
qdel(speech)
return
var/listeners = get_hearers_in_view(1, src) | observers
var/eavesdroppers = get_hearers_in_view(2, src) - listeners
@@ -766,4 +788,4 @@ var/list/headset_modes = list(
output += uppertext(pick(muffle_syllables))
else
output += pick(muffle_syllables)
return output
return output

View File

@@ -1,358 +0,0 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33
/* --- Traffic Control Scripting Language --- */
// Nanotrasen TCS Language - Made by Doohl
/datum/n_Interpreter/TCS_Interpreter
var/datum/TCS_Compiler/Compiler
/datum/n_Interpreter/TCS_Interpreter/HandleError(datum/runtimeError/e)
Compiler.Holder.add_entry(e.ToString(), "Execution Error")
/datum/n_Interpreter/TCS_Interpreter/GC()
..()
Compiler = null
/datum/TCS_Compiler
var/datum/n_Interpreter/TCS_Interpreter/interpreter
var/obj/machinery/telecomms/server/Holder // the server that is running the code
var/ready = 1 // 1 if ready to run code
/* -- Set ourselves to Garbage Collect -- */
/datum/TCS_Compiler/proc/GC()
Holder = null
if(interpreter)
interpreter.GC()
/* -- Compile a raw block of text -- */
/datum/TCS_Compiler/proc/Compile(code as message)
var/datum/n_scriptOptions/nS_Options/options = new()
var/datum/n_Scanner/nS_Scanner/scanner = new(code, options)
var/list/tokens = scanner.Scan()
var/datum/n_Parser/nS_Parser/parser = new(tokens, options)
var/datum/node/BlockDefinition/GlobalBlock/program = parser.Parse()
var/list/returnerrors = list()
returnerrors += scanner.errors
returnerrors += parser.errors
if(returnerrors.len)
return returnerrors
interpreter = new(program)
interpreter.persist = 1
interpreter.Compiler= src
return returnerrors
/* -- Execute the compiled code -- */
/datum/TCS_Compiler/proc/Run(var/datum/signal/signal)
if(!ready)
return
if(!interpreter)
return
interpreter.container = src
interpreter.SetVar("TAU", TAU) // value of tau
interpreter.SetVar("PI", PI) // value of pi
interpreter.SetVar("E", E) // value of e
interpreter.SetVar("SQURT2", Sqrt2) // value of the square root of 2
interpreter.SetVar("FALSE", 0) // boolean shortcut to 0
interpreter.SetVar("false", 0) // boolean shortcut to 0
interpreter.SetVar("TRUE", 1) // boolean shortcut to 1
interpreter.SetVar("true", 1) // boolean shortcut to 1
interpreter.SetVar("NORTH", NORTH) // NORTH (1)
interpreter.SetVar("SOUTH", SOUTH) // SOUTH (2)
interpreter.SetVar("EAST", EAST) // EAST (4)
interpreter.SetVar("WEST", WEST) // WEST (8)
// Channel macros
interpreter.SetVar("$common", COMMON_FREQ)
interpreter.SetVar("$science", SCI_FREQ)
interpreter.SetVar("$command", COMM_FREQ)
interpreter.SetVar("$medical", MED_FREQ)
interpreter.SetVar("$engineering", ENG_FREQ)
interpreter.SetVar("$security", SEC_FREQ)
interpreter.SetVar("$supply", SUP_FREQ)
// Signal data
interpreter.SetVar("$content", signal.data["message"])
interpreter.SetVar("$freq", signal.frequency)
interpreter.SetVar("$source", signal.data["name"])
interpreter.SetVar("$job", signal.data["job"])
interpreter.SetVar("$sign", signal)
interpreter.SetVar("$pass", !(signal.data["reject"])) // if the signal isn't rejected, pass = 1; if the signal IS rejected, pass = 0
// Set up the script procs
/*
-> Send another signal to a server
@format: broadcast(content, frequency, source, job)
@param content: Message to broadcast
@param frequency: Frequency to broadcast to
@param source: The name of the source you wish to imitate. Must be stored in stored_names list.
@param job: The name of the job.
*/
interpreter.SetProc("broadcast", "tcombroadcast", signal, list("message", "freq", "source", "job"))
/*
-> Send a code signal.
@format: signal(frequency, code)
@param frequency: Frequency to send the signal to
@param code: Encryption code to send the signal with
*/
interpreter.SetProc("signal", "signaler", signal, list("freq", "code"))
/*
-> Store a value permanently to the server machine (not the actual game hosting machine, the ingame machine)
@format: mem(address, value)
@param address: The memory address (string index) to store a value to
@param value: The value to store to the memory address
*/
interpreter.SetProc("mem", "mem", signal, list("address", "value"))
/*
-> Delay code for a given amount of deciseconds
@format: sleep(time)
@param time: time to sleep in deciseconds (1/10th second)
*/
interpreter.SetProc("sleep", /proc/delay)
/*
-> Replaces a string with another string
@format: replace(string, substring, replacestring)
@param string: the string to search for substrings (best used with $content$ constant)
@param substring: the substring to search for
@param replacestring: the string to replace the substring with
*/
interpreter.SetProc("replace", /proc/n_replacetext)
/*
-> Locates an element/substring inside of a list or string
@format: find(haystack, needle, start = 1, end = 0)
@param haystack: the container to search
@param needle: the element to search for
@param start: the position to start in
@param end: the position to end in
*/
interpreter.SetProc("find", /proc/smartfind)
/*
-> Finds the length of a string or list
@format: length(container)
@param container: the list or container to measure
*/
interpreter.SetProc("length", /proc/smartlength)
/* -- Clone functions, carried from default BYOND procs --- */
// vector namespace
interpreter.SetProc("vector", /proc/n_list)
interpreter.SetProc("at", /proc/n_listpos)
interpreter.SetProc("copy", /proc/n_listcopy)
interpreter.SetProc("push_back", /proc/n_listadd)
interpreter.SetProc("remove", /proc/n_listremove)
interpreter.SetProc("cut", /proc/n_listcut)
interpreter.SetProc("swap", /proc/n_listswap)
interpreter.SetProc("insert", /proc/n_listinsert)
interpreter.SetProc("pick", /proc/n_pick)
interpreter.SetProc("prob", /proc/prob_chance)
interpreter.SetProc("substr", /proc/docopytext)
interpreter.SetProc("shuffle", /proc/shuffle)
interpreter.SetProc("uniquevector", /proc/uniquelist)
interpreter.SetProc("text2vector", /proc/n_splittext)
interpreter.SetProc("text2vectorEx",/proc/splittextEx)
interpreter.SetProc("vector2text", /proc/vg_jointext)
// Strings
interpreter.SetProc("lower", /proc/n_lower)
interpreter.SetProc("upper", /proc/n_upper)
interpreter.SetProc("explode", /proc/string_explode)
interpreter.SetProc("repeat", /proc/n_repeat)
interpreter.SetProc("reverse", /proc/reverse_text)
interpreter.SetProc("tonum", /proc/n_str2num)
interpreter.SetProc("capitalize", /proc/capitalize)
//interpreter.SetProc("replacetextEx",/proc/n_replacetextEx)
// Numbers
interpreter.SetProc("tostring", /proc/n_num2str)
interpreter.SetProc("sqrt", /proc/n_sqrt)
interpreter.SetProc("abs", /proc/n_abs)
interpreter.SetProc("floor", /proc/Floor)
interpreter.SetProc("ceil", /proc/Ceiling)
interpreter.SetProc("round", /proc/n_round)
interpreter.SetProc("clamp", /proc/n_clamp)
interpreter.SetProc("inrange", /proc/IsInRange)
interpreter.SetProc("rand", /proc/rand_chance)
interpreter.SetProc("arctan", /proc/Atan2)
interpreter.SetProc("lcm", /proc/Lcm)
interpreter.SetProc("gcd", /proc/Gcd)
interpreter.SetProc("mean", /proc/Mean)
interpreter.SetProc("root", /proc/Root)
interpreter.SetProc("sin", /proc/n_sin)
interpreter.SetProc("cos", /proc/n_cos)
interpreter.SetProc("arcsin", /proc/n_asin)
interpreter.SetProc("arccos", /proc/n_acos)
interpreter.SetProc("tan", /proc/Tan)
interpreter.SetProc("csc", /proc/Csc)
interpreter.SetProc("cot", /proc/Cot)
interpreter.SetProc("sec", /proc/Sec)
interpreter.SetProc("todegrees", /proc/ToDegrees)
interpreter.SetProc("toradians", /proc/ToRadians)
interpreter.SetProc("lerp", /proc/mix)
interpreter.SetProc("max", /proc/n_max)
interpreter.SetProc("min", /proc/n_min)
// End of Donkie~
// Time
interpreter.SetProc("time", /proc/time)
interpreter.SetProc("timestamp", /proc/timestamp)
// Run the compiled code
interpreter.Run()
// Backwards-apply variables onto signal data
/* sanitize (almost) EVERYTHING. fucking players can't be trusted with SHIT */
signal.data["message"] = interpreter.GetVar("$content")
signal.frequency = interpreter.GetCleanVar("$freq", signal.frequency)
var/setname = interpreter.GetCleanVar("$source", signal.data["name"])
if(signal.data["name"] != setname)
signal.data["realname"] = setname
signal.data["name"] = setname
signal.data["job"] = interpreter.GetCleanVar("$job", signal.data["job"])
signal.data["reject"] = !(interpreter.GetCleanVar("$pass")) // set reject to the opposite of $pass
// If the message is invalid, just don't broadcast it!
if(signal.data["message"] == "" || !signal.data["message"])
signal.data["reject"] = 1
/* -- Actual language proc code -- */
/var/const/SIGNAL_COOLDOWN = 20 // 2 seconds
/datum/signal/proc/mem(var/address, var/value)
if(istext(address))
var/obj/machinery/telecomms/server/S = data["server"]
if(!value && value != 0)
return S.memory[address]
else
S.memory[address] = value
/datum/signal/proc/signaler(var/freq = COMMON_FREQ, var/code = 30)
if(isnum(freq) && isnum(code))
var/obj/machinery/telecomms/server/S = data["server"]
if(S.last_signal + SIGNAL_COOLDOWN > world.timeofday && S.last_signal < MIDNIGHT_ROLLOVER)
return
S.last_signal = world.timeofday
var/datum/radio_frequency/connection = radio_controller.return_frequency(freq)
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
freq *= 10 // shift the decimal one place
freq = sanitize_frequency(freq)
code = round(code)
code = clamp(code, 0, 100)
var/datum/signal/signal = new /datum/signal
signal.source = S
signal.encryption = code
signal.data["message"] = "ACTIVATE"
connection.post_signal(S, signal)
S.investigation_log(I_WIRES, "NTSL-triggered signaler activated by [S.id] - [format_frequency(frequency)]/[code]")
/datum/signal/proc/tcombroadcast(var/message, var/freq, var/source, var/job)
var/datum/signal/newsign = new /datum/signal
var/obj/machinery/telecomms/server/S = data["server"]
var/obj/item/device/radio/hradio = S.server_radio
if(!hradio)
error("[src] has no radio.")
return
if((!message || message == "") && message != 0)
message = "*beep*"
if(!source)
source = "[html_encode(uppertext(S.id))]"
hradio = new // sets the hradio as a radio intercom
if(!freq || (!isnum(freq) && text2num(freq) == null))
freq = COMMON_FREQ
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
freq *= 10 // shift the decimal one place
if(!job)
job = "Unknown"
//SAY REWRITE RELATED CODE.
//This code is a little hacky, but it *should* work. Even though it'll result in a virtual speaker referencing another virtual speaker. vOv
var/atom/movable/virtualspeaker/virt = new /atom/movable/virtualspeaker(null)
virt.name = source
virt.job = job
//END SAY REWRITE RELATED CODE.
newsign.data["mob"] = virt
newsign.data["mobtype"] = /mob/living/carbon/human
newsign.data["name"] = source
newsign.data["realname"] = newsign.data["name"]
newsign.data["job"] = "[job]"
newsign.data["compression"] = 0
newsign.data["message"] = message
newsign.data["type"] = 2 // artificial broadcast
if(!isnum(freq))
freq = text2num(freq)
newsign.frequency = freq
var/datum/radio_frequency/connection = radio_controller.return_frequency(freq)
newsign.data["connection"] = connection
newsign.data["radio"] = hradio
newsign.data["vmessage"] = message
newsign.data["vname"] = source
newsign.data["vmask"] = 0
newsign.data["level"] = data["level"]
newsign.sanitize_data()
var/pass = S.relay_information(newsign, "/obj/machinery/telecomms/hub")
if(!pass)
S.relay_information(newsign, "/obj/machinery/telecomms/broadcaster") // send this simple message to broadcasters
spawn(50)
qdel(virt)

View File

@@ -0,0 +1,210 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33
/* --- Traffic Control Scripting Language --- */
// Nanotrasen TCS Language - Made by Doohl
/datum/n_Interpreter/TCS_Interpreter/HandleError(datum/runtimeError/e)
if(istype(Compiler,/datum/n_Compiler/TCS_Compiler))
var/datum/n_Compiler/TCS_Compiler/TCS = Compiler
TCS.Holder.add_entry(e.ToString(), "Execution Error")
/datum/n_Interpreter/TCS_Interpreter/AlertAdmins()
if(Compiler && !alertadmins)
if(istype(Compiler, /datum/n_Compiler/TCS_Compiler))
var/datum/n_Compiler/TCS_Compiler/TCS = Compiler
var/obj/machinery/telecomms/server/Holder = TCS.Holder
var/message = "Potential crash-inducing NTSL script detected at telecommunications server [Holder] ([Holder.x], [Holder.y], [Holder.z])."
alertadmins = 1
message_admins(message, 1)
/datum/n_Compiler/TCS_Compiler
var/obj/machinery/telecomms/server/Holder // the server that is running the code
interptype = /datum/n_Interpreter/TCS_Interpreter
/datum/n_Compiler/TCS_Compiler/GC()
Holder = null
..()
/datum/n_Compiler/TCS_Compiler/SetVars(var/datum/signal/signal)
..()
// Channel macros
interpreter.SetVar("$common", COMMON_FREQ)
interpreter.SetVar("$science", SCI_FREQ)
interpreter.SetVar("$command", COMM_FREQ)
interpreter.SetVar("$medical", MED_FREQ)
interpreter.SetVar("$engineering", ENG_FREQ)
interpreter.SetVar("$security", SEC_FREQ)
interpreter.SetVar("$supply", SUP_FREQ)
// Signal data
interpreter.SetVar("$content", signal.data["message"])
interpreter.SetVar("$freq", signal.frequency)
interpreter.SetVar("$source", signal.data["name"])
interpreter.SetVar("$job", signal.data["job"])
interpreter.SetVar("$sign", signal)
interpreter.SetVar("$pass", !(signal.data["reject"])) // if the signal isn't rejected, pass = 1; if the signal IS rejected, pass = 0
/datum/n_Compiler/TCS_Compiler/SetProcs(var/datum/signal/signal)
// Set up the script procs
/*
-> Send another signal to a server
@format: broadcast(content, frequency, source, job)
@param content: Message to broadcast
@param frequency: Frequency to broadcast to
@param source: The name of the source you wish to imitate. Must be stored in stored_names list.
@param job: The name of the job.
*/
interpreter.SetProc("broadcast", "tcombroadcast", signal, list("message", "freq", "source", "job"))
/*
-> Send a code signal.
@format: signal(frequency, code)
@param frequency: Frequency to send the signal to
@param code: Encryption code to send the signal with
*/
interpreter.SetProc("signal", "tcs_signaler", signal, list("freq", "code"))
/*
-> Store a value permanently to the server machine (not the actual game hosting machine, the ingame machine)
@format: tcs_mem(address, value)
@param address: The memory address (string index) to store a value to
@param value: The value to store to the memory address
*/
interpreter.SetProc("mem", "tcs_mem", signal, list("address", "value"))
..()
/* -- Execute the compiled code -- */
/datum/n_Compiler/TCS_Compiler/Run(var/datum/signal/signal)
..(signal)
// Backwards-apply variables onto signal data
/* sanitize (almost) EVERYTHING. fucking players can't be trusted with SHIT */
signal.data["message"] = interpreter.GetVar("$content")
signal.frequency = interpreter.GetCleanVar("$freq", signal.frequency)
var/setname = interpreter.GetCleanVar("$source", signal.data["name"])
if(signal.data["name"] != setname)
signal.data["realname"] = setname
signal.data["name"] = setname
signal.data["job"] = interpreter.GetCleanVar("$job", signal.data["job"])
signal.data["reject"] = !(interpreter.GetCleanVar("$pass")) // set reject to the opposite of $pass
// If the message is invalid, just don't broadcast it!
if(signal.data["message"] == "" || !signal.data["message"])
signal.data["reject"] = 1
/* -- Actual language proc code -- */
/var/const/SIGNAL_COOLDOWN = 20 // 2 seconds
/datum/signal/proc/tcs_mem(var/address, var/value)
if(istext(address))
var/obj/machinery/telecomms/server/S = data["server"]
if(!value && value != 0)
return S.memory[address]
else
S.memory[address] = value
/datum/signal/proc/tcs_signaler(var/freq = COMMON_FREQ, var/code = 30)
if(isnum(freq) && isnum(code))
var/obj/machinery/telecomms/server/S = data["server"]
if(S.last_signal + SIGNAL_COOLDOWN > world.timeofday && S.last_signal < MIDNIGHT_ROLLOVER)
return
S.last_signal = world.timeofday
var/datum/radio_frequency/connection = radio_controller.return_frequency(freq)
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
freq *= 10 // shift the decimal one place
freq = sanitize_frequency(freq)
code = round(code)
code = clamp(code, 0, 100)
var/datum/signal/signal = new /datum/signal
signal.source = S
signal.encryption = code
signal.data["message"] = "ACTIVATE"
connection.post_signal(S, signal)
S.investigation_log(I_WIRES, "NTSL-triggered signaler activated by [S.id] - [format_frequency(frequency)]/[code]")
/datum/signal/proc/tcombroadcast(var/message, var/freq, var/source, var/job)
var/datum/signal/newsign = new /datum/signal
var/obj/machinery/telecomms/server/S = data["server"]
var/obj/item/device/radio/hradio = S.server_radio
if(!hradio)
error("[src] has no radio.")
return
if((!message || message == "") && message != 0)
message = "*beep*"
if(!source)
source = "[html_encode(uppertext(S.id))]"
hradio = new // sets the hradio as a radio intercom
if(!freq || (!isnum(freq) && text2num(freq) == null))
freq = COMMON_FREQ
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
freq *= 10 // shift the decimal one place
if(!job)
job = "Unknown"
//SAY REWRITE RELATED CODE.
//This code is a little hacky, but it *should* work. Even though it'll result in a virtual speaker referencing another virtual speaker. vOv
var/atom/movable/virtualspeaker/virt = new /atom/movable/virtualspeaker(null)
virt.name = source
virt.job = job
//END SAY REWRITE RELATED CODE.
newsign.data["mob"] = virt
newsign.data["mobtype"] = /mob/living/carbon/human
newsign.data["name"] = source
newsign.data["realname"] = newsign.data["name"]
newsign.data["job"] = "[job]"
newsign.data["compression"] = 0
newsign.data["message"] = message
newsign.data["type"] = 2 // artificial broadcast
if(!isnum(freq))
freq = text2num(freq)
newsign.frequency = freq
var/datum/radio_frequency/connection = radio_controller.return_frequency(freq)
newsign.data["connection"] = connection
newsign.data["radio"] = hradio
newsign.data["vmessage"] = message
newsign.data["vname"] = source
newsign.data["vmask"] = 0
newsign.data["level"] = data["level"]
newsign.sanitize_data()
var/pass = S.relay_information(newsign, "/obj/machinery/telecomms/hub")
if(!pass)
S.relay_information(newsign, "/obj/machinery/telecomms/broadcaster") // send this simple message to broadcasters
spawn(50)
qdel(virt)

View File

@@ -0,0 +1,93 @@
/datum/n_Interpreter/vocal_implant/HandleError(datum/runtimeError/e)
if(istype(Compiler,/datum/n_Compiler/vocal_implant))
var/datum/n_Compiler/vocal_implant/VI = Compiler
if(istype(VI.Holder.loc,/obj/item/weapon/implanter/vocal))
var/obj/item/weapon/implanter/vocal/V = VI.Holder.loc
V.say(e.ToString())
/datum/n_Interpreter/vocal_implant/AlertAdmins()
if(Compiler && !alertadmins)
if(istype(Compiler, /datum/n_Compiler/vocal_implant))
var/datum/n_Compiler/vocal_implant/V = Compiler
var/obj/item/weapon/implant/vocal/VI = V.Holder
if(VI)
var/turf/T = get_turf(VI)
var/mob/M = get_holder_of_type(VI,/mob)
var/message = "Potential crash-inducing NTSL script detected in vocal implant[M ? " held by [M]" :""] at [T.x], [T.y], [T.z]."
alertadmins = 1
message_admins(message, 1)
/datum/n_Compiler/vocal_implant
var/obj/item/weapon/implant/vocal/Holder // the implant that is running the code
interptype = /datum/n_Interpreter/vocal_implant
/datum/n_Compiler/vocal_implant/GC()
Holder = null
..()
/datum/n_Compiler/vocal_implant/SetVars(var/datum/signal/signal)
..()
// Signal data
interpreter.SetVar("$content", signal.data["message"])
interpreter.SetVar("$pass", !(signal.data["reject"])) // if the signal isn't rejected, pass = 1; if the signal IS rejected, pass = 0
/datum/n_Compiler/vocal_implant/SetProcs(var/datum/signal/signal)
// Set up the script procs
/*
-> Send another signal to a speaker, same name as telecomms proc for ease of memory.
@format: broadcast(content)
@param content: Message to broadcast
*/
interpreter.SetProc("broadcast", "vibroadcast", signal, list("message"))
/*
-> Store a value permanently to the server machine (not the actual game hosting machine, the ingame machine)
@format: tcs_mem(address, value)
@param address: The memory address (string index) to store a value to
@param value: The value to store to the memory address
*/
interpreter.SetProc("mem", "vi_mem", signal, list("address", "value"))
..()
/* -- Execute the compiled code -- */
/datum/n_Compiler/vocal_implant/Run(var/datum/signal/signal)
..(signal)
// Backwards-apply variables onto signal data
signal.data["message"] = interpreter.GetVar("$content")
signal.data["reject"] = !(interpreter.GetCleanVar("$pass")) // set reject to the opposite of $pass
// If the message is invalid, just don't broadcast it!
if(signal.data["message"] == "" || !signal.data["message"])
signal.data["reject"] = 1
/datum/signal/proc/vi_mem(var/address, var/value)
if(istext(address))
var/obj/item/weapon/implant/vocal/V = data["implant"]
if(!value && value != 0)
return V.memory[address]
else
V.memory[address] = value
/datum/signal/proc/vibroadcast(var/message)
var/obj/item/weapon/implant/vocal/V = data["implant"]
var/atom/movable/speaker = V.imp_in || V.loc
if(!ismob(speaker) || !istype(speaker,/obj/item/weapon/implanter/vocal))
error("[src] is not implanted or in an implanter.")
return
if((!message || message == "") && message != 0)
message = "*beep*"
speaker.say(message)
message_admins("The [V] in [speaker] made \him say \"[message]\" [formatJumpTo(speaker)]")

View File

@@ -0,0 +1,117 @@
/client/verb/visave()
set hidden = 1
if(implant_check(mob))
var/obj/item/weapon/implanter/vocal/V = mob.get_active_hand()
if(V.imp)
var/obj/item/weapon/implant/vocal/VI = V.imp
var/vicode = winget(src, "vicode", "text")
VI.setcode( vicode ) // this actually saves the code from input to the implant
src << output(null, "vierror") // clear the errors
else
src << output(null, "vierror")
src << output("<font color = red>Failed to save: Unable to locate vocal implant. (Back up your code before exiting the window!)</font color>", "vierror")
else
src << output(null, "vierror")
src << output("<font color = red>Failed to save: Unable to locate implant. (Back up your code before exiting the window!)</font color>", "vierror")
/client/verb/vicompile()
set hidden = 1
if(implant_check(mob))
var/obj/item/weapon/implanter/vocal/V = mob.get_active_hand()
if(V.imp)
var/obj/item/weapon/implant/vocal/VI = V.imp
VI.setcode( winget(src, "vicode", "text") ) // save code first
spawn(0)
// Output all the compile-time errors
src << output(null, "vierror")
src << output("<font color = black>Please wait, compiling...</font>", "vierror")
var/list/compileerrors = VI.compile(mob) // then compile the code!
if(!implant_check(mob))
return
if(compileerrors.len)
src << output("<b>Compile Errors</b>", "vierror")
for(var/datum/scriptError/e in compileerrors)
src << output("<font color = red>\t>[e.message]</font color>", "vierror")
src << output("([compileerrors.len] errors)", "vierror")
else
src << output("<font color = blue>vi compilation successful!</font color>", "vierror")
src << output("(0 errors)", "vierror")
else
src << output(null, "vierror")
src << output("<font color = red>Failed to compile: Unable to locate vocal implant. (Back up your code before exiting the window!)</font color>", "vierror")
else
src << output(null, "vierror")
src << output("<font color = red>Failed to compile: Unable to locate implant. (Back up your code before exiting the window!)</font color>", "vierror")
/client/verb/virun()
set hidden = 1
if(implant_check(mob))
var/obj/item/weapon/implanter/vocal/V = mob.get_active_hand()
if(V.imp)
var/obj/item/weapon/implant/vocal/VI = V.imp
var/datum/signal/signal = new /datum/signal
signal.data["message"] = ""
signal.data["reject"] = 0
signal.data["implant"] = VI
VI.Compiler.Run(signal)
if(!signal.data["reject"])
V.say(signal.data["message"])
else
src << output(null, "vierror")
src << output("<font color = red>Failed to run: Unable to locate vocal implant. (Back up your code before exiting the window!)</font color>", "vierror")
else
src << output(null, "vierror")
src << output("<font color = red>Failed to run: Unable to locate implant. (Back up your code before exiting the window!)</font color>", "vierror")
/client/verb/virevert()
set hidden = 1
if(implant_check(mob))
var/obj/item/weapon/implanter/vocal/V = mob.get_active_hand()
if(V.imp)
var/obj/item/weapon/implant/vocal/VI = V.imp
// Replace quotation marks with quotation macros for proper winset() compatibility
var/showcode = replacetext(VI.rawcode, "\\\"", "\\\\\"")
showcode = replacetext(showcode, "\"", "\\\"")
winset(mob, "vicode", "text=\"[showcode]\"")
src << output(null, "vierror") // clear the errors
else
src << output(null, "vierror")
src << output("<font color = red>Failed to revert: Unable to locate vocal implant.</font color>", "vierror")
else
src << output(null, "vierror")
src << output("<font color = red>Failed to revert: Unable to locate implant.</font color>", "vierror")
/client/verb/viclearmem()
set hidden = 1
if(implant_check(mob))
var/obj/item/weapon/implanter/vocal/V = mob.get_active_hand()
if(V.imp)
var/obj/item/weapon/implant/vocal/VI = V.imp
VI.memory = list() // clear the memory
// Show results
src << output(null, "vierror")
src << output("<font color = blue>Implant memory cleared!</font color>", "vierror")
else
src << output(null, "vierror")
src << output("<font color = red>Failed to clear memory: Unable to locate vocal implant.</font color>", "vierror")
else
src << output(null, "vierror")
src << output("<font color = red>Failed to clear memory: Unable to locate implant.</font color>", "vierror")
/proc/implant_check(var/mob/mob)
if(mob && istype(mob.get_active_hand(),/obj/item/weapon/implanter/vocal))
return 1
return 0

View File

@@ -18,12 +18,13 @@
var/datum/scope/curScope
var/datum/scope/globalScope
var/datum/node/BlockDefinition/program
var/datum/node/statement/FunctionDefinition/curFunction
var/datum/node/BlockDefinition/program
var/datum/node/statement/FunctionDefinition/curFunction
var/datum/stack/scopes = new()
var/datum/stack/functions = new()
var/datum/container // associated container for interpeter
var/datum/n_Compiler/Compiler
/*
Var: status
A variable indicating that the rest of the current block should be skipped. This may be set to any combination of <Status Macros>.
@@ -57,7 +58,7 @@
Set ourselves to Garbage Collect
*/
/datum/n_Interpreter/proc/GC()
container = null
Compiler = null
/*
Proc: RaiseError
@@ -85,14 +86,7 @@ Proc: AlertAdmins
Alerts the admins of a script that is bad.
*/
/datum/n_Interpreter/proc/AlertAdmins()
if(container && !alertadmins)
if(istype(container, /datum/TCS_Compiler))
var/datum/TCS_Compiler/Compiler = container
var/obj/machinery/telecomms/server/Holder = Compiler.Holder
var/message = "Potential crash-inducing NTSL script detected at telecommunications server [Compiler.Holder] ([Holder.x], [Holder.y], [Holder.z])."
alertadmins = 1
message_admins(message, 1)
return
/*
Proc: RunBlock
Runs each statement in a block of code.

View File

@@ -0,0 +1,175 @@
/datum/n_Compiler
var/datum/n_Interpreter/interpreter
var/ready = 1 // 1 if ready to run code
var/interptype = /datum/n_Interpreter
/* -- Set ourselves to Garbage Collect -- */
/datum/n_Compiler/proc/GC()
if(interpreter)
interpreter.GC()
/* -- Compile a raw block of text -- */
/datum/n_Compiler/proc/Compile(code as message)
var/datum/n_scriptOptions/nS_Options/options = new()
var/datum/n_Scanner/nS_Scanner/scanner = new(code, options)
var/list/tokens = scanner.Scan()
var/datum/n_Parser/nS_Parser/parser = new(tokens, options)
var/datum/node/BlockDefinition/GlobalBlock/program = parser.Parse()
var/list/returnerrors = list()
returnerrors += scanner.errors
returnerrors += parser.errors
if(returnerrors.len)
return returnerrors
interpreter = new interptype(program)
interpreter.persist = 1
interpreter.Compiler= src
return returnerrors
/* -- Execute the compiled code -- */
/datum/n_Compiler/proc/Run(var/datum/signal/signal)
if(!ready)
return
if(!interpreter)
return
SetVars(signal)
SetProcs(signal)
// Run the compiled code
interpreter.Run()
/datum/n_Compiler/proc/SetProcs(var/datum/signal/signal)
// Set up the script procs
/*
-> Delay code for a given amount of deciseconds
@format: sleep(time)
@param time: time to sleep in deciseconds (1/10th second)
*/
interpreter.SetProc("sleep", /proc/delay)
/*
-> Replaces a string with another string
@format: replace(string, substring, replacestring)
@param string: the string to search for substrings (best used with $content$ constant)
@param substring: the substring to search for
@param replacestring: the string to replace the substring with
*/
interpreter.SetProc("replace", /proc/n_replacetext)
/*
-> Locates an element/substring inside of a list or string
@format: find(haystack, needle, start = 1, end = 0)
@param haystack: the container to search
@param needle: the element to search for
@param start: the position to start in
@param end: the position to end in
*/
interpreter.SetProc("find", /proc/smartfind)
/*
-> Finds the length of a string or list
@format: length(container)
@param container: the list or container to measure
*/
interpreter.SetProc("length", /proc/smartlength)
/* -- Clone functions, carried from default BYOND procs --- */
// vector namespace
interpreter.SetProc("vector", /proc/n_list)
interpreter.SetProc("at", /proc/n_listpos)
interpreter.SetProc("copy", /proc/n_listcopy)
interpreter.SetProc("push_back", /proc/n_listadd)
interpreter.SetProc("remove", /proc/n_listremove)
interpreter.SetProc("cut", /proc/n_listcut)
interpreter.SetProc("swap", /proc/n_listswap)
interpreter.SetProc("insert", /proc/n_listinsert)
interpreter.SetProc("pick", /proc/n_pick)
interpreter.SetProc("prob", /proc/prob_chance)
interpreter.SetProc("substr", /proc/docopytext)
interpreter.SetProc("shuffle", /proc/shuffle)
interpreter.SetProc("uniquevector", /proc/uniquelist)
interpreter.SetProc("text2vector", /proc/n_splittext)
interpreter.SetProc("text2vectorEx",/proc/splittextEx)
interpreter.SetProc("vector2text", /proc/vg_jointext)
// Strings
interpreter.SetProc("lower", /proc/n_lower)
interpreter.SetProc("upper", /proc/n_upper)
interpreter.SetProc("explode", /proc/string_explode)
interpreter.SetProc("repeat", /proc/n_repeat)
interpreter.SetProc("reverse", /proc/reverse_text)
interpreter.SetProc("tonum", /proc/n_str2num)
interpreter.SetProc("capitalize", /proc/capitalize)
//interpreter.SetProc("replacetextEx",/proc/n_replacetextEx)
// Numbers
interpreter.SetProc("tostring", /proc/n_num2str)
interpreter.SetProc("sqrt", /proc/n_sqrt)
interpreter.SetProc("abs", /proc/n_abs)
interpreter.SetProc("floor", /proc/Floor)
interpreter.SetProc("ceil", /proc/Ceiling)
interpreter.SetProc("round", /proc/n_round)
interpreter.SetProc("clamp", /proc/n_clamp)
interpreter.SetProc("inrange", /proc/IsInRange)
interpreter.SetProc("rand", /proc/rand_chance)
interpreter.SetProc("arctan", /proc/Atan2)
interpreter.SetProc("lcm", /proc/Lcm)
interpreter.SetProc("gcd", /proc/Gcd)
interpreter.SetProc("mean", /proc/Mean)
interpreter.SetProc("root", /proc/Root)
interpreter.SetProc("sin", /proc/n_sin)
interpreter.SetProc("cos", /proc/n_cos)
interpreter.SetProc("arcsin", /proc/n_asin)
interpreter.SetProc("arccos", /proc/n_acos)
interpreter.SetProc("tan", /proc/Tan)
interpreter.SetProc("csc", /proc/Csc)
interpreter.SetProc("cot", /proc/Cot)
interpreter.SetProc("sec", /proc/Sec)
interpreter.SetProc("todegrees", /proc/ToDegrees)
interpreter.SetProc("toradians", /proc/ToRadians)
interpreter.SetProc("lerp", /proc/mix)
interpreter.SetProc("max", /proc/n_max)
interpreter.SetProc("min", /proc/n_min)
// End of Donkie~
// Time
interpreter.SetProc("time", /proc/time)
interpreter.SetProc("timestamp", /proc/timestamp)
/datum/n_Compiler/proc/SetVars(var/datum/signal/signal)
interpreter.SetVar("TAU", TAU) // value of tau
interpreter.SetVar("PI", PI) // value of pi
interpreter.SetVar("E", E) // value of e
interpreter.SetVar("SQURT2", Sqrt2) // value of the square root of 2
interpreter.SetVar("FALSE", 0) // boolean shortcut to 0
interpreter.SetVar("false", 0) // boolean shortcut to 0
interpreter.SetVar("TRUE", 1) // boolean shortcut to 1
interpreter.SetVar("true", 1) // boolean shortcut to 1
interpreter.SetVar("NORTH", NORTH) // NORTH (1)
interpreter.SetVar("SOUTH", SOUTH) // SOUTH (2)
interpreter.SetVar("EAST", EAST) // EAST (4)
interpreter.SetVar("WEST", WEST) // WEST (8)

View File

@@ -778,6 +778,86 @@ window "Telecomms IDE"
multi-line = true
no-command = true
window "Vocal Implant IDE"
elem "Vocal Implant IDE"
type = MAIN
pos = 281,0
size = 569x582
anchor1 = none
anchor2 = none
background-color = #ffffff
is-visible = false
saved-params = "pos;size;is-minimized;is-maximized"
title = "Vocal Implant IDE"
statusbar = false
on-close = "exitvi"
elem "button5"
type = BUTTON
pos = 209,464
size = 70x20
anchor1 = 37,80
anchor2 = 49,83
saved-params = "is-checked"
text = "Clear Memory"
command = "viclearmem"
elem "button4"
type = BUTTON
pos = 157,464
size = 52x20
anchor1 = 28,80
anchor2 = 37,83
saved-params = "is-checked"
text = "Revert"
command = "virevert"
elem "button3"
type = BUTTON
pos = 105,464
size = 52x20
anchor1 = 18,80
anchor2 = 28,83
saved-params = "is-checked"
text = "Execute"
command = "virun"
elem "vierror"
type = OUTPUT
pos = 0,488
size = 566x94
anchor1 = 0,84
anchor2 = 99,100
font-family = "sans-serif"
font-size = 9
saved-params = "max-lines"
elem "button2"
type = BUTTON
pos = 53,464
size = 52x20
anchor1 = 9,80
anchor2 = 18,83
saved-params = "is-checked"
text = "Compile"
command = "vicompile"
elem "button1"
type = BUTTON
pos = 0,464
size = 53x20
anchor1 = 0,80
anchor2 = 9,83
saved-params = "is-checked"
text = "Save"
command = "visave"
elem "vicode"
type = INPUT
pos = 0,0
size = 569x464
anchor1 = 0,0
anchor2 = 100,80
font-family = "Courier"
font-size = 10
saved-params = ""
command = "cancel"
multi-line = true
no-command = true
window "mainwindow"
elem "mainwindow"
type = MAIN

View File

@@ -760,6 +760,86 @@ window "Telecomms IDE"
multi-line = true
no-command = true
window "Vocal Implant IDE"
elem "Vocal Implant IDE"
type = MAIN
pos = 281,0
size = 569x582
anchor1 = none
anchor2 = none
background-color = #ffffff
is-visible = false
saved-params = "pos;size;is-minimized;is-maximized"
title = "Vocal Implant IDE"
statusbar = false
on-close = "exitvi"
elem "button5"
type = BUTTON
pos = 209,464
size = 70x20
anchor1 = 37,80
anchor2 = 49,83
saved-params = "is-checked"
text = "Clear Memory"
command = "viclearmem"
elem "button4"
type = BUTTON
pos = 157,464
size = 52x20
anchor1 = 28,80
anchor2 = 37,83
saved-params = "is-checked"
text = "Revert"
command = "virevert"
elem "button3"
type = BUTTON
pos = 105,464
size = 52x20
anchor1 = 18,80
anchor2 = 28,83
saved-params = "is-checked"
text = "Execute"
command = "virun"
elem "vierror"
type = OUTPUT
pos = 0,488
size = 566x94
anchor1 = 0,84
anchor2 = 99,100
font-family = "sans-serif"
font-size = 9
saved-params = "max-lines"
elem "button2"
type = BUTTON
pos = 53,464
size = 52x20
anchor1 = 9,80
anchor2 = 18,83
saved-params = "is-checked"
text = "Compile"
command = "vicompile"
elem "button1"
type = BUTTON
pos = 0,464
size = 53x20
anchor1 = 0,80
anchor2 = 9,83
saved-params = "is-checked"
text = "Save"
command = "visave"
elem "vicode"
type = INPUT
pos = 0,0
size = 569x464
anchor1 = 0,0
anchor2 = 100,80
font-family = "Courier"
font-size = 10
saved-params = ""
command = "cancel"
multi-line = true
no-command = true
window "mainwindow"
elem "mainwindow"
type = MAIN

View File

@@ -1148,6 +1148,7 @@
#include "code\game\objects\items\weapons\implants\types\pax_implant.dm"
#include "code\game\objects\items\weapons\implants\types\tracking_implant.dm"
#include "code\game\objects\items\weapons\implants\types\uplink_implant.dm"
#include "code\game\objects\items\weapons\implants\types\vocal_implant.dm"
#include "code\game\objects\items\weapons\melee\energy.dm"
#include "code\game\objects\items\weapons\melee\misc.dm"
#include "code\game\objects\items\weapons\storage\backpack.dm"
@@ -2589,8 +2590,8 @@
#include "code\modules\research\xenoarchaeology\tools\tools_locater.dm"
#include "code\modules\research\xenoarchaeology\tools\tools_pickaxe.dm"
#include "code\modules\runescape\runescape_pvp.dm"
#include "code\modules\scripting\compiler.dm"
#include "code\modules\scripting\Errors.dm"
#include "code\modules\scripting\IDE.dm"
#include "code\modules\scripting\Options.dm"
#include "code\modules\scripting\stack.dm"
#include "code\modules\scripting\AST\AST Nodes.dm"
@@ -2599,7 +2600,10 @@
#include "code\modules\scripting\AST\Operators\Binary Operators.dm"
#include "code\modules\scripting\AST\Operators\Unary Operators.dm"
#include "code\modules\scripting\Implementations\_Logic.dm"
#include "code\modules\scripting\Implementations\Telecomms.dm"
#include "code\modules\scripting\Implementations\Telecomms\Compiler.dm"
#include "code\modules\scripting\Implementations\Telecomms\IDE.dm"
#include "code\modules\scripting\Implementations\vocalimplant\Compiler.dm"
#include "code\modules\scripting\Implementations\vocalimplant\IDE.dm"
#include "code\modules\scripting\Interpreter\Evaluation.dm"
#include "code\modules\scripting\Interpreter\Interaction.dm"
#include "code\modules\scripting\Interpreter\Interpreter.dm"