Jump to content

Recommended Posts

Posted

 

Ok, I'm going to start this guide with the reason I'm writing it. I've been teaching myself some of the less well known aspects of ss13. To teach myself Tcomms scripting I set myself a fun challenge - write a script that each time someone spoke changed the name of the person speaking, and changed the message to a random panic message or a urgent call for help.

 

So, I wrote the script, learning pretty much everything about tcomms as I went, and eventually got it working, and working well. So today was the first time I was an antag since I wrote it. I ahelped for permission to upload it, was given it, ordered an emag, and headed out to the tcomms satellite, uploaded and executed it. First few messages it didn't change as it had to collect names. Then the chaos started. This was about 20 minutes into the round.

 

Within 5 minutes the shuttle was called :cry: Someone got onto a station bounced radio and pointed out that someone had hacked tcomms - Ah, I thought, now they'll go and fix it and recall the shuttle... nope. Then I realised.... no-one actually knew how to fix it. The script was still running when the shuttle docked at centcom.

 

So, I'm going to start off by explaining what to do if some git does what I did and uploads a malicious script. Go to the telecoms control room. All scripts run on the telecommunications traffic control computer, so that's the one you want. When you click on it, it shows the network it's connected to and a list of servers on that network. The only network (afaik) is tcommsat so make sure this is in the network part, then scan for servers. You'll get a list of all the servers(channels) available. Click on one will give you the options of editing the code and toggling between having the code run either always or never. setting it to never will disable any script running on it... beneficial or malicious. Job done. To make sure it doesn't get started again, click the edit code button and delete any code in there.

 

To upload a script, same thing but click the edit code instead. This will give you a window in which you can write/paste your code. Once it's in there, click the apply and compile buttons. When you hit compile it will go through and check that you have no errors in the code. Assuming you don't, you then hit execute. please note, the script is not running at this point! It is now ready to run. You need to change the signal execution to always before it will start.

 

Ok, so now you know how to upload and remove code from tcomms. So, how do you write the script? Ok... you NEED some experience with coding to go any further. I'll explain a fair amount in this guide, but I'm not about to teach you programming from scratch. If you want to learn and don't have any coding experience I'd suggest finding a good JavaScript guide online and starting with that as there's quite a few similarities between it and the NTSL used in ss13.

 

Next step, read http://80.244.78.90/wiki/index.php/NT_Script . It's really not very good (I'll update it when I get the chance) but it'll give an overview of the language. Then come back here.

 

Ok, so you've got some experience and have read through the wiki? Great, now we can get started. As I go through, I'll use examples from the script I wrote. Also, anything that's in *here* is stuff for you to put in, not to copy.

 

There are 3 types of variables in NTSL. Strings, Numbers and Vectors. Obviously strings hold text and numbers hold numbers. Vectors hold lists of either strings or numbers. You define them as follows

 

$index = 1;

$name = “”; // gives an empty string

$name = “Kiera Whiteman”;

$list = vector(); //gives an empty vector

$numbers = vector(1,2,3,4,5); // gives a vector of numbers

accusations = vector(" is a changeling! They're draining the fluids out of ",

" has an esword and just killed ",

" is dressed as a wizard and just threw a fireball at ",

" has an arcane tome and just dragged off ",

" just turned into a blob! It killed ",

" is wearing a red spacesuit! So is "); // gives a vector of strings

 

And yes, that last one is from my script ;)

 

Ok, so we know what variables are available. Before we go onto what we can do with them, there's a few rules, and a few set variables you need to know about.

 

Set variables -

PI = 3.141592653;

E = 2.718281828;

SQURT2 = 1.414213562;

FALSE = 0;

TRUE = 1;

NORTH = 1;

SOUTH = 2;

EAST = 4;

WEST = 8;

 

$common = 1459

$science = 1351

$command = 1353

$medical = 1355

$engineering = 1357

$security = 1359

$supply = 1347

 

 

These are always fixed.

 

$source // the person who just sent the message over the channel

