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 = ""