DINKC reference.


** What's new in 1.06? (10-19-99) ** 

Only one feature has been added.  You can attach a script to nearly any
key on the keyboard now.  This way, you can have A create an apple and +
make something bigger or whatever.  Inside of source.zip there is a file
called key-80.c.  If this exists in the story dir, pressing key 80 will
run this script.  (it's actually in Dink too, try pressing P you should hear
a pillbug squeal in delight)

Keys Dink uses internally such as Shift, Enter, Ctrl, etc are not
usable.  F1-F12 and the numpad are though!  Hit Alt-D to get into debug
mode while testing your DMOD and it will show you the numeric value for
any key you hit.

To recap:

If a file called  "key-(anynumber here).c" exists in the story dir, Dink
will load it and run the main() procedure in it.  Don't forget to call
killthistask(); when done, or the script will stay in memory...

** preface **

For info on DINKEDIT.EXE, check TUT1.TXT, is a tutorial on how to use
it to create your own addon/adventure.

This file is an attempt to give detailed information on every DinkC command.

Creating DINK addons isn't for beginners - at least some programming
experience is required, hopefully in C.  Although studying included .C
files might be enough...

In any case, I hope to continue to improve the system and language and offer
tutorials and other help files to make creating new worlds for Dink an easier
process.

Please visit http:  www.rtsoft.com to find the latest Dink info and
developement tools/info.

If you have any questions or comments, feel free to email me, mime encoded
file attachments are ok if you'd like me to check out something you are
doing.  Unless I get totally busy I'll have time for everyone.

I really enjoy helping and I believe that the more creative minds that use
this system to bring their own story and characters to life, the better.

Thanks,

Seth Able Robinson
email: sethable@rtsoft.com


  Part 1:  DinkC - what is it?
  Part 2:  List of internal procedures
  Part 3:  A changing world
  Part 4:  How items work
  Part 6:  How the choice commands work
  Part 5:  Advanced techniques
  Part 7:  Known limitations
                                 PART 1

                       -= DINKC - WHAT IS IT? =-

  Ok, it is a scripting language.  I created a simular language for a
  door game I wrote called LORD2, and I got a LOT of email about what people
  liked and didn't like so I made this one better.  A lot better.

  DinkC supports:

  * The format is in standard C/C++ for the most part
  * Nested loops thousands of levels deep
  * Procedural structure support, limited only by stack
  * Both local and global dynamic variable creation with custom names. All
    GLOBALS you create are saved with the player data file automatically.
  * Attaching a DinkC script to an object, person or monster - giving it's
    own brain.
  * Up to 200 DinkC scripts can be running AT THE SAME TIME.
  * Memory for scripts and other things are dynamically created and
    destroyed continuously.  Blank spaces in the .C file ect will not
    waste space.
  * Powerfull callback functions to tell Dink when to run your script
  * If an object has a script associated with it (done in DinkEdit) if
    hit, talked to, killed ect it will automatically look in its script
    for a hit(), die(), attack() or talk() procedure.
  * Run with /DEBUG Dink.exe will report all errors/debug strings in DEBUG.TXT
  * Compilable with compile.exe, file gets smaller and encrypted.



*** THE COMMANDS ***

When a Dinkc file is associated with a sprite, a function called main() is
loaded and run when the screen is loaded.  This is where you establish
when what procedures will be called when.

Map screens can also be associated with a sprite, this is run before the
screen is drawn and the sprites are created. (&current_sprite and sp() don't
work yet, because no sprites exist!

Even though the syntax is C/C++, there are many differences, most are to
make writing dinkscripts easier, such as all var names must start with
&, but you can put a var name in the middle of any string and it will be
deciphered correctly.

For instance:

Say("You have &life life points, dink.", &current_sprite);

Would make the NPC say:

"You have 15 life points, dink."

This is true of all variables, no matter what type.


                                PART 2

                 -=  THE LIST OF INTERNAL PROCEDURES  =-                     

make_global_int( char nameofvar[20], int default_value);

    this makes a global int - the default_value is only given to it if
    this is the first time it has been initted and doesn't exist already in
    the saved game file. (usually these are in the main.c file)

int &junk;
int &crapvar = 0;
int &num = sp(12);
int &nummy = random(100,1);

  The int statement creates a variable accessable ONLY to the current
  instance of the current script.  It differs from C in one way:  The scope
  of an int in DinkC is the ENTIRE script.  Any procedure in the script can
  see/use this var.

int random(max, plus_this);

  Random(10,1) would return a number between 1 and 10.  Random(5,0) would
  return a number from 0 to 4.  Simular to how C does it.

int scripts_used();

  Returns the # of scripts being used right now.  If > 190 or so, you
  probably don't want to create any more until the guy moves to another
  screen or something.

int inside_box(int x, int y, int left, int up, int right, int down);

//is the x and y cord inside this box?  Returns 1 if yes.

int busy(int sprite);

  returns 0 if this sprite is not 'talking' to someone.  Else it returns
  the sprite # of the speech.

if (&life > 5) &life = 0;
if (random(10,1) != 1)
   {
    say("The number is not 1!",1);
   } else
   {
    say("The number IS 1!",1);
   }

   works like a standard if statement

debug( char string[200] );

  Writes to the debug.txt file if the -debug parm is set when dink.exe
  is run.  Var names can be used inside the string.

initfont( char string[200] );

  Dink will change to this font.  Default is Arial.  You can change fonts
  as many times as you want.  This was included so international translations
  would be easier.

void show_bmp (char string[200], int over_postion?, int unused)

  Needs dir.  Example:

  show_bmp("tiles\crap.bmp", 0,0);

  The first # parm tells dink to show a crosshair of their position
  on the map, used in Dink to show the location, only works if you make
  the map from a screen shot from Dinkedit.

void copy_bmp_to_screen(char string[200]);

  Copies any .bmp file to the screen.  Good way to not waste memory, needs
  to be Dink's pallete.  (or whatever splash.bmp is, this is where Dink
  get's it's base pallete)