$content // the content of the message

$freq // the frequency of the message, so which channel it was broadcast over

$pass // determines if the message will be broadcasted, 0 for no and 1 for yes

$job // the job of the source

 

Obviously these change with each message. They can be changed within a script. Note that $source can be changed to anything, but if it's not a valid name of a current crewmember it will be shown in italics and capitals, which is a bit of a give away. Also, if you change the frequency, the message will NOT then be ran through any scripts on that channel's server.

 

Ok, the rules for variables.

If a variable is defined within a block of code ( set of { } )it is only available within that block (and any blocks in that block). So for example -

 

def example()

{

$index = 1; // defining index within the example function but outside of the while block

while($index <= 10){

$count += 1; // defines and uses count within the while block

$index += 1; // this is fine as the while block is within the example block

}

$index += 1; // index is now 11

$count +=1; // count is NOT 11, it is 1. The count in the while block died at the end of the block, so this is a new variable

}

// trying to use index here would have the same result as using count outside the while did

 

The other major rule is that string and number variables are not directly interchangable. For example

 

$first = 1;

$second = “2”;

$third = $first * $second; // This will not be 3, it would still be empty as you can't multiply a string

$fourth = $first + $second; // again, this will not be 3, it will be a string containing “12”

 

There are 2 functions that are built in to help with this, tostring() and tonum() which I'll cover in a minute.

Vectors can hold combinations of strings and numbers.

The mem function (I'll come to it later) isn't very robust with respect to keeping strings as strings and numbers as numbers, so I'd advise using the tostring or tonum functions on anything you retrieve using it.

 

Ok, so what can you do with the variables? Each type have built in functions. The vector ones are covered in the wiki, so I'll leave those for you to look at.

 

STRINGS

 

+ adds to strings together

 

$test1 = “Kiera Whiteman “

$test2 = “is hacking into telecoms!”

$test3 = $test1 + $test2 // this is “Kiera Whiteman is hacking into telecoms!”

 

length – takes a string as it's argument and returns the number of characters in the string as a number.

 

$test = “test”;

$howlong = length($test); // this is now 4

 

substr – takes a string and 2 numbers as its arguments, returns a smaller string, starting at the position of the second argument and ending the character BEFORE the position of the third argument

 

$test = “testing this”;

$part = substr($test, 3, length($test) + 1); // this will now be “sting this”, however, without the +1 it would be “sting thi”

 

find – takes 2 strings as it's argument and returns a number, either the position of the second string in the first string or 0 if is wasn't found

 

$test = "testing this";

$position = find($test, "this"); // this is now 9

 

replace – this seems to be bugged. It should take 3 string arguments, replacing all instances of the second string in the first string with the third string

 

$test = “testing this”;

$changed = ($test, “t”, “s”); // this should now be “sessing shis”, however, when I tried it it came out as “ssshis”

 

lower and upper – these take a string and return the same contents but in lower case or upper case respectively

 

$test = "testing this";

$big = upper($test); // this now contains “TESTING THIS”

$small = lower($big); // this now contains “testing this”

 

explode – takes 2 strings. Returns a vector with the first string split at each point it found the second string

 

$test = “We need a longer sentence for this one”;

$seperated = explode($test, “ “); // this now contains a vector with 8 entries, first is “we”, second is “need” and so on

 

repeat – takes a string and a number and returns a string with the string repeated n number of times where n is the number. DO NOT use this to spam!

 

$test = "testing this";

$repeated = repeat ($test,3); // this now contains “testing thistesting thistesting thistesting this”

 

 

reverse – takes a string, returns a string. Can you guess what is does?

 

$test = "testing this";

$twisted = reverse($test); // this now contains “siht gnitset”

 

tonum – takes a string, returns it as a number if possible. It must be in numerical symbols

 

$first = "5"; // string

$second = "10"; // string

$third = tonum($first) * tonum($second); this is now 50

 

really don't try this with anything that might not be a number, it'll mess up big time

 

 

NUMBERS

 

ok, to start you've got 3 obvious... +, - and *. Less obvious is divide, which is ^

 

sqrt – takes a number, returns the square root of the number

 

$test = sqrt(9) // this is 3

 

floor, ceil and round – each take a number and return a number. floor rounds it down, ceil rounds it up, and round rounds it to the nearest integer

$test = 3.14159;

$down = floor($test); // this is 3

$up = ceil($test); // this is 4

$near = round($test); // this is 3

 

clamp – takes three numbers. The second is the lower limit, the third is the upper limit. If the first is within the limits, returns it unchanged, otherwise changes it to the limit it crossed.

 

$test1 = 50;

$test2 = 10;

$test3 = 28;

$result1 = clamp($test1, 20, 40); // this is 40

$result2 = clamp($test2, 20, 40); // this is 20

$result3 = clamp($test3, 20, 40); // this is 28

 

inrange – similar to clamp, but returns 1 if the value is within the limits and 0 if it's not

 

$test1 = 10;

$test2 = 28;

$result1 = inrange($test1, 20, 40) // this is 0

$result2 = inrange($test2, 20, 40) // this is 1

 

tostring – this takes a number and returns it as a string

 

$test = 50;

$string = tostring($test); // this now contains “50”

 

OTHER FUNCTIONS

 

these don't relate directly to a type of variable

 

 

mem – Usually variables are discarded once the script has finished running. mem lets you store things in server memory and take them out. You can put any type of variable into memory, although if you're storing numbers, use tonum when you get them out as it can get confused. It takes 2 arguments if you're storing something, a string as a name to store it under and the variable to be stored, or 1 if you're retrieving a variable, a string for the name it was stored under

 

$randoms = vector("Oh fuck, the sing is loose!", "Oh god, I'm vomiting spiders!", "oh shit, there's a ninja onboard", "Help! The AI's trying to kill me!", "Help, xeno Empress!", "Blob! Grab the welders");

mem("randoms", $randoms); // puts the vector of strings into memory as “randoms”

 

$randoms = mem(“randoms”); //retrieves the vector – this doesn't need to be the same time it was put in

 

And yes, you can guess where that came from :)

 

