NPC-Server for GServer (Graal Server for Graal2001)

The NPC server is a program which logins to the gserver
similar to a normal user and controls npcs in a 
specified set of levels. It runs in a loop, executes
npc scripts and sends the updates of the npc attributes
and player attributes to the gserver, which then sends
it to the players if needed (only a few informations
are sent to the player).

1. The npc server program consist of following instructions:

- load options
- connect to gserver
  if connection can be established:
  - load levels
  - load npcs from database
  - while the connection to the gserver is up:
    - get data from gserver;
      this contains following type of data:
      - player attributes
      - temporary level data (bombs, explosions, bush removing, items, ...)
      - PMs
      - game time
      - levels to update/reload
      - server. flags added/deleted
    - run all npc scripts
    - send changed npc attributes to the players
    - send changed player attributes to the gserver & players
    - wait till 0.1 seconds are over

    
2. The scripting on the npc server is different then the
Graal client-side scripting. Some commands are missing
(mainly temporary graphical / sound stuff), some
commands are added (easier accessing of player attributes).
In the section 1 you might have seen that the minimum timeout
is now 0.1 seconds because the npc scripts only run all 0.1
seconds.
There is a new set of events happening on the npcserver:
- created			the npc script is run for the first time
- initialized		the npc is reloaded (e.g. restart of npcserver)
- playerenters		a player enters the current level
- playerleaves		-"- leaves the current level (doesn't happen when he disconnects)
- playertouchsme		the player touchs the npc (see setshape command to make this working)
- playerchats		the player chat text has been changed
- npcwarped 		the npc has entered a different level (see canwarp / canwarp2)
- exploded			an explosion has hit the npc
More events are added when needed.
All code of the npcs are done server-side, except when
you add a line //#CLIENTSIDE, then all following code is sent
to the client. The client only reads attributes of the npcs,
but doesn't send it to other players when he changes it. So
the client-side part of the script is for graphical stuff
and music, not for things which must be secure or need to
be synchronized with other players.


3. Following commands are supported by the npc server:

- destroy;
- if (condition) command(s);
- while (condition) command(s);
- for (command;condition;command) command(s);
- break;
- continue;
- sleep time;
- set flagname;
- unset flagname;
- setstring flagname,value;
- message messagetext;
- setgif/setimg filename;
- setgifpart/setimgpart filename,offsetx,offsety,width,height;
- setshape shapetype,width,height;
- drawoverplayer;
- drawunderplayer;
- dontblock;
- blockagain;
- show;
- hide;
- noplayeronwall;
- showcharacter;
- setcharprop messagecode,value;
- setcharani ganiname,parameter(s);
- setchargender male/female;
- putbomb power,x,y;
- putexplosion radius,x,y;
- putexplosion2 power,radius,x,y;
- setarray variablename,arraysize;
- canwarp;
- canwarp2;
- cannotwarp;
- callnpc index,eventname;
- with (players[i]) command(s);
- with (getplayer(accountname)) command(s);
- with (npcs[i]) command(s);
- with (getnpcs(name)) command(s);
- join classname;
- warpto levelname,x,y;
- tokenize str;
- tokenize2 delimiters,str;

- setplayerprop messagecode,value;
- setani ganiname,parameter(s);
- setgender male/female;
- say signindex;
- enableweapons;
- disableweapons;
- toweapons weaponname;
- addweapon weaponname;
- removeweapon weaponname;
- setlevel2 levelname,x,y;
- freezeplayer2;
- unfreezeplayer;

Explaination of new commands:
- setshape:
  Because the npcserver doesn't load images and
  so doesn't know the size of the npc this is needed
  for letting the npcserver know what the size of the
  npc is so that it can automatically detect if the
  player touchs the npc;
  at this time only rectangle shapes are supported,
  so you need to do setshape 1,width,height;
  (0 would be directly take the image, this will
  be supported on client-side too someday)