void wait_for_button();

  This lets you control the input to do certain things.  Used in the
  shark event in Mystery Island.

  After this is called, the script will wait for a button to be pressed.

  The value is in &result.

  Here is some diagnostic code that shows what the values mean:

  //input is read from keyboard and gamepad

  if (&result == 1) say(1, "You pressed button 1!");
  if (&result == 2) say(1, "You pressed button 2!");
  if (&result == 3) say(1, "You pressed button 3!");
  if (&result == 4) say(1, "You pressed button 4!");
  if (&result == 5) say(1, "You pressed button 5!");
  if (&result == 6) say(1, "You pressed button 6!");
  if (&result == 7) say(1, "You pressed button 7!");
  if (&result == 8) say(1, "You pressed button 8!");
  if (&result == 9) say(1, "You pressed button 9!");
  if (&result == 10) say(1, "You pressed button 10!");


  if (&result == 12) say(1, "You pressed DOWN!");
  if (&result == 14) say(1, "You pressed LEFT!");
  if (&result == 16) say(1, "You pressed RIGHT!");
  if (&result == 18) say(1, "You pressed DOWN!");


void stop_wait_for_button();

  If you need to give back normal gamepad/keyboard control for some
  reason (like the guy died or something) call this.  What happens
  to the script that called it to begin with?  Good question.


void push_active( 1_or_0);

  1 by default.  If 0, the human_brain (usually Dink) will not allow
  'pushing'.  I use this when I turned Dink into a duck on Mystery Island.