broadcast – takes up to 4 arguments – message, frequency, source, job and transmits a the message over coms. If you don't pass it all the values it will use the defaults - frequency: 1459, source: the server name, job: None

 

broadcast (“I'm innocent!”, $common, “Kiera Whiteman”);

 

Kiera Whiteman [145.9] says, "I'm innocent!" will be transmitted over the radio

 

broadcast(“I'm innocent!”);

 

COMMON SERVER [145.9] says, "I'm innocent!" will be transmitted over the radio

 

If $pass is 1 at the end of the script a broadcast will be transmitted with

 

broadcast($content, $freq, $source, $job)

 

If pass is 0, it won't be sent.

 

signal – takes 2 numbers as arguments, frequency and code. Works just like a remote signaller

 

if($message = “drop dead captain”)

{

signal(145.7, 30); // you didn't put a remote controlled grenade in the captain's backpack did you???

}

 

I think the only other bit I need to cover is user defined functions. You can define your own functions for things that you need to do more than once, or are likely to use in more than one script.

To do this you use def. You then put the function name, followed by any arguments within brackets, and put the function code in curly brackets the same as in an if or while block. If you need the function to send back a value, you do return followed by the variable.

 

I'm getting tired now, so I hope that made sense. As an example, here's a handy one I made for my script. It takes a vector and a string and searches the vector for the string, returning the position in the vector if it's found, and 0 if it's not

 

def searchVec($vector, $string)

{

$index = 1;

$found = 0;

$test = "";

while ($index <= length($vector) && $found == 0) // continues until it's found or reaches the end of the vector

{

$test = at($vector, $index);

if ($test == $string){$found = $index;}

$index += 1;

}

return $found;

}

 

As an example how to use it, here's the one that collected the names of people talking over the radio

 

def AddName()

{

$names = vector();

$names = mem("names");

 

if (searchVec($names, $source) == 0) // if the name isn't already in the vector

{

push_back($names, $source);

}

mem("names", $names);

}

 

Ok, that's it for now. I will add useful functions and scripts occasionally as I make them... but only positive ones, if you want ones for your antags, use what I've shown you and make them yourself – if you made it this far you've already got some clues about my script ;)

 

Link to comment
https://www.paradisestation.org/forum/topic/1733-guide-to-tcomm-scripting/
Share on other sites

Posted

 

At some point I'll probably take you up on that Fj. At the moment I'm focussed on medbay stuff, but I'll let you know if/when I do want to learn atmos

 

Back on thread, here's a script for all you AI's. Don't you just hate that you can't access the different channels in the same way as people with earpieces? Well, here's the solution. I couldn't use the : as the game automatically strips the first 2 characters from a message if it's the ai and it starts with :, so we're using @ instead. So it's @c for command, @m for medical, ect. It's fully annotated so you can see what's going on and make changes if you want

 

def searchVec($vector, $string)

{

$index = 1;

$found = 0;

$test = "";

while ($index <= length($vector) && $found == 0)

{

$test = at($vector, $index);

if ($test == $string){$found = $index;}

$index += 1;

}

return $found;

}

 

def initialise () // so the vector doesn't have to be set up each time

{

$codes = vector("@e", "1357", "@n", "1351", "@m", "1355", "@s", "1359", "@c", "1353", "@u", "1347"); // set up a vector with each code to access the channel followed by the channel frequency

$initialised = 1;

mem("codes", $codes); // stores the vector in memory

mem("initialised", $initialised); // this is so we can check easily whether this has already been run

}

 

if (tonum(mem("initialised")) != 1) {initialise();} // If the codes aren't already in memory, run initialise

if ($job == "AI") // Makes sure only the AI can use this

{

if (substr($content, 1, 2) == "@") // only runs if the first character is @

{

$channel = substr($content, 1, 3); // gets the first 2 characters of the message

$codes = mem("codes"); // gets the codes vector from memory

$find = searchVec($codes, $channel); // searches for $channel in the codes vector

if ($find != 0) // if the code has been found

{

$freq = tonum(at($codes, $find + 1)); // change the frequency of the message to the correct one

$content = substr($content, 4, length($content) + 1); // strips the first 3 characters (the code and space) from the message

}

}

}

 

Posted

 

Yeah, as I said, I'm not about to try and teach people to code from scratch. As long as you read the first part of the guide on what to do if someone uploads a malicious script you're good. I will start updating the wiki next week, and will try and cover some of the more basic stuff in that.

 

The AI one can just be copied and pasted into the common server edit code window, so you don't need to understand it to use it.

 

If anyone has an idea for a script but can't code it themselves let me know and I'll see what I can do

 

  • 1 month later...
Posted

 

Regarding that AI script, I wrote an alternative one which is shorter and does the same thing (but if you mistakenly put the character combination ina place other than the start of the message it still sends it to the channel, which keeps the message classified). Here it is:

 

 

// script by Citinited.

 

def getfreq(n) // Let's define a function to speak on a specific channel.

{

$freq = n; // n here corresponds to n in getfreq() and allows us to set a frequency when we define n.

$len = length($content);

$content = substr($content, 3, $len + 1); // strip the first two characters from the $content string.

}

 

if($job == 'AI') // only runs if the AI is speaking.

{

if(find($content, '/u'))

{

getfreq(1347); // define the supply channel as the frequency in getfreq().

}

if(find($content, '/n'))

{

getfreq(1351);

}

if(find($content, '/c'))

{

getfreq(1353);

}

if(find($content, '/m'))

{

getfreq(1355);

}

if(find($content, '/e'))

{

getfreq(1357);

}

if(find($content, '/s'))

{

getfreq(1359);

}

if(find($content, '/a'))

{

getfreq(1447);

}

}

 

 

I believe I can make this even shorter, which I'll probably do later.

 

Other misc. scripts:

 

Standard jobseeker

 

 


if($job != 'No id' && $source != 'Unknown')

{

$source = $job + ' - ' + $source;

}

 

if($job == 'No id')

{

$source = 'NO ID - ' + $source;

}

 

if($source == 'Unknown')

{

$source = 'UNKNOWN - ' + $source;

}

 

 

For highlander:

 

 


$source = "THERE CAN BE ONLY ONE";

$foo = explode($content, " ");

$bar = "huehuehue";

$content = repeat($bar, length($foo) - 1);

 

 

Posted

 

Hi Citinited. Firstly, it's great to see someone else adding to this :)

 

I'm going to make some comments about your code. Please take these in the spirit that they're given, namely constructive criticism in order to help you improve your coding, rather than an attack on your code. As I said, it's great to see someone else doing telecomms scripting.

 

Firstly, and most importantly, have you tested the code? Because I don't think it'll work. When I did my code I ran into an issue, namely that I couldn't use / - it seems that the game strips the slashes from the start of AI talk before it reaches the telecomms script. That's why I used @ instead.

 

The second problem I can see is that you're making an assumption about what the user will do, namely that the /e or whatever will be at the start of whatever they put in. Never make assumptions about how your user will act, because as soon as you do, they'll break your code. For example, if I was to use your code and try and say over general -

 

'to talk over engineering use /e'

 

it would go over engineering as

 

'talk over engineering use /e'

 

The third and final problem is one of efficiency. Every time the AI speaks, the content is searched through 7 times, whether or not the code has already found the /u or whatever. As a rule of thumb, you don't want code doing more work than necessary, especially on a platform like byond, which is far from efficient to start with (as an example, simply opening the ban list, which is a table with ~1000 entries lags the server for a good few minutes)

 

Shorter code is great, but only if it does the job as well and as efficiently as the longer code.

 

Posted

 

Thanks for the reply, Vampyr. That's fine, I often find that writing code and putting it out helps with my own understanding as much as anything else, and often leads to better code.

 

I'll respond to your points individually.

 

Firstly, and most importantly, have you tested the code? Because I don't think it'll work. When I did my code I ran into an issue, namely that I couldn't use / - it seems that the game strips the slashes from the start of AI talk before it reaches the telecomms script. That's why I used @ instead.

 

 

To be fair, as any special character can be used here, I see this as a non-issue. However, / does indeed work, and I have tested this out already on both a server ran on my laptop and the paradise server, as AI. Perhaps NT Script was updated?

 

The second problem I can see is that you're making an assumption about what the user will do, namely that the /e or whatever will be at the start of whatever they put in. Never make assumptions about how your user will act, because as soon as you do, they'll break your code. For example, if I was to use your code and try and say over general -

 

'to talk over engineering use /e'

 

it would go over engineering as

 

'talk over engineering use /e'

 

 

This is a good point, but with the script I wrote it would only be used by the AI, and not anybody else. This means that the situation you posted wouldn't come up. While what I just outlined is semantics, this can be easily solved by only running the script should the first character be a /, or an @ as in your example. However, I quite like it running whenever the script finds a /e or whatever as it means that messages not properly checked by the AI player (consider the message "sa /eBreach in atmos!", for example) will still retain their confidentiality. However the problem you outlined could easily be solved by not running the script should the AI player include another set of special characters. For example:

 

 


if(find($content, '/u'))

{

//stop the script.

}

 

 

The third and final problem is one of efficiency. Every time the AI speaks, the content is searched through 7 times, whether or not the code has already found the /u or whatever. As a rule of thumb, you don't want code doing more work than necessary, especially on a platform like byond, which is far from efficient to start with (as an example, simply opening the ban list, which is a table with ~1000 entries lags the server for a good few minutes)

 

Shorter code is great, but only if it does the job as well and as efficiently as the longer code.

 

Another great point, and one which could be semi-solved by running the code only if the special character is found. (@ is actually better for this purpose given that the AI is unlikely to use the character @ in their normal tasks.) So that would mean that every time the AI speaks, only one line of code is executed if no @ symbol is found.

 

Good points to consider, and I might end up writing a second more streamlined version than this one. However, one of the problems of NT Script is that it has little documentation, and I need to find the correct place in the github file that deals with NT Scripting.

 

 

I know this is an old post but how do you add more than one script at a time? Every time I try I break the traffic control console

 

You should just be able to paste it below the other scripts, but make sure that curly braces are properly formatted and that every line ends with a semicolon.

 

Posted

 

 

Firstly, and most importantly, have you tested the code? Because I don't think it'll work. When I did my code I ran into an issue, namely that I couldn't use / - it seems that the game strips the slashes from the start of AI talk before it reaches the telecomms script. That's why I used @ instead.

 

 

To be fair, as any special character can be used here, I see this as a non-issue. However, / does indeed work, and I have tested this out already on both a server ran on my laptop and the paradise server, as AI. Perhaps NT Script was updated?

 

 

 

As long as it works, that's fine. My main concern here was people that don't understand the code copying and pasting it into the server and finding it wouldn't work rather than it being difficult for you to fix. And yes, I would guess that NTSL has been updated.

 

The second problem I can see is that you're making an assumption about what the user will do, namely that the /e or whatever will be at the start of whatever they put in. Never make assumptions about how your user will act, because as soon as you do, they'll break your code. For example, if I was to use your code and try and say over general -

 

'to talk over engineering use /e'

 

it would go over engineering as

 

'talk over engineering use /e'

 

 

This is a good point, but with the script I wrote it would only be used by the AI, and not anybody else. This means that the situation you posted wouldn't come up. While what I just outlined is semantics, this can be easily solved by only running the script should the first character be a /, or an @ as in your example. However, I quite like it running whenever the script finds a /e or whatever as it means that messages not properly checked by the AI player (consider the message "sa /eBreach in atmos!", for example) will still retain their confidentiality. However the problem you outlined could easily be solved by not running the script should the AI player include another set of special characters. For example:

 

 


if(find($content, '/u'))

{

//stop the script.

}

 

 

 

 

Ok, having it search the whole message to ensure a typo doesn't reveal a confidential message makes sense. If you want to do it that way I would however suggest you change the code that strips out the /e or whatever, as at the moment that always strips the first 3 characters. A tip to help you do this - find() doesn't just return a yes or no response, it returns the location in the string of the first character. Using that and a couple of substrs you can take it out from where it actually is rather than always at the beginning.

 

The third and final problem is one of efficiency. Every time the AI speaks, the content is searched through 7 times, whether or not the code has already found the /u or whatever. As a rule of thumb, you don't want code doing more work than necessary, especially on a platform like byond, which is far from efficient to start with (as an example, simply opening the ban list, which is a table with ~1000 entries lags the server for a good few minutes)

 

Shorter code is great, but only if it does the job as well and as efficiently as the longer code.

 

Another great point, and one which could be semi-solved by running the code only if the special character is found. (@ is actually better for this purpose given that the AI is unlikely to use the character @ in their normal tasks.) So that would mean that every time the AI speaks, only one line of code is executed if no @ symbol is found.

 

 

 

That would work. Something else that would help is after the first if using elseif so it only keeps searching if it hasn't already found the code and dealt with it. Although thinking about it, you may not want to do that... I've just realised there's a benefit to your code that I'm not sure if you realise... namely that you can actually broadcast over more than one channel at a time. So if the AI wants to say something to both command and security they can do '/c /s Intruder in the bridge'. That ability is probably worth the extra processing.

 

Good points to consider, and I might end up writing a second more streamlined version than this one. However, one of the problems of NT Script is that it has little documentation, and I need to find the correct place in the github file that deals with NT Scripting.

 

 

the best documentation I've found is TG's wiki - http://www.tgstation13.org/wiki/NT_Script. However, I did find that some of the functions they have there don't work on our version.

 

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. Terms of Use