- noplayeronwall:
  When doing onwall() or onwall2() then the npcserver
  doesn't check for players, this can be good
  if you want to do your own detection for near
  players or when the npc shouldn't be stopped
  by players (like the Elephant, it alway has
  players on it so it couldn't move)
- setgender/setchargender:
  simply changes the gender of the player/npc
- canwarp/canwarp2:
  This command only works for database npcs,
  other npcs are not allowed to go out the level;
  by calling these commands you enable
  automatic warping when the npc touchs a link;
  canwarp is for npcs which already know the way
  and want to be warped exactly like a player
  (good when you use the waytracker item for
  recording a way for police npcs or so);
  canwarp2 is for overworld npcs which move
  randomly, so this is using the normal links
  and check for onwall on the destination level,
  it also doesn't let the npc go into small
  links (houses)
- cannotwarp:
  disables automatically warping again
- with:
  Normally you can get informations about
  players by accessing players[i].xxx variables
  or attaching a (i) to message codes (like #a(i)
  gives you the account name of the i-th player
  in the current level),
  but when doing server-side npcs then you
  also want to use all the commands you have for
  manipulating players aimed at special players,
  for this you can use the with command
  with (players[i]) { setlevel2 worldl-17.nw,30,30; }
  will warp player with index i to the specified level,
  with (getplayer(Stefan)) { setplayerprop #3,head19.gif; }
  will give the player with accountname Stefan
  the head with filename head19.gif;
  when the requested player doesn't exist then
  the commands inside { } will not be executed;
  you shouldn't do sleep/break/continue inside
  a with block because then the old player is not
  reset when leaving the with block
- with (npcs):
  The commands with (npcs[index]) ... and with (getnpc(name)) ...
  are similar to the 'with' commands which change the current
  player, they just set the current npc. All this. variables
  and variables based on the npc level are now read from the
  requested npc. Only local script variables (like i,j) still
  work inside the 'with' brackets. When calling 'with' to change
  the current npc it doesn't change the script itself, the
  commands and functions are still called from the original npc.
- join:
  This normally loads a text file (join bomys; will
  load bomys.txt from the configured scripts folder)
  and adds it to the npc script, so it works
  similar to an include;
  the internal representation is slightly different:
  each npc has a list of scripts, the own script
  is parsed and added as the first script in this list;
  when you do join then another script is parsed
  and added to the script list;
  when the script for the requested class is already
  in the memory (used by another npc) then it is not
  copied, it is just added to the script list;
  so when several npcs are using the same class
  then it is only hold one time in the memory and
  the script object is put into the script lists
  of the npcs;
  functions can be overwritten
- warpto:
  warps the npc into another level, only works
  when it is a database npc
- addweapon:
  This is like 'toweapons', but the script for the
  weapon is not taken from the current npc, its taken
  from database, which means the weapon must already
  exist. This command is used in the trading house in
  Graal2001.
- removeweapon:
  just deletes a weapon
- tokenize/tokenize2:
  This comes with a bunch of functions and message codes
  to support string parsing:
  - strlen(str) - lenght of the string
  - startswith(part,str) - tests if 'str' is starting with 'part'
  - indexof(part,str) - gives the position of 'part' in 'str'
    (-1: doesn't appear in 'str')
  - #e(start,lenght,str) - extracts a substring out of str
  - #T(str) - removes the spaces at the beginning and at the end
  - tokenize str; - divides the specified string into tokens,
    e.g. tokenize I am happy!; will produce 3 tokens (I, am, happy!)
  - tokenscount - gives you the count of tokens produced with tokenize
  - #t(index) - gives you a token
  - tokenize2 delimiter,str; - while tokenize always uses
    spaces and commas, you can add more delimiters here,
    e.g. tokenize2 o, Yoshi is cool; will give you (Y,shi,is,c,l)
      

4. Variable types

There are several new variables types, here
a list of all currently available types:

- Floating point numbers which can be used in normal
  assignments. They differ in some attributes: some are
  read-only, only available in a certain parts of
  the world / of the scripts, or are not saved.

  - varname
    only available for the current script / class script
  - this.varname
    for the current npc; when this is a database npc
    then the this. variables are saved in the database,
    all other variables are dynamically and not saved
  - level.varname
    for the current level (where the npcs is in)
  - server.varname
    server-wide variables (only for the current
    npcserver)
  - object attributes (npcs, players, signs)
    like in normal graal, only one new variable
    (playerlastdead) which remembers the time of
    the last dead / login (works only on graal2001
    yet)
  - built-in variables

- Flags / string variables (can be used with
  set, unset, setstring and the #s message code)

  - flags
    are for the current player and are stored in
    his account
  - client.flags
    like normal flags, but will also be sent to the
    client and the client can change them
  - this.flags
    are for the current npc and are stored in database
    too when the npc is a database npc
  - server.flags
    like on normal graal, they are server-wide and are also
    shared with other npcservers of the same world;
    they are not sent to the player


5. NPC server commands

You can control the npcs by sending PMs to the npcserver
which is logged into the game server like a normal player.
The npc server will then send you a responce. At this time
only PMs of players with (NPC Admin) tag in the nickname
will be answered.
Most commands are only one line, some commands can
contain several lines (e.g. when updating the script
of an npc).
You get a list of commands when sending some unknown command
(like help) to the npcserver.

Important are following commands:
- add npc
  An example is:

  add npc Paul
  1000
  POLICE
  Stefan Knorr
  worldl-17.nw
  30
  30

  This adds an npc with the name 'Paul', with id 1000,
  type POLICE (max 6 chars), scripter 'Stefan Knorr'
  into level worldl-17.nw at position 30,30.
  The id must be unique, so the next npc must get the
  id 1001. All database npcs (except built-in npcs like
  the gralats) must have ids >=1000.
  The level name and position is the start position,
  when you do reset npc then the npc will be warped
  back to this position.

- reset npc <npcname>
  Resets all npc attributes (except the script) and
  puts it back to the start position.
  The name of the npc must be correct, its
  case sensitive

- update script <npcname>
  An example is:

  update script Paul
  if (created) {
    setimg jrock.gif;
    setshape 1,32,32;
  }
  if (playertouchsme)
    say 0;

  This changes the script of an npc.
  After the first line you need to add
  the script. The script of the npc is updated,
  but when the npc does sleep or so then it might
  not continue to run, because the npcserver
  stops a running sleep, the return line might have
  changed. So you will sometimes need to do
  'reset npc <npcname>'

- get script <npcname>
  Returns the script of an npc or
  of a class. Turn off HTML in PMs when you want to
  see it correctly formated.

- update class <classname>
  Works similar to 'update script' but this
  changes the script of a class which npcs can
  join with 'join <classname>;'. It is saved
  in the scripts folder of the server. When
  npcs already member of that class then they
  will automatically use the updated class script
  (because its only hold one time in the memory)

- get npc list
  List all database npcs (id>=1000) and their
  position.

- dump vars,
  dump vars <npcname>
  Dumps all variables of the current level or the specified
  npc.

- reload level list
  In the npc server configuration you must specify a text file
  which lists all levels that are served by this npc server.
  When you add new levels and you want the npcs working in that
  level then you need to add the new level names to the
  level list, then send 'reload level list' to the npcserver.

  
6. Message codes

There is a new message code #F for the level name of the
current player because it can be different to the level
of the npc (#L), e.g. when the player leaves the level
(if (playerleaves) ...) or you do with (getplayer(accountname)) ...
Also read the comments about the 'tokenize' command for
more message codes.