NPC Code:
== '''Excaliber's Scripting Guide''' ==
I'm hoping this allows people to learn how to script a bit easier, I believe all staff should know some scripting basics, and I find other GS2 guides hard to follow.
Starting:
Level NPCs:
A level NPC is an npc that functions in a specific level. These are the most common on most servers. They are also a very important part of developement.
A script:
<pre>
//this is a note
/*
this is also a note, these help make scripts more clear
this type of note can go on as longe as it's inclosed
*/
//#CLIENTSIDE
//This tells the computer that the following
//script will be on the players client,
//as opposed to the server, this is very important.
function onPlayertouchesme()
//this checks if the player has touched the NPC
{ // starting bracket
message("Hey!"); //displays a message above the NPC
}//closing bracket
</pre>
The above script checks to see if the event, playertouchsme is true, then acts on what is in the bracket. It is important to include this clientside, because too many serverside scripts can cause problems for an NPC server, however thay are more secure. If you wanted to do the last script serverside, you would omit the //#CLIENTSIDE , and add the shape to the NPC:
<pre>
function onCreated()
{
setshape(1,32,32);
setimg("block.png");
//img can also be done in the box above the script
}
function onPlayertouchsme()
{
message("You made a serverside script!");
}
</pre>
This takes a bit longer than clientside commands, and it takes up serverside script time, however, for secure actions, it may be necessary.
A secure script example:
<pre>
function onCreated()
{
this.account="Excaliber7388";
setshape 1,32,32;
chat="Excaliber's Chest";
}
function onPlayertouchsme()
{
if(player.account==this.account)
{
chat="This is a secure message!";
}
}
</pre>
First, I would like to quickly point out the 'if' statement. Some guides go into this a lot, but it's actually quite simple. The if statement is a very important part of scripting. It's what allows the NPC to make choices. The code withing the brackests of an if statement will only execute if the statement is true. Also, there is a catchall statement you can use, else. If the if statement is not true, the else will exectue it's script, other wise it is ignored.
Example:
<pre>
//#CLIENTSIDE
function onCreated()
{
this.a=1;
}
if(this.a==1)
//always use '==' for comparasons, NEVER just '=' as '=' is for assignments
{
this.b=2;
}
else if(this.a==2) //will only execute if first is false, AND this.a==2
{
this.b=3;
}
else //catch all if above 2 statements are false
{
this.b=4;
}
</pre>
Now, in the above example, this.b will always be 2, however, it's the format that's important.
<pre>
if(statement)
else if(statement)
else if(statement //may be used many times
else //catch all
</pre>
Now, back to serverside and clientside scripts:
By putting the checks on serverside, you disable a hackers ability to send an action from their client. So, this script is much more secure, and can only be accessed by the specified account (Excaliber7388 in this case). Dark Rival does these checks serverside as well, but we also apply a different method, to avoid having too much serverside script time:
<pre>
function onCreated()
{
this.account="Excaliber7388";
setshape 1,32,32;
chat="Excaliber's Chest";
}
function onActiontouched()
{
if(player.account==this.account)
{
chat="Yup, this is still secure, yay!";
}
}
//#CLIENTSIDE
function onPlayertouchsme()
{
triggeraction(x,y,"Touched","Touched";//no params needed
}
</pre>
This script will take up more time, but the playertouchsme check is clientside. Would this help much? Probably not, in fact, sending the action in this case would probably create more lag than having the script serverside. However, if a large portion of the script is serverside, and can be moved to clientside without risking security, it may be a good idea.
Serverisde timeouts=bad idea.
You shouldn't send to many actions to serverside, especially if you do so quickly. You should always limit the serverside timeouts, and the amount of time between actions to the server from client should be spaced out. Also, you should never have a timeout shorter than .05 on serverside, though it is possible, please do not do it. Dark Rival has had problems in the ast regarding serverside loops, however these have ben fixed.
Here are two examples of serverside scripts, the first it VERY bad, the second is more productive:
<pre>
function onCreated()
{
setshape 1,32,32;
}
function onActiontime()
{
for(server.i=0;server.i<5;server.i++)
//WHY use server for this???
//for loops, use this. it works only for this NPC
{
sleep(.01);
//WAY to short of a sleep time for the server
}
server.time+=.1;
}
//#CLIENTSIDE
function onCreated()
{
settimer(.05); //minimum clientside timeout
}
function onTimeout()
{
trigegraction(x,y,"time","time");
settimer(.05);
//will cause a loop through the timeout, sending action
//to serverside TOO quickly, AND while the server is
//still working on the last action. This WILL cause the
//server to crash.
}
</pre>
If you put the above script on a server, say goodbye to your NPC server, because it will crach pretty quickly!
A better version:
<pre>function onCreated()
{
setshape 1,32,32;
//setshape is needed for all level NPCs triggeractions
//this setsit to a square of tiles 32 pix by 32 pix
}
function onActiontime()
{
server.time++;
//no need for incredible accuracy
}
//#CLIENTSIDE
function onCreated()
{
settimer(1);
}
function onTimeout()
{
triggeraction(x,y,"time","time");
settimer(1);
}
</pre>
This will execute the script less often, however, it still is an unlimited loop. Don't have too many of these NPCs, or you could have problems!
Now, you may have noticed this line in the bad script:
<pre>
for(server.i=0;server.i<5;server.i++)
</pre>
This is a 'for' loop. There are many types of loops, however the foor loop isn't the easiers to learn first. Therefore, I will show you the 'while loop' first.
The while loop executes whatever is held within it's brackets as lond as the statement within it is true. The while loop is just like an if statement, but you cannot use else with a with statement.
format:
<pre>
wile(statments)
{
//commands
}
//if statment is still true, it will loop
</pre>
here's a script example:
<pre>
//#CLIENTSIDE
function onCreated()
{
this.size=10;
}
while(this.size<=20) //while this.size is less than or equal to 20
{
chat="The fish was " @this.size@ " centimeters long!";
//as the story is retold....
sleep(.5);
this.size=this.size+1;
//increment the size
}
</pre>
The above script will run through the loop until this.size is equal or greater than 20.
Some important things to know:
<pre>
if(a==b) means if a is equal to b
if(a<b) means if a is less than b
if(a>b) means if a is greater than b
if(a<=b) means if a is less than or equal to be
if(a>=b) means if a is greater than or equal to be
a++ is the same as a=a+1
a-- is the same as a=a-1
a+=b means a=a+b
a-=b means a=a-b
a*=b means a=a*b (multiplied by b)
a/=b means a=a/b
++a is just like a, but it will add to a before anything else in the line,
this is not important to know now
--a just like ++a, but with subtraction
a%b Modulus, basically, its a/b and th remainder is the output
for example 10%3=1
</pre>
These may be helpfull in loops, or in any other rational expressions you may use. (Well, it's important for all scripting!)
Now, recall this line:
<pre>
chat="The fish was " @this.size@ " centimeters long!";
</pre>
The output for this is: The fish was (size here) centimeters long!
The script NEEDS to know where a variable is, so if you want to put it in between a string you surround it with the '@' symbol. Or if it's at the end, you only put the '@' at the beggining of the variable. The variable and the '@' do NOT go in the quotes. Example:
<pre>
//#CLIENTSIDE
function onPlayertouchsme()
{
chat="Well hi there, " @player.account@ " I like your sword, " @player.swordimg;
}
</pre>
This would display the player's account, and sword image.
Now, back to the 'for' loop.
This a very interesting loop it's format is as follows:
<pre>
for(command;expression;command)
{
}
</pre>
Here's an example:
<pre>
//#CLIENTSIDE
function onPlayertouchsme()
{
for(this.i=0;this.i<10;this.i++)
{
chat="Loops: " @this.i;
sleep(.5);
}
}
</pre>
This loop would be activated when the player touchs it.
It would go through 10 loops, displaying the count, and taking .5 seconds for each loop (due to the sleep() command)
Now remember this while loop?
<pre>
function onCreated()
{
this.size=10;
}
while(this.size<=20)
{
chat="The fish was " @this.size@ " centimeters long!";
//as the story is retold....
sleep(.5);
this.size=this.size+1;
//increment the size
}
</pre>
There's an easier way of doing it with a for loop, in fact all loops done with wile can be replaced by for.
<pre>
for(this.size=10;this.size<=20;this.size++)
{
chat="The fish was " @this.size@ " centimeters long!";
sleep(.5);
}
</pre>
See? Much shorter and easier to use!
Once you know the format of a for loop, you can make longer and more complex loops as well, and they'll take less time to script, which is always a bonus.