home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC Gamer 4.2
/
1998-08_Disc_4.2.iso
/
dinkdemo
/
addons
/
dink104.exe
/
DINKC.TXT
< prev
next >
Wrap
Text File
|
1998-03-30
|
40KB
|
1,108 lines
DINKC reference.
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. (¤t_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.", ¤t_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 say(char string[200], int sprite);
Says the string above the sprite you wish. Use ¤t_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( void );
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 ¤t_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.
void 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.
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.
playmidi(char midiname[12]);
stopmidi(char midiname[12]);
stopcd( void );
****** 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(¤t_sprite, 1000); would cause ¤t_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.
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(¤t_sprite);
if (&hold != 0)
{
this was placed by the editor, lets make the barrel stay flat
editor_type(¤t_sprite, 3);
editor_seq(¤t_sprite, 173);
editor_frame(¤t_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)