diff --git a/code/game/objects/radio/radio.dm b/code/game/objects/radio/radio.dm
index 9b9e1fb171..0ab794a27a 100644
--- a/code/game/objects/radio/radio.dm
+++ b/code/game/objects/radio/radio.dm
@@ -14,7 +14,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
last_transmission
frequency = 1459 //common chat
traitor_frequency = 0 //tune to frequency to unlock traitor supplies
- canhear_range = 3
+ canhear_range = 3 // the range which mobs can hear this radio from
obj/item/device/radio/patch_link = null
obj/item/device/uplink/radio/traitorradio = null
wires = WIRE_SIGNAL | WIRE_RECEIVE | WIRE_TRANSMIT
@@ -63,6 +63,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
checkpower()
/obj/item/device/radio/initialize()
+
if(freerange)
if(frequency < 1200 || frequency > 1600)
frequency = sanitize_frequency(frequency)
@@ -145,6 +146,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
//..()
if (usr.stat || !on)
return
+
if (!(issilicon(usr) || (usr.contents.Find(src) || ( in_range(src, usr) && istype(loc, /turf) ))))
usr << browse(null, "window=radio")
return
@@ -155,6 +157,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
if(A && target)
A.ai_actual_track(target)
return
+
else if (href_list["faketrack"])
var/mob/target = locate(href_list["track"])
var/mob/living/silicon/ai/A = locate(href_list["track2"])
@@ -169,7 +172,9 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
usr << "Target is not on or near any active cameras on the station. We'll check again in 5 seconds (unless you use the cancel-camera verb)."
sleep(40)
continue
+
return
+
else if (href_list["freq"])
var/new_frequency = (frequency + text2num(href_list["freq"]))
if (!freerange || (frequency < 1200 || frequency > 1600))
@@ -248,6 +253,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
return
/obj/item/device/radio/talk_into(mob/M as mob, message, channel)
+
if(!on) return // the device has to be on
if(GLOBAL_RADIO_TYPE == 1) // NEW RADIO SYSTEMS: By Doohl
@@ -424,7 +430,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
sleep(rand(10,25)) // wait a little...
if(signal.data["done"])
- //we're done here.
+ // we're done here.
return
// Oh my god; the comms are down or something because the signal hasn't been broadcasted yet.
@@ -615,6 +621,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use
R.show_message(rendered, 2)
/obj/item/device/radio/hear_talk(mob/M as mob, msg)
+
if (broadcasting)
talk_into(M, msg)
/*
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 131329e76f..2d4dcd96cb 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -250,6 +250,7 @@
for(var/obj/effect/speech_bubble/S in range(1, mob))
if(S.parent == mob)
S.loc = mob.loc
+
moving = 0
return .
@@ -364,6 +365,8 @@
if((istype(turf,/turf/simulated/floor)) && (src.lastarea.has_gravity == 0)) // No one else gets a chance.
continue
+
+
/*
if(istype(turf,/turf/simulated/floor) && (src.flags & NOGRAV))
continue
diff --git a/code/modules/scripting/Errors.dm b/code/modules/scripting/Errors.dm
index 361d762048..5b722d007c 100644
--- a/code/modules/scripting/Errors.dm
+++ b/code/modules/scripting/Errors.dm
@@ -52,6 +52,10 @@
message+="[id]'. "
if(T)message+="Found '[T.value]'."
+
+ UnterminatedComment
+ message="Unterminated multi-line comment statement: expected */"
+
DuplicateFunction
New(name, token/t)
message="Function '[name]' defined twice."
@@ -121,4 +125,8 @@
DivisionByZero
name="DivideByZeroError"
- message="Division by zero attempted."
\ No newline at end of file
+ message="Division by zero attempted."
+
+ MaxCPU
+ name="MaxComputationalUse"
+ message="Maximum amount of computational cycles reached (>= 1000)."
\ No newline at end of file
diff --git a/code/modules/scripting/IDE.dm b/code/modules/scripting/IDE.dm
index f45ad8d400..f935bf9257 100644
--- a/code/modules/scripting/IDE.dm
+++ b/code/modules/scripting/IDE.dm
@@ -1,7 +1,7 @@
client/verb/tcssave()
set hidden = 1
- if(mob.machine)
- if(istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob))
+ if(mob.machine || issilicon(mob))
+ if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || issilicon(mob))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob)
return
@@ -23,8 +23,8 @@ client/verb/tcssave()
client/verb/tcscompile()
set hidden = 1
- if(mob.machine)
- if(istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob))
+ if(mob.machine || issilicon(mob))
+ if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob)
return
@@ -74,8 +74,8 @@ client/verb/tcscompile()
client/verb/tcsrun()
set hidden = 1
- if(mob.machine)
- if(istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob))
+ if(mob.machine || issilicon(mob))
+ if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob)
return
@@ -140,8 +140,8 @@ client/verb/tcsrun()
client/verb/exittcs()
set hidden = 1
- if(mob.machine)
- if(istype(mob.machine, /obj/machinery/computer/telecomms/traffic))
+ if(mob.machine || issilicon(mob))
+ if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode == mob)
Machine.storedcode = "[winget(mob, "tcscode", "text")]"
@@ -152,8 +152,8 @@ client/verb/exittcs()
client/verb/tcsrevert()
set hidden = 1
- if(mob.machine)
- if(istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob))
+ if(mob.machine || issilicon(mob))
+ if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob)
return
diff --git a/code/modules/scripting/Implementations/Telecomms.dm b/code/modules/scripting/Implementations/Telecomms.dm
index 99cf87d8e2..04e6797481 100644
--- a/code/modules/scripting/Implementations/Telecomms.dm
+++ b/code/modules/scripting/Implementations/Telecomms.dm
@@ -47,6 +47,8 @@
if(!interpreter)
return
+ interpreter.container = src
+
interpreter.SetVar("PI" , 3.141592653) // value of pi
interpreter.SetVar("E" , 2.718281828) // value of e
interpreter.SetVar("SQURT2" , 1.414213562) // value of the square root of 2
@@ -155,6 +157,25 @@
interpreter.SetProc("prob", /proc/prob_chance)
interpreter.SetProc("substr", /proc/docopytext)
+ // Donkie~
+ // 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/n_reverse)
+ interpreter.SetProc("tonum", /proc/n_str2num)
+
+ // Numbers
+ interpreter.SetProc("tostring", /proc/n_num2str)
+ interpreter.SetProc("sqrt", /proc/n_sqrt)
+ interpreter.SetProc("abs", /proc/n_abs)
+ interpreter.SetProc("floor", /proc/n_floor)
+ interpreter.SetProc("ceil", /proc/n_ceil)
+ interpreter.SetProc("round", /proc/n_round)
+ interpreter.SetProc("clamp", /proc/n_clamp)
+ interpreter.SetProc("inrange", /proc/n_inrange)
+ // End of Donkie~
// Run the compiled code
@@ -163,7 +184,7 @@
// Backwards-apply variables onto signal data
/* sanitize EVERYTHING. fucking players can't be trusted with SHIT */
- signal.data["message"] = trim(copytext(sanitize(interpreter.GetVar("$content")), 1, MAX_MESSAGE_LEN))
+ signal.data["message"] = interpreter.GetVar("$content")
signal.frequency = interpreter.GetVar("$freq")
var/setname = ""
@@ -171,14 +192,18 @@
if(interpreter.GetVar("$source") in S.stored_names)
setname = interpreter.GetVar("$source")
else
- setname = "[trim(copytext(sanitize(interpreter.GetVar("$source")), 1, MAX_MESSAGE_LEN))]"
+ setname = "[interpreter.GetVar("$source")]"
if(signal.data["name"] != setname)
signal.data["realname"] = setname
signal.data["name"] = setname
- signal.data["job"] = trim(copytext(sanitize(interpreter.GetVar("$job")), 1, MAX_MESSAGE_LEN))
+ signal.data["job"] = interpreter.GetVar("$job")
signal.data["reject"] = !(interpreter.GetVar("$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 -- */
datum/signal
@@ -188,7 +213,7 @@ datum/signal
if(istext(address))
var/obj/machinery/telecomms/server/S = data["server"]
- if(!value)
+ if(!value && value != 0)
return S.memory[address]
else
@@ -202,7 +227,7 @@ datum/signal
var/obj/machinery/telecomms/server/S = data["server"]
var/obj/item/device/radio/hradio
- if(!message)
+ if((!message || message == "") && message != 0)
message = "*beep*"
if(!source)
source = "[html_encode(uppertext(S.id))]"
@@ -213,7 +238,7 @@ datum/signal
freq *= 10 // shift the decimal one place
if(!job)
- job = "None"
+ job = "?"
newsign.data["mob"] = H
newsign.data["mobtype"] = H.type
@@ -222,9 +247,9 @@ datum/signal
else
newsign.data["name"] = "[html_encode(uppertext(source))]"
newsign.data["realname"] = newsign.data["name"]
- newsign.data["job"] = html_encode(job)
+ newsign.data["job"] = job
newsign.data["compression"] = 0
- newsign.data["message"] = html_encode(message)
+ newsign.data["message"] = message
newsign.data["type"] = 2 // artificial broadcast
if(!isnum(freq))
freq = text2num(freq)
@@ -242,4 +267,4 @@ datum/signal
newsign.data["vmessage"] = H.voice_message
newsign.data["vname"] = H.voice_name
newsign.data["vmask"] = 0
- S.relay_information(newsign, "/obj/machinery/telecomms/broadcaster") // send this simple message to broadcasters
+ S.relay_information(newsign, "/obj/machinery/telecomms/broadcaster") // send this simple message to broadcasters
diff --git a/code/modules/scripting/Implementations/_Logic.dm b/code/modules/scripting/Implementations/_Logic.dm
index a76f4006e1..bb87dc9af3 100644
--- a/code/modules/scripting/Implementations/_Logic.dm
+++ b/code/modules/scripting/Implementations/_Logic.dm
@@ -53,6 +53,7 @@
if(isobject(e))
if(istype(e, /list))
chosenlist = e
+ i = 2
else
if(chosenlist)
chosenlist.Add(e)
@@ -66,6 +67,7 @@
if(isobject(e))
if(istype(e, /list))
chosenlist = e
+ i = 2
else
if(chosenlist)
chosenlist.Remove(e)
@@ -101,23 +103,142 @@
if(haystack && needle)
if(isobject(haystack))
if(istype(haystack, /list))
- if(length(haystack) + 1 >= end && start > 0)
+ if(length(haystack) >= end && start > 0)
var/list/listhaystack = haystack
return listhaystack.Find(needle, start, end)
else
if(istext(haystack))
- if(length(haystack) + 1 >= end && start > 0)
+ if(length(haystack) >= end && start > 0)
return findtext(haystack, needle, start, end)
// Clone of copytext()
/proc/docopytext(var/string, var/start = 1, var/end = 0)
if(istext(string) && isnum(start) && isnum(end))
- if(length(string) >= end && start > 0)
+ if(start > 0)
return copytext(string, start, end)
// Clone of length()
/proc/smartlength(var/container)
if(container)
if(istype(container, /list) || istext(container))
- return length(container)
\ No newline at end of file
+ return length(container)
+
+// BY DONKIE~
+// String stuff
+/proc/n_lower(var/string)
+ if(istext(string))
+ return lowertext(string)
+
+/proc/n_upper(var/string)
+ if(istext(string))
+ return uppertext(string)
+
+/*
+//Makes a list where all indicies in a string is a seperate index in the list
+// JUST A HELPER DON'T ADD TO NTSCRIPT
+proc/string_tolist(var/string)
+ var/list/L = new/list()
+
+ var/i
+ for(i=1, i<=lentext(string), i++)
+ L.Add(copytext(string, i, i))
+
+ return L
+
+proc/string_explode(var/string, var/separator)
+ if(istext(string))
+ if(istext(separator) && separator == "")
+ return string_tolist(string)
+ var/i
+ var/lasti = 1
+ var/list/L = new/list()
+
+ for(i=1, i<=lentext(string)+1, i++)
+ if(copytext(string, i, i+1) == separator) // We found a separator
+ L.Add(copytext(string, lasti, i))
+ lasti = i+1
+
+ L.Add(copytext(string, lasti, lentext(string)+1)) // Adds the last segment
+
+ return L
+
+Just found out there was already a string explode function, did some benchmarking, and that function were a bit faster, sticking to that.
+*/
+proc/string_explode(var/string, var/separator)
+ if(istext(string) && istext(separator))
+ return dd_text2list(string, separator)
+
+proc/n_repeat(var/string, var/amount)
+ if(istext(string) && isnum(amount))
+ var/i
+ var/newstring = ""
+ for(i=0, i<=amount, i++)
+ if(i>=1000)
+ break
+ newstring = newstring + string
+
+ return newstring
+
+proc/n_reverse(var/string)
+ if(istext(string))
+ var/newstring = ""
+ var/i
+ for(i=lentext(string), i>0, i--)
+ if(i>=1000)
+ break
+ newstring = newstring + copytext(string, i, i+1)
+
+ return newstring
+
+// I don't know if it's neccesary to make my own proc, but I think I have to to be able to check for istext.
+proc/n_str2num(var/string)
+ if(istext(string))
+ return text2num(string)
+
+// Number shit
+proc/n_num2str(var/num)
+ if(isnum(num))
+ return num2text(num)
+
+// Squareroot
+proc/n_sqrt(var/num)
+ if(isnum(num))
+ return sqrt(num)
+
+// Magnitude of num
+proc/n_abs(var/num)
+ if(isnum(num))
+ return abs(num)
+
+// Round down
+proc/n_floor(var/num)
+ if(isnum(num))
+ return round(num)
+
+// Round up
+proc/n_ceil(var/num)
+ if(isnum(num))
+ return round(num)+1
+
+// Round to nearest integer
+proc/n_round(var/num)
+ if(isnum(num))
+ if(num-round(num)<0.5)
+ return round(num)
+ return n_ceil(num)
+
+// Clamps N between min and max
+proc/n_clamp(var/num, var/min=-1, var/max=1)
+ if(isnum(num)&&isnum(min)&&isnum(max))
+ if(num<=min)
+ return min
+ if(num>=max)
+ return max
+ return num
+
+// Returns 1 if N is inbetween Min and Max
+proc/n_inrange(var/num, var/min=-1, var/max=1)
+ if(isnum(num)&&isnum(min)&&isnum(max))
+ return ((min <= num) && (num <= max))
+// END OF BY DONKIE :(
diff --git a/code/modules/scripting/Interpreter/Interaction.dm b/code/modules/scripting/Interpreter/Interaction.dm
index 0dda1656d6..f0b7c988d2 100644
--- a/code/modules/scripting/Interpreter/Interaction.dm
+++ b/code/modules/scripting/Interpreter/Interaction.dm
@@ -28,6 +28,8 @@
*/
Run()
cur_recursion = 0 // reset recursion
+ cur_statements = 0 // reset CPU tracking
+ alertadmins = 0
ASSERT(src.program)
RunBlock(src.program)
diff --git a/code/modules/scripting/Interpreter/Interpreter.dm b/code/modules/scripting/Interpreter/Interpreter.dm
index ec13bd950d..70849ffa15 100644
--- a/code/modules/scripting/Interpreter/Interpreter.dm
+++ b/code/modules/scripting/Interpreter/Interpreter.dm
@@ -24,6 +24,8 @@
stack
scopes = new()
functions = new()
+
+ datum/container // associated container for interpeter
/*
Var: status
A variable indicating that the rest of the current block should be skipped. This may be set to any combination of .
@@ -31,10 +33,12 @@
status=0
returnVal
- max_iterations=100 // max iterations without any kind of delay
- cur_iterations=0 // current iteration
- max_recursion=50 // max recursions without returning anything (or completing the code block)
- cur_recursion=0 // current amount of recursion
+ max_statements=1000 // maximum amount of statements that can be called in one execution. this is to prevent massive crashes and exploitation
+ cur_statements=0 // current amount of statements called
+ alertadmins=0 // set to 1 if the admins shouldn't be notified of anymore issues
+ max_iterations=100 // max number of uninterrupted loops possible
+ max_recursion=50 // max recursions without returning anything (or completing the code block)
+ cur_recursion=0 // current amount of recursion
/*
Var: persist
If 0, global variables will be reset after Run() finishes.
@@ -88,56 +92,74 @@
CreateGlobalScope()
curScope = globalScope
- for(var/node/statement/S in Block.statements)
- while(paused) sleep(10)
- if(istype(S, /node/statement/VariableAssignment))
- var/node/statement/VariableAssignment/stmt = S
- var/name = stmt.var_name.id_name
- if(!stmt.object)
- // Below we assign the variable first to null if it doesn't already exist.
- // This is necessary for assignments like +=, and when the variable is used in a function
- // If the variable already exists in a different block, then AssignVariable will automatically use that one.
- if(!IsVariableAccessible(name))
- AssignVariable(name, null)
- AssignVariable(name, Eval(stmt.value))
+ if(cur_statements < max_statements)
+
+ for(var/node/statement/S in Block.statements)
+ while(paused) sleep(10)
+
+ cur_statements++
+ if(cur_statements >= max_statements)
+ RaiseError(new/runtimeError/MaxCPU())
+
+ 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)
+ break
+
+ if(istype(S, /node/statement/VariableAssignment))
+ var/node/statement/VariableAssignment/stmt = S
+ var/name = stmt.var_name.id_name
+ if(!stmt.object)
+ // Below we assign the variable first to null if it doesn't already exist.
+ // This is necessary for assignments like +=, and when the variable is used in a function
+ // If the variable already exists in a different block, then AssignVariable will automatically use that one.
+ if(!IsVariableAccessible(name))
+ AssignVariable(name, null)
+ AssignVariable(name, Eval(stmt.value))
+ else
+ var/datum/D = Eval(GetVariable(stmt.object.id_name))
+ if(!D) return
+ D.vars[stmt.var_name.id_name] = Eval(stmt.value)
+ else if(istype(S, /node/statement/VariableDeclaration))
+ //VariableDeclaration nodes are used to forcibly declare a local variable so that one in a higher scope isn't used by default.
+ var/node/statement/VariableDeclaration/dec=S
+ if(!dec.object)
+ AssignVariable(dec.var_name.id_name, null, curScope)
+ else
+ var/datum/D = Eval(GetVariable(dec.object.id_name))
+ if(!D) return
+ D.vars[dec.var_name.id_name] = null
+ else if(istype(S, /node/statement/FunctionCall))
+ RunFunction(S)
+ else if(istype(S, /node/statement/FunctionDefinition))
+ //do nothing
+ else if(istype(S, /node/statement/WhileLoop))
+ RunWhile(S)
+ else if(istype(S, /node/statement/IfStatement))
+ RunIf(S)
+ else if(istype(S, /node/statement/ReturnStatement))
+ if(!curFunction)
+ RaiseError(new/runtimeError/UnexpectedReturn())
+ continue
+ status |= RETURNING
+ returnVal=Eval(S:value)
+ break
+ else if(istype(S, /node/statement/BreakStatement))
+ status |= BREAKING
+ break
+ else if(istype(S, /node/statement/ContinueStatement))
+ status |= CONTINUING
+ break
else
- var/datum/D = Eval(GetVariable(stmt.object.id_name))
- if(!D) return
- D.vars[stmt.var_name.id_name] = Eval(stmt.value)
- else if(istype(S, /node/statement/VariableDeclaration))
- //VariableDeclaration nodes are used to forcibly declare a local variable so that one in a higher scope isn't used by default.
- var/node/statement/VariableDeclaration/dec=S
- if(!dec.object)
- AssignVariable(dec.var_name.id_name, null, curScope)
- else
- var/datum/D = Eval(GetVariable(dec.object.id_name))
- if(!D) return
- D.vars[dec.var_name.id_name] = null
- else if(istype(S, /node/statement/FunctionCall))
- RunFunction(S)
- else if(istype(S, /node/statement/FunctionDefinition))
- //do nothing
- else if(istype(S, /node/statement/WhileLoop))
- RunWhile(S)
- else if(istype(S, /node/statement/IfStatement))
- RunIf(S)
- else if(istype(S, /node/statement/ReturnStatement))
- if(!curFunction)
- RaiseError(new/runtimeError/UnexpectedReturn())
- continue
- status |= RETURNING
- returnVal=Eval(S:value)
- break
- else if(istype(S, /node/statement/BreakStatement))
- status |= BREAKING
- break
- else if(istype(S, /node/statement/ContinueStatement))
- status |= CONTINUING
- break
- else
- RaiseError(new/runtimeError/UnknownInstruction())
- if(status)
- break
+ RaiseError(new/runtimeError/UnknownInstruction())
+ if(status)
+ break
+
curScope = scopes.Pop()
/*
@@ -212,14 +234,9 @@
*/
RunWhile(node/statement/WhileLoop/stmt)
var/i=1
- if(!cur_iterations)
- cur_iterations = 1
while(Eval(stmt.cond) && Iterate(stmt.block, i++))
- cur_iterations++
continue
status &= ~BREAKING
- cur_iterations -= i
- if(cur_iterations <= 0) cur_iterations = 0
/*
Proc:Iterate
@@ -227,7 +244,7 @@
*/
Iterate(node/BlockDefinition/block, count)
RunBlock(block)
- if(max_iterations > 0 && (count >= max_iterations || cur_iterations + 1 >= max_iterations))
+ if(max_iterations > 0 && count >= max_iterations)
RaiseError(new/runtimeError/IterationLimitReached())
return 0
if(status & (BREAKING|RETURNING))
@@ -294,3 +311,4 @@
else if(!istype(value) && isobject(value)) value = new/node/expression/value/reference(value)
//TODO: check for invalid name
S.variables["[name]"] = value
+
diff --git a/code/modules/scripting/Parser/Expressions.dm b/code/modules/scripting/Parser/Expressions.dm
index 3245a4a28f..f27a30d3ad 100644
--- a/code/modules/scripting/Parser/Expressions.dm
+++ b/code/modules/scripting/Parser/Expressions.dm
@@ -276,7 +276,13 @@
exp.func_name=curToken.value
NextToken() //skip function name
NextToken() //skip open parenthesis, already found
+ var/loops = 0
+
for()
+ loops++
+ if(loops>=1000)
+ CRASH("Something TERRIBLE has gone wrong in ParseFunctionExpression ;__;")
+
if(istype(curToken, /token/symbol) && curToken.value==")")
return exp
exp.parameters+=ParseParamExpression()
diff --git a/code/modules/scripting/Parser/Parser.dm b/code/modules/scripting/Parser/Parser.dm
index 9f88fd2227..ad961c755f 100644
--- a/code/modules/scripting/Parser/Parser.dm
+++ b/code/modules/scripting/Parser/Parser.dm
@@ -171,7 +171,12 @@
NextToken() //skip function name
if(!CheckToken("(", /token/symbol)) //Check for and skip open parenthesis
return
+ var/loops = 0
for()
+ loops++
+ if(loops>=6000)
+ CRASH("Something TERRIBLE has gone wrong in ParseFunctionStatement ;__;")
+
if(!curToken)
errors+=new/scriptError/EndOfFile()
return
diff --git a/code/modules/scripting/Scanner/Scanner.dm b/code/modules/scripting/Scanner/Scanner.dm
index 173b50b453..35f21b39fd 100644
--- a/code/modules/scripting/Scanner/Scanner.dm
+++ b/code/modules/scripting/Scanner/Scanner.dm
@@ -59,6 +59,10 @@
line = 1
linepos = 0 //column=codepos-linepos
n_scriptOptions/nS_Options/options
+
+ commenting = 0
+ // 1: single-line
+ // 2: multi-line
list
/*
Variable: ignore
@@ -112,12 +116,16 @@
Scan() //Creates a list of tokens from source code
var/list/tokens=new
for(, src.codepos<=lentext(code), src.codepos++)
+
var/char=copytext(code, codepos, codepos+1)
if(char=="\n")
line++
linepos=codepos
+
if(ignore.Find(char))
continue
+ else if(char == "/")
+ ReadComment()
else if(end_stmt.Find(char))
tokens+=new /token/end(char, line, COL)
else if(string_delim.Find(char))
@@ -129,6 +137,8 @@
tokens+=ReadNumber()
else if(options.symbols.Find(char))
tokens+=ReadSymbol()
+
+
codepos=initial(codepos)
line=initial(line)
linepos=initial(linepos)
@@ -228,4 +238,50 @@
errors+=new/scriptError("Bad number: ", T)
T.value=0
codepos-- //allow main Scan() proc to read the next character
- return T
\ No newline at end of file
+ return T
+
+/*
+ Proc: ReadComment
+ Reads a comment and outputs the type of comment
+*/
+
+ ReadComment()
+ var
+ char=copytext(code, codepos, codepos+1)
+ nextchar=copytext(code, codepos+1, codepos+2)
+ charstring = char+nextchar
+ comm = 1
+ // 1: single-line comment
+ // 2: multi-line comment
+ expectedend = 0
+
+ if(charstring == "//" || charstring == "/*")
+ if(charstring == "/*")
+ comm = 2 // starts a multi-line comment
+
+ while(comm)
+ if(++codepos>lentext(code)) break
+
+ if(expectedend) // ending statement expected...
+ char = copytext(code, codepos, codepos+1)
+ if(char == "/") // ending statement found - beak the comment
+ comm = 0
+ break
+
+ if(comm == 2)
+ // multi-line comments are broken by ending statements
+ char = copytext(code, codepos, codepos+1)
+ if(char == "*")
+ expectedend = 1
+ continue
+ else
+ char = copytext(code, codepos, codepos+1)
+ if(char == "\n")
+ comm = 0
+ break
+
+ if(expectedend) expectedend = 0
+
+ if(comm == 2)
+ errors+=new/scriptError/UnterminatedComment()
+
diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi
index 857fe21d01..43d039f98f 100644
Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ
diff --git a/icons/obj/stationobjs.dmi b/icons/obj/stationobjs.dmi
index 3ad2e5afbc..9a133457ad 100755
Binary files a/icons/obj/stationobjs.dmi and b/icons/obj/stationobjs.dmi differ
diff --git a/interface/skin.dmf b/interface/skin.dmf
index fcdde69d00..4cc3f89132 100644
--- a/interface/skin.dmf
+++ b/interface/skin.dmf
@@ -331,7 +331,7 @@ menu "menu"
window "Telecomms IDE"
elem "Telecomms IDE"
type = MAIN
- pos = 281,0
+ pos = 303,13
size = 652x582
anchor1 = none
anchor2 = none
@@ -372,8 +372,8 @@ window "Telecomms IDE"
type = BUTTON
pos = 180,464
size = 60x20
- anchor1 = none
- anchor2 = none
+ anchor1 = 28,80
+ anchor2 = 37,83
font-family = ""
font-size = 0
font-style = ""
@@ -400,8 +400,8 @@ window "Telecomms IDE"
type = BUTTON
pos = 120,464
size = 60x20
- anchor1 = none
- anchor2 = none
+ anchor1 = 18,80
+ anchor2 = 28,83
font-family = ""
font-size = 0
font-style = ""
@@ -428,8 +428,8 @@ window "Telecomms IDE"
type = OUTPUT
pos = 0,488
size = 648x94
- anchor1 = none
- anchor2 = none
+ anchor1 = 0,84
+ anchor2 = 99,100
font-family = "sans-serif"
font-size = 9
font-style = ""
@@ -454,8 +454,8 @@ window "Telecomms IDE"
type = BUTTON
pos = 60,464
size = 60x20
- anchor1 = none
- anchor2 = none
+ anchor1 = 9,80
+ anchor2 = 18,83
font-family = ""
font-size = 0
font-style = ""
@@ -482,8 +482,8 @@ window "Telecomms IDE"
type = BUTTON
pos = 0,464
size = 60x20
- anchor1 = none
- anchor2 = none
+ anchor1 = 0,80
+ anchor2 = 9,83
font-family = ""
font-size = 0
font-style = ""
@@ -510,8 +510,8 @@ window "Telecomms IDE"
type = INPUT
pos = 0,0
size = 652x464
- anchor1 = none
- anchor2 = none
+ anchor1 = 0,0
+ anchor2 = 100,80
font-family = "Courier"
font-size = 10
font-style = ""