void say(char string[200], int sprite);

  Says the string above the sprite you wish.  Use &current_sprite to
  choose the 'owner' of the script.  (assuming it is attached to a
  sprite)
  If sprite was already talking, the old text will be erased. (so they
  don't overlap)
  If sprite is 1000, this will be a 'text only' say, not attached to a
  sprite.  If this happens, you can use the return int to set it's x and
  y.  1000 is used for cut scenes - it also turns off centering.

  If you need to know the SPRITE # of a certain text sprite, you can check
  &last_text at anytime.

int say_xy(char string[200], int x, int y);

  Same as above except automatically attaches it to 1000 and lets you
  specify X Y cordinates. (returns sprite #)

  If you specify ` (reverse apostrophy/unshifted tilde) and then a #, it
  will show the text in this color.  Example:

  These are the same as LORD color codes in the BBS world.
  1234567890!@#$% can be used.  (15 colors)

   say("`4Hello!", 1); (reddish)

   NOTE:  If text doesn't seem to be showing up after a `%, it's probably
   because you have a I, D or J next, which are C syntax for other
   things - just put a space after `% and you'll be fine.

say_stop(char string[200], int sprite);

  same as above but stops this script until the thing is said

say_stop_xy(char string[200], int x, int y);

  Same as above except automatically attaches it to 1000 and lets you
  specify X Y cordinates.

say_stop_npc(char string[200], int sprite);

  Say as say_stop, but will not let the PLAYER skip things by hitting
  space - if the player may be talking to someone else while this
  is called, (like two girls talking in the background) you should use
  this, it is safe.  Otherwise callbacks may conflict...


wait(int amount);

  stops this script for this length in thousands of a second.

freeze(int sprite);

  this sprite now cannot move on its own.  Can still be killed though.

unfreeze(int sprite);

  to use with above, sprite can move again.

move(int sprite, int direction, int destination, int nohard);

  This sprite will move in this direction (can be 2, 4, 6 or 8) until
  sprites X or Y (depending on dir) meets or exceeds 'destination'.  If
  nohard is 1 then the sprite can walk through hardness, if 0 it will get
  stuck on walls like normally.

move_stop(int sprite, int direction, int destination, int nohard);

  same as above but stops script until the sprite has met the destination
  use carefully, if the sprite hits something, it will NEVER get to the
  destination and therby the script will never be finished.  Won't cause
  a game crash or anything though.  

set_callback_random(char name_of_proc[20], int min, int max);

  tells Dink to run this proc inside of the current script every
  min + random (max) thousandths of a second.

goto crap;
  skipping this part
crap:

  Goto differs from C in one way:  The scope of a goto is the ENTIRE script.
  You can 'goto' other procedures.

return;

  exits the script.  returning values (ie, return(&var) is not supported.

my_proc();

  This is how you run a procedure located in the current script.

void external(string name_of_c_file, string name_of_proc);

  This is how you run a procedure in another script.  It will return
  when it is finished.  Example: external("make", "func1");


int spawn(string name_of_c_file);

  Sort of like above - but calls it's main() and doesn't stop the script
  it was called from, or affect it in any way.  Be carefull with this.. if
  you create a 'spawn to infinity' loop you won't like it.

  Anything spawned this way is NOT attached to a sprite, (use sp_script if
  you wish to do this) and must be killed manually when you are finished
  with it, or it will ALWAYS be in memory.  (it can live past a screen
  change...)  Do a kill_this_task(); to kill it when done.

  Returns the script # created.  0 if there was an error.

int sp(1);

  VERY important - this returns the actual SPRITE # of a certain EDITOR screen
  object.  For instance, if you placed one tree on a screen with the editor,
  you could use the above to get the sprite # of the tree.  (it would
  most likely be 2, (dink is ALWAYS 1) but never guess)

int sp_editor_num(int sprite);

  the opposite of the above - returns the EDITOR # of a given sprite.  Will
  return -1 if sprite did not originate from the editor.

int is_script_attached( int sprite num );

  Returns the script id # in memory attached to the sprite.  0 if none.
  dam-fire.c uses to determine if a tree has something 'special' going on
  with it. (so it doesn't just burn it like normal)

void run_script_by_number( int script id #, char proc_name[30]);

  For use with above basically.  You can force a script to run it's DIE
  procedure for instance.

int create_sprite(int x, int y, int brain, int pseq, int pframe);

  creates a new sprite.  Returns the actual sprite #.  (uses the 1st unused slot
  it can find, returns 0 if all 300 are already in use.

int get_sprite_with_this_brain( int brain, int sprite calling)

  This returns the first sprite # that is on the screen with this
  brain. It will not include the sprite # sent in the second parm in the
  search, so a sprite can check for 'other brains of it's type' if needed.

int get_rand_sprite_with_this_brain( int brain, int sprite calling)

  Same as above but returns a random sprite #.  Used by dragons to
  target the town folk.

void init(char line[100]);

  Let's you do a line normally done in the dink.ini file.  I use this to
  'replace' graphics on the fly.  (load new graphics to already used
  sequences, for dink's weapons)

  Example:  init("load_sequence graphics\milli\mill- 45 100");

add_exp(int amount, int sprite_to_get_x_y_cords from);

  Let's you add experience visually correct, so the # floats above something.


draw_hard_map( void );

  this will recalculate ALL hardness based on what is current on the
  screen.  If you dynamically turn off a sprite, it will leave the screen, but
  if the sprite has type 0 hardness it will still be there until this is
  called.  This isn't very fast, so don't use it on a regular bases, but instead
  for special things.

draw_hard_sprite( int sprite_number );

  Same as above, but limited to ONE sprites hardbox.  MUCH faster then the
  above.  Breaking barrels use this.

draw_background( void );

  draws the background (tiles and all type 0 sprites).  After seriously
  playing with the screen (like doing a fill_screen) this can fix it up.
  All dead bodies ect will be missing, so keep this in mind.

preload_seq( int sequence);

  Loads the graphic sequence if it isn't already cached somewhere.
  (sequences are defined in DINK.INI)

kill_this_task( void );

  kills the current script completely.  This is rarely used, because
  if a script is attached to a sprite, it will die when the sprite does
  automatically.  ESCAPE.C uses this because it isn't attached to anything.
  (this is run when the ESCAPE button is pressed)

kill_game( void );

  Yup, like it sounds.

fade_down( void );

  the screen will fade to black.  It's up to you to call the following
  command..  The script is paused until this is accomplished, then
  continued.

fade_up( void );

  yay, we can see again.  Script is paused until this is accomplished, then
  continued.

void set_dink_speed( int speed)

  Let's you change dink's walking speed.  3 is normal, 2 is fast. 1 is..
  I don't know, never tried it, but I'm gonna guess real fast.

void reset_timer(void)
  
  Sets the persons time to 0, and starts the counter from now.
  (done in start-1.c, when the guy starts a new game)

force_vision( int new_vision );

  changes vision and updates screen to reflect vision changes - basically
  like walking onto a new screen, but doesn't change dinks location.
  warning: it kills all script and sprites and reconstructs them using
  the new vision.  It also makes the script being run stay alive by
  connecting it to sprite 1000 (script never dies).  You must use
  kill_this_task() or it will NEVER die.

int set_mode(int mode);

  sets the mode.  You should never use this, is used for low level stuff
  mode 2: This loads the players screen and draws the statbar stuff then
  changes it to mode 3 automatically, which is 'regular game mode'
  mode 2: In mouse selection mode (use keep_mouse if you want mouse
          support INSIDE the game portion as well)
  mode 3: Game is running now.

void set_keep_mouse( int O_or_1);

   Let's say you wanted curser like mouse control in your addon, set this
   right after loading the game, and the mouse support will stay.  Note
   that sprite one must have brain 13 for it to activate. (1 is yes)

void activate_bow( void );

  Special hack needed for the bow weapons 'charge up' time stuff.
  (check item-b1.c for example)

  If you use this, keep in mind the bow animations base is 100 and is
  6 frames per dir (it uses 8 directions).  This is hardcoded.

int get_last_bow_power( void );

  For use with above, the weapons script can create the arrow or whatever
  according to how well they shot it.  

void screenlock( int );

  Pass this 1 to 'lock the screen'.  This means Dink cannot walk off the
  screen.

  Pass 0 to change it back to normal.

void dink_can_walk_off_screen( int );

  Pass 1 and Dink can walk off the screen and not trigger a 'screen scroll'
  to the next screen.  I use this for cutscenes that I want Dink to be
  able to walk off the screen on.  I'm entirely tired of saying the word
  screen.

void stop_entire_game( int );

  Set this to 1 and the whole game will be frozen EXCEPT for the choice
  commands.  I needed this for lraise.c, people didn't much like the fact
  that they had to pick where their skill points were going WHILE doing
  battle...  It will unfreeze the game and set this back to 0 automatically
  as soon as something is chosen.

int get_last_sprite_with_this_brain(int brain, int sprite_to_not_count );

  Helpfull with screenlock technology or any instance when you want to 
  know when all enemies are dead, or you want to target an enemy of
  a certain brain type.

  The 'sprite_to_not_count' parm is usually &current_sprite, it ignores
  this sprite with doing its computation.  Use 0 otherwise.

  Check en-pill1.c for example usage.

void fill_screen(int color)

  fills the entire screen a certain color.  0 is black.

int playsound (int sound_num, min_speed, rand_speed_to_add, int sprite_or_0,
                bool repeat?)

  this plays a sound.  If rand_speed_to_add isn't 0, a random # will be
  made from this # and added to the sound. If sprite isn't 0, the sound
  will be 3d based on this sprites location.  If the sprite is killed
  for some reason, the sound will die too.  (ie, a fire goes out)
  if repeat is not 0, the sound will repeat.

  Returns the soundbank used.  Use this for special control of the sound.

void sound_set_vol(int sound_bank, int vol)

   Change the volume of a sound. 0 means NO CHANGE, -10000 means VERY low -
   remember, it doesn't turn off the sound (even if you can't here it), it
   just makes it play quieter. No amplication is done.  -1500 sounds about
   'half volume'.
   
   

void sound_set_survive(int sound_bank, 1 or 0)

   If 1, sound will SURVIVE a load_map command.  Use this on an already
   looping sound to provide 'looping music'.

void sound_set_kill(int bank);

   Good way to kill a certain sound. (any sound, including "survive")

void kill_all_sounds(void)

   Kills all "survive" sounds, good if you don't want to keep track of the
   sound # forever.  Does not effect regular sounds, so essentially it's a
   badly labeled procedure.  I could probably have renamed it in the type
   it took me to explain this.

void kill_shadow(int sprite);

  checks for sprites with a brain of 15 and brain_parm of 'sprite' - and
  kills them.  I use this to remove shadows until projectiles when the
  projectile explodes, but isn't killed.  (shadows kill themselves when
  the sprite they were attached to dies)


void hurt(int sprite, int damage);

  This let's you tell the game to hurt this sprite for a certain amount
  of damage.  Defense will be calculated.  If the hit is for 0, there is
  a %50 chance that it will hit the guy for 1.  (this way there are never
  any 'I'm completely stuck' situations unless we purposely code them)

  Also:  If the thing is hurt blood spurts are added, free of charge.


void script_attach(int # to attach it to);

  you can attach the current script to another sprite, (it will be
  unnattached from the current one) or attach it to 1000, which means
  "no sprite, will not die on it's own".  For instance, if you wanted
  Dink to say "I'm poisoned" every 5 seconds for infinity, (and you didn't
  want the script deleted when Dink moved to a new screen) setting it 1000
  is the way to go.  You must then use kill_this_task to get rid of it, if
  you don't, it will be there FOREVER.

void load_screen( void )

  loads screen

void draw_screen( void )

  draws the screen.

void draw_status( void )

  draws the status bar and side bars

  To warp someone in a script, change &player_map and then call the three
  above procedures.

void playmidi(char midiname[12]);

void stopmidi(char midiname[12]);


void turn_midi_off( void );

   This disables midi's attached to screens. (good if you are using a
   different kind of music and don't want the screen attached midi's to
   ruin it.

void turn_midi_on( void );

  Undoes the above.  This is also called when a game is loaded.

stopcd( void );

  Stops the cd player.

****** COMMANDS TO GET/CHANGE SPRITE ATTRIBUTES ******

A negetive # means 'do not change' in these values.

A return value of -1 means the sprite does not exist (most likely), or there
was an error of some kind.  A good way to check it something is dead or not.

Example:

int &my_speed = sp_speed(1,-1);   gets the speed of dink
sp_speed(1,2);   sets dinks speed to 2

int sp_speed(int sprite, int value (-1 to not change));

  speed of sprite:  If one is still too fast, see sp_timing. (max of 5);
  example:  dink uses speed 1, timing 0.  (no timing delay) and moves
  pretty quick.

int sp_dir(int sprite, int value (-1 to not change));

  Direction of sprite.  If this is set, the sprite will face in this
  direction.  (based on what is in sp_base_walk)  If a speed is set, it
  will be moving in this direction.

int sp_mx(int sprite, int value (-1 to not change));

  X movement.  If you don't want to deal with directions, or need some
  kind of weird movement, this lets you specify in pixels how fast the
  sprite is going.  -5 would move left. 5 would move right.

int sp_my(int sprite, int value (-1 to not change));

  Y movement.  Same deal as above basically.

int sp_disabled(int sprite, int value (-1 to not change));

  If 1, sprite is not currently disabled - it is there, but not being
  drawn or moved.  I used this on the bridge, to make a hardness I
  could remove quickly without wanted to deal with 'visions' in the
  editor.

int sp_size(int sprite, int value (-1 to not change));

  100 is normal.  200 would make this sprite twice its size.  Sizing
  can cause a large performance hit, (unless the card supports hardware
  transparent scaling) so use sparingly.

int sp_hitpoints(int sprite, int value (-1 to not change));

  Hitpoints of sprite.  If 0, nothing happens when this sprite cannot die.
  In both cases, if a script is assigned to the sprite, hit() and die() will
  be run. (well, die is run when their hitpoints < 1, naturally)

int sp_strength(int sprite, int value (-1 to not change));

  Strength of a sprite.  If a sprite has a strength of 10, he will hit
  between 6 and 10.  

int sp_defense(int sprite, int value (-1 to not change));

  Defense of a sprite - this is taken right off the top of the damage
  done to them.  Someone with a defense of 20000 would likely be
  invincible. <g>

int sp_exp(int sprite, int value (-1 to not change));

  How much experience someone would get if they killed 'em.  

int sp_sound(int sprite, int value (-1 to not change));

  Attaches a sound (by #) to the sprite and repeats it until the sprite
  it killed.  Uses '3d' sound cueing. (forces 22kz?)

int sp_timing(int sprite, int value (-1 to not change));

  Allows you to set a delay between how often the sprites brain is called.
  a delay of 33 means 30.3 times a second.  66 means.. 15.15!  Without
  using this, everything would move entirely too fast.

void sp_kill(int sprite, int delay before killing);

  This lets you tell sprite to kill itself in a timed way.
  sp_kill(&current_sprite, 1000); would cause &current_sprite to die
  in 1 second. (0 to disable, use on text to make it stay there)

void sp_kill_wait(int sprite);

  This is the delay created by sp_kill.  Sometimes you want to change
  this to 0, so a sprite will react immediatly.

int sp_x(int sprite, int value (-1 to not change));
int sp_y(int sprite, int value (-1 to not change));

  x and y cordinates of the sprite.  Based on their 'depth dot'. (the
center of the persons foot, usually, or base of the tree)

** ABOUT PSEQ and PFRAME **

We never say "Show pic 788 for this sprite".  Instead we tell it which
sequence, then what frame.  This means you can add frames to any sequence
(max of 50) and not 'throw off' any other pics/animations in the game.

PSEQ and PFRAME are the CURRENT sequence and frame that sprite is showing
on the screen - if SEQ is set, this means the sprite is showing this
animation.  (PFRAME will change very quickly if this is happening)

A tree or rock will not have a SEQ set to 0, meaning no sequence.  The
PSEQ and PFRAME will not change in this case, you can change it to show
another sprite, ect.

If you wanted the tree to suddenly burn, you would set its SEQ to whatever
animation you wanted - now the PSEQ and PFRAME will automatically follow
the SEQ script and run through the animation!  Based on the brain, it
could repeat, too.

Editting the DINK.INI file allows you to add your OWN sequences!!!

int sp_pseq(int sprite, int value (-1 to not change));

  sequence of the current pic being displayed
  change this to change the sprites pic

int sp_pframe(int sprite, int value (-1 to not change));

  frame of the sequence of the current pic being displayed
  change this to change the sprites pic

int sp_seq(int sprite, int value (-1 to not change));

  current sequence being used as an animation, 0 if none
  change this to have the sprite play this sequence like an animation.  When
  finished, it will change back to 0 unless the sprites brain is a
  'repeating' brain such as a woodstove fire.

int sp_frame(int sprite, int value (-1 to not change));

  frame of the sequence of the ANIMATION this sprite is playing, not valid
  if now 'seq' is set. (seq is the animation)

int sp_frame_delay(int sprite, int value (-1 to not change));

  If not 0, this forces all animations on this sprite to play at this delay
  in 1000th's of a second between frames.  This lets you 'turbo charge'
  monsters, so the walking anim will match the extra fast speed.

int sp_base_walk(int sprite, int value (-1 to not change));

  Should be 10, 20, 30 ect.  This means the sprite will play animations from that
  base - if they walk left (direction 4, look at your numpad) it would play seq
  104 if the base was 100.  All bases work this way.  If diagonals don't exist it will
  use non diagonals for diagonal movement automatically, and vice versa.

int sp_base_attack(int sprite, int value (-1 to not change));

  Like base_walk but determines the sequences to use to attack.

void sp_attack_wait(int sprite, int value);

  Time in 1000ths of a seconds that you would like this monster to
  forget about it's target.  

int sp_target(int sprite, int value (-1 to not change));

  This is who you want this sprite to attack.  Unless a base_attack is
  set, it isn't going to do much but follow the guy around.

int sp_brain(int sprite, int value (-1 to not change));

  lets you set the brain of a sprite.  This tells the sprite how to behave, very
  important!
   brain 1:  Human brain.
   brain 2: not used
   brain 3: Duck brain.
   brain 4: Pig brain.
   brain 5: When seq is done, kills but leaves last frame drawn to the
            background
   brain 6: Repeat brain - does the active SEQ over and over.
   brain 7: Same as brain 5 but does not draw last frame to the background.
   brain 9: Person/monster (No diagonals)
   brain 10: Person/monster (Only diagonals)
   brain 11: Missile brain - repeats SEQ.
   brain 12: Will shrink/grow to match size in sp_brain_parm, then die
   brain 13: Mouse brain (for intro, the pointer) (do set_keep_mouse to use
             inside game as well)
   brain 14: Button brain.  (intro buttons, can be used in game as well)
   brain 15: Shadow brain.  Shadows for fireballs ect.
   brain 16: Smart People brain.  They can walk around, stop and look.
   brain 17: Missile brain, but kills itself when SEQ is done.

   Info on brain 11:

   Check item-fb.c for an example of the missile brain used.  Brain_parm
   and brain_parm2 can be used to set certain sprites this missile cannot
   damage. (for instance, the guy who shot it and the missiles shadow
   effect, if used)

   Info on brain 15:

   To use this brain, use sp_brain_parm() to set a sprite to mimic.  It
   will copy this sprites location until the sprite is killed, at which
   point it will kill itself.

int sp_brain_parm(int sprite, int value (-1 to not change));

  Some brains use this parm for special stuff.  Like the shrink/grow
  brain. (used when they pick up an item)

int sp_reverse(int sprite, int value (-1 to not change));

  If 1, this sprite will play the SEQ's in reverse.  Usefull is some
  situations, such as the opening menu we're we play the arrow animations
  both ways.

void sp_touch_damage(int sprite, int value);

  If > 0 then this sprite will cause this much damage to dink if touched.
  If -1, this sprite will run touch() in the sprites script if touched.
  (used for picking up hearts by touch for instance)

void sp_distance(int sprite, int value);

  Set's the range of the guys weapon.  At least for Dink.. with monsters
  with touch damage enabled it might do something else, can't remember.

void sp_attack_hit_sound(int sprite, int value);

  Sets the sound effect # to play when this sprite attacks and hits
  something.  If 0, defaults to 9, punch. (used in item-sw1.c)

void sp_attack_hit_sound_speed(int sprite, int value);

  Sample speed to play the above sound.  Usually at 22050.  Forget to 
  set this and the sound won't play...

  If > 0 then this sprite will cause this much damage to dink if touched.
  If -1, this sprite will run touch() in the sprites script if touched.
  (used for picking up hearts by touch for instance)


void sp_hard(int sprite, int value);

  If 0 then this sprites hardbox will be added to the hardmap.  If 1, it
  will not be.  Moving sprites should be 1.
  a call to draw_hard_map() must be made to implement any changes.
  (or draw_hard_sprite)

int sp_active(int sprite, int value (-1 to not change));

  1 if sprite is active, 0 if not active.  You can kill a sprite using
  this, or check to see if one is active.

int sp_move_nohard(int sprite, int value (-1 to not change));

  1 means this sprite will not be stopped by any hard obstacles.

int sp_flying(int sprite, int value (-1 to not change));

  1 means this sprite can move over 'low hardness' like water.

int sp_script(int sprite, char script_file_name);

  this kills any other script assigned to sprite, and attaches this new
  one.  Do *NOT* include the .C extension.  It IMMEDIATLY runs this
  scripts main() before continueing the current script.

int sp_que(int sprite, int value (-1 to not change));

  This is the virtical depth que for the sprite.  In most cases, it is
  set to 0, which means use the Y cords of the depth dot for this.  Is some
  cases, (say a cloud) you want to override this.  Change it to 1000 and it
  will be 'on top' of everything.

int sp_noclip(int sprite, int value (-1 to not change));

  if 1, this sprite will not be clipped in the normal game window.  (if you
  really want a sprite on the status bar, this is the way to do it..)

int sp_nodraw(int sprite, int value (-1 to not change));

  The sprite will behave like normal except for one small thing... you can't
  see it. (change to 1 to activate this)

int sp_nocontrol(int sprite, int value (-1 to not change));

  if 1, this sprite cannot control himself until the current sequence he is
  on is over.  This is used for dink's weapon scripts only right now.

int sp_range(int sprite, int value (-1 to not change));

  For use with missiles - increases the 'checking range'.  Put 600 and
  it will hit everything on the screen, always.


                   -= PART 3 - A CHANGING WORLD =-

We want the world to remember changes.  For instance, if Dink burns a
tree down, traveled to the other end of the world and then game back, the
tree should still be burned.  The same goes for taking items, breaking
stuff and anything else you can think off.

How do we make the game remember?  Yes, we could assign &vars to everything
and have scripts kill off what has been taken and change stuff, but this
would become very tedious.. so we only do that in special spots where
greater control is required.  But for little stuff, like keeping hearts
we picked up from reappearing we use another system.

Screen commands issued from the players data file. (SCIFTPDF for short, ha)

The players save game file is capable of storing one parm, a seq and frame
for EVERY editor sprite in the entire game.  The parm tells the draw_map
command to override what the map data says and do it different.  For
example, we have the barrels script do this:

  int &hold = sp_editor_num(&current_sprite);

  if (&hold != 0)
    {
       this was placed by the editor, lets make the barrel stay flat
     editor_type(&current_sprite, 3); 
     editor_seq(&current_sprite, 173);
     editor_frame(&current_sprite, 4);
       type means show this seq/frame combo as background in the future
    }

Editor_type can be set to the following:
0 - no change
1 - kill sprite completely
2 - draw pic from enclosed seq/frame data as a sprite WITHOUT hardness
3 - draw pic from enclosed seq/frame data as a BACKGROUND object WITH
    hardness (can't walk behind)
4 - draw pic from enclosed seq/frame data as a sprite WITH hardness
5 - draw pic from enclosed seq/frame data as a BACKGROUND object WITHOUT
    hardness (can't walk behind)
6 - kill sprite, but let him come back after 5 minutes
7 - kill sprite, but let him come back after 3 minutes
8 - kill sprite, but let him come back after 1 minute

Seq and frame store the sequence and frame of the new sprite to be displayed,
for instance, a burned tree.  The associated hardbox will be taken from
the sprites info if 4 or 5 is used.

You may wish to call a draw_hard_sprite() afterward if hardness has been
changed.

                   -= PART 4 - HOW ITEMS WORK =-

With DinkC, you can make unlimited items.  The only limit is Dink can only
hold 16 in his inventory at one time.. this isn't because I was a lazy
programmer (for once) but I believe having to manage your items and not
being able to stock pile too many healing potions will actually make
this a better game.

Everything here also applies to magic.  The difference between magic
and items/weapons is you only have 8 magic slots, and a different button
is used to activate magic.  (so, 24 items at once basically)

This means adding a new weapon that shoots an animation of your face
across the screen with it's own sound effect is very easy to do!

You can COMPLETELY control the behavior of any item.  First, you need
to give the player the item.

int add_item(char scriptname[8], int seq, int frame);
int add_magic(char scriptname[8], int seq, int frame);

The rest is handled by the items script itself.  The best way to understand
how it works is to look at item-fst.c.. but I'll explain anyway.

The sequence and frame are the picture of the item.  As soon as this is
called, 'void pickup( void )' is called from the script.  If simple having
this item in your inventory does something magical, this is the time to
do it.

When they click on it to arm it, the script is loaded into memory and
two procedures from it are called, first it looks for 'void arm( void )'
and runs this.  This is where you would add 8 to the strength or makes
or increased Dink's size by 50 or whatever.

Second it looks for 'void armmovie( void )' - any special thing like Dink
saying "wow, I just armed so and so" is said here.  This is ONLY called
if the player arms the weapon.

When the armour is DISARMED it runs 'void disarm( void )' from the
items script.  Use a kill_this_task at the end of it.

Here are some other calls it will calls it will check for.  If any of these
don't exist, it assumed you don't need that function and doesn't sweat it.

void use( void ) - when the button is pressed, this is called.  
void holdingdrop( void ) - run when dropped and item was armed at the time.
                           (drop will be run also)
void drop( void ) - when item is 'dropped' this is called. 

void pickup( void ) - when item is 'picked up' this is called.

Use a kill_this_task at the end of drop() and pickup() also.

Now, there are times when you may need other commands dealing with
items - so here they are.

int free_items();

   Returns how many free item spots remain.

int free_magic();

   Returns how many free magic spots remain.

int count_item(char name_of_item_script)

  Returns how many items with this script name they have.

int count_magic(char name_of_item_script)

  Returns how many items with this script name they have.

void kill_cur_item( void )

  Kills the currently armed item.  Run's it's disarm script, then drop
  script.

void kill_cur_magic( void )

  Same as above but for killing the current magic equipped.

void kill_this_item( char name_of_item_script )

  Kills first instance of this item by name of script.  If it's currently
  armed, it will run it's disarm and drop, otherwise it will just run the drop
  script and remove it.

void kill_this_magic( char name_of_item_script )

  Same as above but applies to the magic slots.

int compare_weapon( char name_of_item_script)

   Example:  compare_weapon("ITEM-B1"); would return 1 if the armed item's
             script was item-b1.  Used in s3-gobg.c.

int compare_magic( char name_of_item_script)

   Works like above.

               -= PART 5 - HOW THE CHOICE STATEMENT WORKS =-

This is the dialog box that we pop up so often.  Everything from the load
game menu to talking with NPC's is done with this.  It's pretty simple
to use, here is an example:

choice_start()
   "Yes"
   "No"
choice_end()

That is all!  The result will be put into &result.  So:

if (&result == 1)
  {
  //said yes!
  }

if (&result == 2)
  {
  //said no!
  }

Now, lets say we want SOME options to show up, because we are too lazy
to make seperate procedures for every possible combination.  Very easy:

choice_start()
(&life < &lifemax)   "I need to be healed, I'm hurt"
(&life >= &lifemax)  "I'm just dandy"
   "Leave"
choice_end()

Only two options will show up, depending on the players life.  Option 2
will still return 2, even if only option 1 is listed, so you can have your IF
statement check for 1 and 2, and just the chosen one will be activated.

Note:  These commands can be stacked:

(&love != 1) (&life == 5)   "Love isn't 1, and you have five life!"

The choice statement can handle up to TWENTY choices.  If this takes
up more than one screen, it will automatically handly scrolling to the
next screen when it needs to.

We could stop here, but there are a few more things:

You can put special commands at the top of the choice statement to change
how it looks.

choice_start()
        set_y 240
        set_title_color 15
        title_start();
Would you like to rock steady?
        title_end();

   "Yes"
   "No"
choice_end()

Ok, set_y sets the y cord that the choices will begin listing.
title_start() lets you specify a 'title' for the top.

set_title_color (optional) lets you specify what color the title is.
(you cannot change the color of the options themselves!)

The colors are the same as the ANSI char set if you know about BBSing...1
through 15.


                  -= PART 6 - ADVANCED TECHNIQUES =-

The way scripts can call other scripts, who they are connected to and
when they die is kind of like time travel - lots of theory and really
hard to understand.

Attaching a script to an item causes it to be run when the screen is
drawn - this is fine and dandy.  There is ANOTHER kind of script that
can be run BEFORE the screen is drawn, this is the script assocated with
the MAP SCREEN.  (done by pressing B in the map editor)

This script doesn't have the luxury of knowing certain info, like using
the Sp() command will always return 0, because no sprites have been drawn
yet.  However, it's a powerfull tool when used correctly, you can change
vision statements and set music BEFORE the screen is entered.

(you can do a wait(1) in a 'screen script', and it will draw all the sprites
and return, so you can manipulate the rest of the data)

                  -= PART 7 - KNOWN LIMITATIONS =-

Ok - try as we might, we can't be diluted into thinking DinkC is really
C.  It is an interpretated scripting language written to understand C
syntax.  To a DEGREE - here are the rules, pay attention or be prepared
to be frustrated!

*** Only do one command per line. ***

instead of
freeze(1); say("Hi",1); unfreeze(1);

do it like this:

freeze(1);
say("Hi",1);
unfreeze(1);

This does not waste memory.

DinkC PARTLY supports multiple commands per line, but later I decided not
to pursue it.. was eating up too much processer time with my slow searches..

**** COOL MATH NOT SUPPORTED ****

DinkC can only understand ONE math question at a time.

Dink understands:

==, !=, +=, -+, >, <, >=, <=,/,*

Instead of:  if ( (5 + 3) > 2) ....
you must do something else such as:

int &crap = 5;
&crap += 3;
if (&crap > 2)
 {
 //blahblah
 }
You also cannot do:
&crap++; or &crap--.  Use &crap += 1; instead.

You can do:
Pap < Seth.  Grace Jones < Zorin.  Bond > OnnaTop.  K6 < p200mmx.

*** BE CAREFULL WITH YOUR COMMENTS ***

Comment like this:

  int &crap = 5;
  //sets this to 5, duh

Not like this:
  
  int &crap = 5;    //sets this to 5, duh

This could cause a problem, some commands use 'does this char exist in this
string?' procedures.  Don't do it.

If a script command is not working, turn debug mode on and study the
output - most likely you can figure what is going on from that.  If that
fails, try scripting it another way, or studying source that I have done
in the dink/story dir.

Note:  The COMPLETE script source is zipped up in the DEVELOP dir. (SOURCE.ZIP)
