That's weird as the zip at my end has this html file...
Creating
Enemy Sprites a basic tutorial
style='font-size:10.0pt;mso-bidi-font-size:12.0pt'>Written by Simon Klaebe June
2002 ver 1.1
Intro: This tutorial is an overview of what I do when creating
new enemy sprites. Some of this is covered in other documents such as the
Revised DinkC.txt and the Advanced Dink.ini Editing files, but hopefully
the beginner and maybe the intermediate DMOD author will find this document
useful. As an example Im using the scripts and graphics from my Eyeball
Junior sprite.
What makes up an Enemy Sprite
- The
script and its DinkC commands (used to control the sprite)
- The
dink.ini lines (the dink.ini file loads graphics and sets depth dots etc)
- The
graphics (used to display the sprite)
- The
sounds (used to make the sprite alive/react)
Here is a copy of the script of Eyeball Junior (Not exactly
the same as I have in my download file, but this is an improvement):
//eyeball
junior
void
main( void )
{
style='mso-tab-count:1'> preload_seq(861);
style='mso-tab-count:1'> preload_seq(863);
style='mso-tab-count:1'> preload_seq(865);
style='mso-tab-count:1'> preload_seq(867);
style='mso-tab-count:1'> preload_seq(869);
style='mso-tab-count:1'> preload_seq(872);
style='mso-tab-count:1'> preload_seq(874);
style='mso-tab-count:1'> preload_seq(876);
style='mso-tab-count:1'> preload_seq(878);
style='mso-tab-count:1'> sp_brain(&current_sprite, 9);
style='mso-tab-count:1'> sp_speed(&current_sprite, 5);
style='mso-tab-count:1'> sp_base_walk(&current_sprite, 860);
style='mso-tab-count:1'> sp_base_death(&current_sprite, -1);
style='mso-tab-count:1'> sp_base_attack(&current_sprite, 870);
style='mso-tab-count:1'> sp_range(&current_sprite, 45);
style='mso-tab-count:1'> sp_distance(&current_sprite, 35);
style='mso-tab-count:1'> sp_exp(&current_sprite, 80);
style='mso-tab-count:1'> sp_hitpoints(&current_sprite, 20);
style='mso-tab-count:1'> sp_defense(&current_sprite, 2);
style='mso-tab-count:1'> sp_touch_damage(&current_sprite, 2);
style='mso-tab-count:1'> sp_strength(&current_sprite, 15);
style='mso-tab-count:1'> int &mcounter;
}
void
hit( void )
{
style='mso-tab-count:1'> sp_target(&current_sprite,
&enemy_sprite);
style='mso-tab-count:1'> playsound(52, 10050, 2000,
&current_sprite, 0);
}
void
die( void )
{
style='mso-tab-count:1'>
style='mso-tab-count:1'> int &hold =
sp_editor_num(&current_sprite);
style='mso-tab-count:1'> if (&hold != 0)
style='mso-tab-count:2'> editor_type(&hold, 6);
style='mso-tab-count:1'>
style='mso-tab-count:1'> &save_x = sp_x(&current_sprite,
-1);
style='mso-tab-count:1'> &save_y = sp_y(&current_sprite,
-1);
style='mso-tab-count:1'>
style='mso-tab-count:1'> external("emake",
"xlarge");
}
void
attack( void )
{
style='mso-tab-count:1'> playsound(51, 11000, 0,
&current_sprite, 0);
style='mso-tab-count:1'> &mcounter = random(500,0);
style='mso-tab-count:1'> sp_attack_wait(&current_sprite,
&mcounter);
}
And here are the relevant dink.ini lines
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>//eyeball junior
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>load_sequence
graphics\eye2\eyej1- 861 75 28 61 -22 -18 26 9
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>load_sequence
graphics\eye2\eyej3- 863 75 46 65 -22 -17 21 11
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>load_sequence
graphics\eye2\eyej5- 865 75 46 63 -22 -18 24 9
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>load_sequence
graphics\eye2\eyej7- 867 75 23 59 -17 -16 23 12
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>load_sequence
graphics\eye2\eyej9- 869 75 44 56 -23 -16 19 12
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>load_sequence
graphics\eye2\eyej2- 872 75 26 61 -20 -16 20 9
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>load_sequence
graphics\eye2\eyej4- 874 75 50 54 -26 -21 26 9
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>load_sequence
graphics\eye2\eyej6- 876 75 65 56 -29 -17 17 11
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>load_sequence graphics\eye2\eyej8-
878 75 53 43 -20 -18 20 15
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>set_frame_special 872 5 1
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>set_frame_special 874 5 1
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>set_frame_special 876 5 1
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>set_frame_special 878 5 1
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:teal'>
Okay, lets run
through this script:
In the main() procedure you have nine preload_seq();
commands. These make sure that the walk, attack, and death graphics play
smoothly and dont hang while the Dink Engine loads them.
Then you have the commands that make the sprite move:
style='color:navy'>sp_brain(); and sp_speed();
Just a qick note about the brains brain 10 is not good for enemy sprites that
have an attack sequence basically a brain 10 enemy doesnt physically chase
or target another sprite. But it does run the attack() procedure calling it
again and again until the attacking sprite (usually Dink) has either died or left
the screen.
Ive got brain 9 (diagonal movement) and a speed of 5
(pretty fast). The higher the sp_speed setting the fast the sprite moves. There
is another command you can use to control the speed, which is sp_timing(); -
This apparently controls how often the brain is called. Ive used it to slow
down the playback of various graphics (usually brain 6) the higher the number
used in sp_timing the faster the sequence is played.
The next three lines:
style='color:navy'> sp_base_walk(&current_sprite,
860);
style='color:navy'> sp_base_death(&current_sprite,
-1);
style='color:navy'> sp_base_attack(&current_sprite,
870);
tell the Dink Engine which series of graphics, or sequences,
it must use in making the sprite walk, die and attack. They will only work if
you have corresponding lines in the dink.ini file that loads the graphics when
the game starts. Because the death graphics (see the sequence of bmps below)
are part of the same base sequence number (860) as the walking graphics I have
written sp_base_death(&current_sprite, -1); Or you could create a series of
directional death graphics and give it a new base sequence number:
i.e. base sequence 880 could include death sequences for
directions 2, 4, 6, 8
style='color:navy'> sp_base_death(&current_sprite,
880);
If you dont have a death graphic, then the default is an
exploding ball of blood.
Here is a sample of the graphics for Eyeball Junior:
![]()
src="./esprite11_files/image002.gif" v:shapes="_x0000_i1025">
Most of the standard Dink graphics have one bmp file for the
death of the sprite with the exception of Dink, and the ducks (sort of).
style="mso-spacerun: yes"> The Boncas and Dragons have 1 bmp file for
each direction, but this is not a sequence. There
is no reason why your enemy cant die gracefully on screen. Just make a
sequence and put it in as XX5 in the dink.ini lines (with the base_walk lines)
and use the sp_base_death(&current_sprite, -1) command. Both the eyeball
and the end boss in The Creeping Sands have death sequences, as does the new
dragon in my current DMOD Im working on. And if you really want to go to town
with killing off your new sprite, make graphics of him dying in four different
directions (2, 4, 6, 8) and give them dink.ini lines with a new sequence base
and the sprite will probably die in the correct direction it is facing NB I
havent tested this myself, but it seems logical.
Now the fun part setting the range and distance the new
ENEMY attacks at.
The command
style='color:teal'>sp_distance(&current_sprite, XX); does define
how close a sprite has to be to Dink before it attacks, and
style='mso-bidi-font-weight:normal'>sp_range(&current_sprite,
XX); defines the range of the attack, but only becomes active if you
have an sp_strength() value for your ENEMY.
As long as the value of sp_range() is greater than or equal
to sp_distance() the ENEMY will have a successful attack. If the two values are
equal then Dink can use the hit and run battle technique that every player
knows so well, but if you make the sp_range() value larger then it becomes
harder for Dink to evade the strike, especially if the sp_range () value is
greater than Dinks own.
There is another way of defining the attack of your ENEMY is
using the hardbox definition of the bmp set to actually hit Dink. Ill go into
this in more detail soon when I deal with the dink.ini lines. What is
interesting here is that if you do use this second method, then the
sp_touch_damage() value is used to determine the damage done to Dink.
The next two DinkC commands: sp_exp(&current_sprite, XX);
and sp_hitpoints(&current_sprite,
XX); - the first assigns the number of experience points the ENEMY
gives the player on dying, the second assigns the life value of the ENEMY, or
the number of points Dink has to take away for the sprite to die.
How many of these hit points that get taken away with each
attack by Dink is influenced by the next command: sp_defense(&current_sprite, XX);
The higher the sp_defense() setting the tougher the ENEMY is to kill. If the
sp_defense() value is higher than Dinks strength then Dink will at most do 1
point of damage when attacking, and sometimes not any.
Now for the ENEMYs attack: sp_touch_damge(&current_sprite, XX);
sp_strength(&current_sprite, XX); The first value determines how
strong the ENEMY is when it touches Dink how much damage this touching does
varies depending on Dinks own defense and a random element. Interestingly
enough even if Dinks defense value is higher than the sp_touch_damage() value
it is still possible for the ENEMY to do 1 point damage to Dink, every so
often. If you find this fact inconsistent you could write your own touch()
procedure for the ENEMY, and activate it by setting the sp_touch_damage() value
to 1 in the main procedure.
i.e.
void main (
void )
{
style='mso-tab-count:1'> sp_touch_damage(&current_sprite, -1);
style='mso-tab-count:1'>
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
}
void touch (
void )
{
style="mso-spacerun: yes"> int
&enemy_dam = 15;
style='mso-tab-count:1'> if (&enemy_dam >= &defense)
style='mso-tab-count:1'> {
style='mso-tab-count:2'> hurt(1, &enemy_dam);
style='mso-tab-count:1'> }
}
Or you could make the act of touching your enemy interesting
by changing Dink completely perhaps turning him to stone, or making him vomit
in which case add some more interesting code to the touch() procedure.
Now if your ENEMY has attack graphics then the sp_strength()
command determines how much damage it
does on the hit frame of the sequence, (see Revised DinkC.txt as to how
Ted thinks this damage value is calculated). This special frame is defined in
the dink.ini files. Eyeball Juniors hit frame is number 5, and I made him get
angry red for this. The necessary dink.ini line for attacking in the 2
direction is:
set_frame_special 872 5 1
872 is the sequence number, 5 is the 5th frame of the
sequence, and the value 1 tells the Dink Engine that this is the frame on
which the sprite hits. (NB I wonder if there are other values than 1 that the
Engine responds to it has possibilities). You need at least one
set_frame_special line for each direction the sprite attacks in.
Other things you can tinker with for the attack is the delay
the hit frame stays up on screen. Dink does this with the sword. It can lend a
sense of wait if you have, say an Orc like enemy with a large club and you want
that club to hit hard. The command is
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
color:#003300'>set_frame_delay 872 5 100
Now the 5th frame of the hit sequence is up 100
milliseconds instead of 75.
Back to the main procedure The last line -
style="mso-spacerun: yes"> int &mcounter;
style="mso-spacerun: yes"> - defines a variable that is used in the
attack procedure. This value delays how soon the ENEMY attacks Dink again after
the last attack.
Now for the hit procedure: sp_target(&current_sprite,
&enemy_sprite); tells the ENEMY to attack the sprite that just
hit it. If you dont want your ENEMY to fight other ENEMY sprites then just
change this to sp_target(&current_sprite, 1); as 1 is the sprite number
for Dink.
The next command playsound(52, 10050, 2000,
&current_sprite, 0); lets the ENEMY have a sound when it gets
hit. This could be a cry of pain, a squishy sound of being hit whatever takes
your fancy. The more unique you are here the more your ENEMY sprite stands out.
Now for the various values:
52 is the number of the sound file as defined in the
START.C file of your DMOD. If you havent defined number 52 with a line such
as:
load_sound(52, BSQUISH.WAV);
Then the DMOD will crash when you hit your sprite. The other
thing to remember when using your own new .wav files is make sure you fill in
the information area of the file I use Goldwave to do this. If your wav
file hasnt had this done the DMOD might well
crash. Not always, mind you, as Ive got some wavs that dont have this
and Dink Engine doesnt have a problem, but I remember when I created sounds
for the scorpions in SOB that it wasnt until I filled in the information
section of the new wavs that the game would actually get passed the loading
screen (i.e. finish the START.C script which is where all your new sounds are
loaded). [NB Thanks to Silencers file on sound for this.]
What is the information area? Its a list of properties
for the file. It lets the author of the file include such information as:
TITLE; AUTHOR; COPYRIGHT; and DESCRIPTION. In Goldwave just look for the
information submenu under the File menu. You dont need to do all of the
categories I believe you can get away with DESCRIPTION only. But Im in the
habit of putting them all in even if its just the word unknown copied and
pasted into each section.
Back to the
style='color:navy'>playsound(52, 10050, 2000, &current_sprite, 0);
10050 is the base value of the speed of the sound so if it is a 22050 kHz
sound you can set this value to 22050. The sound I used in Eyeball junior has a
speed of 11025kHz. I didnt actually use this value as I wanted to have the
sound vary when the ENEMY is hit this I think gives the ENEMY a more
life-like quality.
2000 is the random speed to add to the base speed.
Basically a number between 0 and 2000 is added to 10050 to determine the speed
of the sound. So by using these two values the sound will randomly play back
between 10050 and 12050 not much of a difference, but still subtle enough I
hope.
&current_sprite plays the sound in a 3D perspective
based on the ENEMYs position and Dinks position. That is it will be either
louder or softer, depending on how far Dink is from the ENEMY. If you set this
value to 0 the sound will play at its set level.
The 0 at the end of the playsound command tells the Dink
Engine to only play the sound once. If you set this to 1 the sound plays in a
loop. I never loop these sounds.
Other things you can do in the hit procedure:
If youve got a boss and you want to help the player know
you weak this guy is you could add these line:
style='mso-bidi-font-weight:normal'>int &dam =
sp_hitpoints(&current_sprite, -1);
style='mso-bidi-font-weight:normal'>say('3&dam,
&current_sprite);
This will put a blue value (because of the '3) of the
remaining hitpoints above the ENEMYs head after Dink hits it.
You can try also using various other commands such as
compare_weapon() to see if Dink has used a certain weapon on the ENEMY, but
this is not foolproof. Dink may have a sword armed but have used a spell to
attack. The other thing I came across with using compare_weapon() in the hit
procedure was a bug which crashed the game. I no longer do this on moving
sprites now, so I can only say try it at your own risk.
The die procedure:
Just the basics are here which are find out the editor
number of the sprite, and find out where the sprite was when it died. Knowing
the editor number lets you then determine if the ENEMY should die completely,
or come back within a certain amount of time and you use the editor_type()
command to do this. Here are the various types as given in the DinkC.txt file:
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>0 - no change
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>1 - kill sprite completely
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>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 WITHOUT hardness (can be walked on or behind)
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>4 - draw pic from enclosed seq/frame
data as a sprite WITH hardness
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>5 - draw pic from enclosed seq/frame
data as a BACKGROUND object WITH
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
hardness (can't walk behind)
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>6 - kill sprite, but let him come back
after 5 minutes
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>7 - kill sprite, but let him come back
after 3 minutes
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>8 - kill sprite, but let him come back
after 1 minute
The die procedure is also used to reward the player for
killing the sprite by creating some kind of goodie. &save_x and &save_y
are two global variables that let you save the position the monster died by the
commands:
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy'>&save_x =
sp_x(&current_sprite, -1);
style='mso-tab-count:1'> &save_y = sp_y(&current_sprite,
-1);
The next command tells the Engine to run the procedure
xlarge from the emake.c file
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy'>external("emake",
"xlarge");
This makes either a large red heart or some gold. If you
want your enemy to make something special such as a hellfire magic scroll
then youll have to write your own procedure inside the emake.c file. So first
copy the emake.c file from the source of the original game (Im assuming you
have that of course) into your story folder and then edit it away. Usually you
have to write another script that gets attached to the goodie sprite that the
emake.c file creates. Lets call the new procedure sfbscrol as sfb is short
for super-fire-ball.
So in emake.c I would write
void
sfbscrol( void )
{
style='mso-tab-count:1'> &save_y += 5;
style='mso-tab-count:1'> int &crap = create_sprite(&save_x,
&save_y, 0, 422, 2);
style='mso-tab-count:1'> sp_script(&crap, getsfb);
}
Then Id write a new script that gives Dink Hellfire magic
when he touches the scroll and save it as getsfb.c
//this
script gives Hellfire magic
void
main( void )
{
style='mso-tab-count:1'> sp_touch_damage(&current_sprite, -1);
}
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>void
talk(void)
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>{
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
style='mso-tab-count:1'> Say("This scroll has strange magic
words on it.", 1);
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>}
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>void
touch(void)
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>{
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
style='mso-tab-count:1'> //dink touched this sprite
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
style='mso-tab-count:1'> Playsound(10,22050,0,0,0);
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
style='mso-tab-count:1'> sp_brain_parm(&current_sprite, 10);
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
style='mso-tab-count:1'> sp_brain(&current_sprite, 12);
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
style='mso-tab-count:1'> sp_touch_damage(&current_sprite, 0);
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
style='mso-tab-count:1'> sp_timing(&current_sprite, 0);
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
style='mso-tab-count:1'> add_magic("item-sfb",437,2);
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
style='mso-tab-count:1'> say("I now have HELLFIRE
MAGIC!", 1);
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
style='mso-tab-count:1'> //kill this item so it doesn't show up
again for this player
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
style='mso-tab-count:1'> int &hold =
sp_editor_num(&current_sprite);
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
style='mso-tab-count:1'> if (&hold != 0)
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
style='mso-tab-count:2'> editor_type(&hold, 1);
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>
style='mso-tab-count:1'>
lang=EN-US style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:navy;mso-ansi-language:EN-US'>}
Other things the die procedure is used for is unlocking
screens, advancing the story by increasing a global variable, etc.
The last procedure: attack(): Here is where you can
influence how often your ENEMY attacks its foe, plus play a sound for the
attack. The first command is for a new
sound. Once again, what you choose here can either make your enemy real, or not.
And remember about making sure the sound is loaded in START.C file and is the
right type of wav file (see above). The best use Ive made for this is the whip
sound I put in for the scorpions in SOB and The Creeping Sands. Its just the
sound of the whip flying, but doesnt include the crack at the end.
What sound Dink makes when he gets hit defaults to sound
number 9 (check your START.C file) unless you use the following two commands:
style='color:teal'> sp_attack_hit_sound(&current_sprite,
XX):
style='color:teal'> sp_attack_hit_sound_speed(&current_sprite,
XXXXX);
So if you want Dink to groan in a special way when your
ENEMY hits him, create the sound, load it in START.C and then use these two
commands in the main procedure of your ENEMY script. The second command tells
the Dink Engine how fast to play the sound. The first command wont work
without the second.
Once again you could vary this sound by putting these
commands in the attack procedure and using a random variable to change the base
speed. I have yet to do this, but the theory is it should work.
I used these two commands (in the main procedure) to have
Dink react in a different way to being hit by the end boss farts in The
Creeping Sands.
The last two lines of the attack() procedure define how much
the ENEMY should wait before attacking again. The smaller the wait value
(&mcounter in this case) the quicker the monster attacks. I varied these
across the different types of scorpions in SOB. Yellow waited the longest, and
the Red would attack the quickest and thus was more deadly. (Cant remember
with the purple/black scorpions).
Now for the dink.ini lines without these you wont have
you pretty pictures.
The format is basically this:
load_sequence graphics/
style='color:navy'>folder/file- Seq No.
Frame Delay 6 Hard box co-ordinates
Your bmp files are in the folder
style='color:#003300'> inside the GRAPHICS folder, and should have a
name that follows the Dink convention of file-01.bmp,
file-02.bmp, file-03.bmp etc. That is four to five characters then a -, and
then incremental numbering starting at 01. The - is optional, but it kinda
makes the numbers stand out.
To make a sprite walk in 4 (or even 8 directions) you need
to designate an entire sequence base which are 10 numbers from 0 to 9 i.e.
870 to 879. (Im not sure what the 0 sequence is used for, and it may be
possible to pinch this for other graphics) Sequence
number 5 is for the death of the sprite. The rest are the eight
directions based on the numberpad of a standard keyboard.
Frame Delay is the time
in milliseconds between replacing one bmp with the next. The higher this value
the longer the wait between redrawing the bmp files. 75 to 100 is a good value
for monsters, but experiment to find what is best for you.
The next 6 numbers define the depth dot and hard box of the
ENEMY sprite.
![]()
src="./esprite11_files/image003.gif" v:shapes="_x0000_i1026">
I dont enter these numbers in manually but use DinkEdit to
do all the calculation for me. TAB cycles through 3 modes: a) adjusting the
position of the Depth Dot [the cross] and b) and c) the top, bottom and sides
of the hardbox. Use CRTL key with arrow keys to get fine adjustments all of
these instructions are at the bottom of the screen.
Where to put the depth dot? Depends on the design of your
enemy. If its something that is walking then place it on the feet, I go for
something just a few pixels about the bottom of the feet. But if it is flying
then put the depth dot beneath the graphic. You will have to do some
experimenting to find out what fits your particular graphic the best.
What I usually do when working these settings out is write
the load_sequenece lines in the
dink.ini file with just the Seq No and
style='color:#003300'> Frame Delay and then close the dink.ini file.
Then I open up the Dmod in DinkEdit, find my new graphics
and select the first bmp in the sequence, then press E to edit the depth dot
and hard box. Once Ive got the right position of the depth dot and the right
shape of the hard box by using the arrow keys, I save everything for that
graphics by pressing S and then TAB out to find the first sprite of the next
sequence and repeat for all new sequences. Then I exit out of the editor,
re-open the dink.ini file and there will be some new lines at the bottom of the
file - based on what I just did in DinkEdit, e.g.
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:maroon'>SET_SPRITE_INFO 872 1 26
61 -29 -29 28 35
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:maroon'>SET_SPRITE_INFO 874 1 50
54 -41 -68 26 9
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:maroon'>SET_SPRITE_INFO 876 1 65
56 -30 -36 35 28
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:maroon'>SET_SPRITE_INFO 878 1 53
43 -24 -42 61 15
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
Then
I just cut and paste the last six numbers from each line into the corresponding
load_sequence line (after the frame delay value). Then I go back and delete the
rest of the SET_SPRITE_INFO lines.
That
is, further back up the dink.ini file are the lines that you wouldve added to
make the graphics first appear in the editor:
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>load_sequence
graphics\eye2\eyej2- 872 75
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>load_sequence
graphics\eye2\eyej4- 874 75
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>load_sequence
graphics\eye2\eyej6- 876 75
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>load_sequence
graphics\eye2\eyej8- 878 75
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:teal'>
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>Grab the last six number of the first
SET_SPRITE_INFO line
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:maroon'>SET_SPRITE_INFO 872 1
style='background:lime'>26 61 -29 -29 28 35
Copy and paste them after the corresponding load_sequence
line for sequence number 872.
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman";color:#003300'>load_sequence
graphics\eye2\eyej2- 872 75 26 61 -29 -29 28 35
Do that for the other sequence directions and then delete
the SET_SPRITE_INFO lines as you can only have 600 of these and may need every
single one if you add a lot of graphics (I ran out of them in SOB). What this
means is that the hardbox and depth dot dont change over the seqence but you
may well want to change it. Lets say you create an enemy graphic that you want
to be vulnerable only at certain times. E.g. image the eyeball bouncing and stretching
to extremes from a nice sphere to a tall thin tube like creature. When it is
a tub creature it should be harder to hit. How to do this? Well you have to go
in and manually adjust the hardbox for all the relevant graphics not just the
first one.
Now you should have a great moving graphic so write your
script and watch the sprite go to work.
Making new Graphics:
This is a huge topic and its scope depends on the software
you have available to create the graphics. If you want true 3D graphics created
from scratch you need a 3D graphics program. Either one of the mainstream
modelling/animation programs such as 3D-Studio Max, Lightwave, Maya, SoftImage
style='font-family:Symbol;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman";mso-char-type:symbol;mso-symbol-font-family:Symbol'>
style='mso-char-type:symbol;mso-symbol-font-family:Symbol'> or
if you cant afford those try a find a copy of Blender (I use this, as it is
free if you want a copy search for Eylsium on the web as Blender is no longer
being made at this time [June 2002].)
I also find it useful to have a 2D graphics program to help
tidy up the renders from the 3D program, loading the Dink 256 colours (palette)
and for adding in the shadow. The more powerful the better: Photoshop or Paint
Shop Pro (I use this) are the first two that spring to mind.
Without getting into the specifics of graphics programs lets
talk about the number of graphics you need to create.
You will need (at least*) 1 set of 4 sequences for the walk
and another set of 4 sequences for the attack. Note, the attack is optional, as
you can have the ENEMY SPRITE just make contact with Dink/your hero to do
damage. Like Eyeball Sr.
*Then you have those special dudes the pillbugs and the
slimes. These only have 2 walking sequences, but thats because you cant tell
which end of the sprite is which nice bit of design work less number of
graphics to generate, but still a good monster. It looks the same walking
direction 1 as direction 9; and the same walking direction 3 as direction 7.
The walking graphics are defined by the command:
sp_base_walk(&current_sprite, XXX);
where XXX is the sequence number in the dink.ini file.
It can be either the diagonals [Brain 9]:
![]()
src="./esprite11_files/image004.gif" v:shapes="_x0000_i1027">
Or the vertical/horizontal [Brain 10]:
![]()
src="./esprite11_files/image005.gif" v:shapes="_x0000_i1028">
The brain of your ENEMY is usually defined in the main()
procedure of the enemys script:
sp_brain(&current_sprite, X);
This command supposedly dictates the way the creature walks
at least thats what the DinkC.txt
file says, brain 9 is diagonal movement, brain 10 is vertical/horizontal. Be
careful with the brain 10 monster (see the beginning of this document for notes
about it not targeting the sprite that hits it). It seems brain 10 is best for
monsters that cast magic, or shoot missiles. Look at the original games dragon
scripts to see how Seth used Brain 10.
Actually what seems to define the direction the sprite moves
more than the sp_brain command are the graphic seqences available in the sp_base_walk
base sequence. There is no reason why you cant have a brain 9 sprite and have
graphics for all eight directions defined. I did that for the end boss in The
Creeping Sands what I found was that he mainly used the even numbered
graphic sequences to chase after Dink and the odd numbered ones when he was
just ambling around (ie not pursuing a target).
The only sprite that has all eight directions that comes
with the original graphics is Dink, so if you want to make an Enemy based on
the Dink graphics make him brain 9 and try him out using his sequences base
70 for walking, or load up (put in) the sword carrying Dink into a spare base
slot (Ive chosen base 870 below) in your dink.ini file
load_sequence
graphics\dink\sword\walk\d-sw1- 871 43 64 69 -14 -10 14 10
load_sequence
graphics\dink\sword\walk\d-sw2- 872 43 35 70 -21 -10 19 10
load_sequence
graphics\dink\sword\walk\d-sw3- 873 43 28 69 -13 -9 13 9
load_sequence
graphics\dink\sword\walk\d-sw4- 874 43 66 75 -14 -12 20 12
load_sequence graphics\dink\sword\walk\d-sw6-
876 43 27 69 -23 -10 23 10
load_sequence
graphics\dink\sword\walk\d-sw7- 877 43 38 94 -20 -10 20 10
load_sequence
graphics\dink\sword\walk\d-sw8- 878 43 30 96 -15 -12 15 12
load_sequence
graphics\dink\sword\walk\d-sw9- 879 43 31 80 -13 -9 13 9
As for the death pinch the graphics from seqence number
436, by using the following commands
set_frame_frame 875 1 436 1
set_frame_frame 875 2 436 2
set_frame_frame 875 3 436 3
etc up until the last bmp file
set_frame_frame 875 16 436 16
Saves you having to upload the graphics that everyone
already has, and still just write sp_base_death(&current_sprite, -1); after
sp_base_walk(&current_sprite, 870);
Oh, and not to forget the attack: These graphics dont have
an active slot so you have to create on for them (They are first loaded in the
dink.ini file and then replaced by the hand punch graphics so when Dink arms
a sword there is code the in the item-sw1 (or sw2, or sw3) file that loads
the sword graphics in the punch seqence numbers.)
style='font-size:9.0pt;mso-bidi-font-size:10.0pt;font-family:"Courier New";
color:#003300'>load_sequence_now graphics\dink\sword\hit\d-sa2- 882 75 52 92
-23 -12 24 11
style='font-size:9.0pt;mso-bidi-font-size:10.0pt;font-family:"Courier New";
color:#003300'>load_sequence_now graphics\dink\sword\hit\d-sa4- 884 75 74 90
-23 -13 23 14
load_sequence_now
graphics\dink\sword\hit\d-sa6- 886 75 33 92 -18 -14 18 10
style='font-size:9.0pt;mso-bidi-font-size:10.0pt;font-family:"Courier New";
color:#003300'>load_sequence_now graphics\dink\sword\hit\d-sa8- 888 75 46 109
-17 -16 17 10
style='font-size:10.0pt;font-family:"Courier New"'>
style='font-size:10.0pt;font-family:"Courier New";color:#003300'>//set which
frame can 'hit'
style='font-size:10.0pt;font-family:"Courier New";color:#003300'>set_frame_special
882 3 1
style='font-size:10.0pt;font-family:"Courier New";color:#003300'>set_frame_special
884 3 1
style='font-size:10.0pt;font-family:"Courier New";color:#003300'>set_frame_special
886 3 1
style='font-size:10.0pt;font-family:"Courier New";color:#003300'>set_frame_special
888 3 1
style='font-size:10.0pt;font-family:"Courier New";color:#003300'>
style='font-size:10.0pt;font-family:"Courier New";color:#003300'>
style='font-size:10.0pt;font-family:"Courier New";color:#003300'>//make it
delay on the third sprite for longer than 75
style='font-size:10.0pt;font-family:"Courier New";color:#003300'>set_frame_delay
882 2 100
style='font-size:10.0pt;font-family:"Courier New";color:#003300'>set_frame_delay
884 2 100
style='font-size:10.0pt;font-family:"Courier New";color:#003300'>set_frame_delay
886 2 100
style='font-size:10.0pt;font-family:"Courier New";color:#003300'>set_frame_delay
888 2 100
style='font-size:10.0pt;font-family:"Courier New"'>
Tips in creating good
ENEMY graphics:
1.
Add a shadow. This helps the sprite appear on the ground (or
not if the shadow is not connected to the sprite and that is what you want). I
render my sprites out as targa files with a solid black shadow. I then use the
paint fill tool in Paint Shop Pro to replace the black with the dink shadow
which is just alternating black and white pixels I recommend getting the
Simple Graphics Tutorial by mimifish it includes a useful bmp of these
pixels. The standard Dink shadow seems to be a South-West direction, so I try
and be consistent with that.
2.
Remove the light coloured pixels that surround the image.
Sometimes your rendering can cause these, especially when you load up the dink
palette of 256 colours. I use the colour replace tool in Paint Shop Pro and go
thru removing the offending edges painstaking and boring, but it really does
improve the look of the graphics. I find that the offending colours are as
follows: Palette Index 1 (a very light pink), Index 3 (a very light yellow),
Index 22 (a light grey), Index 59 (a light blue).
3.
Size Dont make the graphics too big. As Dink is only about
75 pixels high standing (100 including his shadow). Anything bigger than 250 I
find starts to look out of proportion. The other thing is graphics that are
box/square like in shape are good, as are those that are tall but sprites
that are long i.e. snakes, lizards, etc can start to look funny when they
spin around and change direction.
4.
Number of graphics for a walk cycle. I find 8 to 12 a good
number. Dinks walk is defined by 8 bmps, and most of the peasants/enemies have
10.
5.
Number of graphics for attack cycle. This can vary 5 seems
to be the Dink standard. I used 7 in eyeball junior as I had him bounce up and
lash out with his tail, hit on frame 5 and then use frames 6 and 7 to come back
down. Just remember the more attack graphics the longer the attack takes to
execute/finish.
6.
Number of death graphics at least 16, give you character
room to die.
Magic Attacks:
What about wizards and Sorcerors attacks? Here is where a
brain 10 for an enemy is good. Just put your attacking code into the attack
procedure
Well you can do magic attacks in a couple of ways. Do
something like the seth.c script in the original Dink, in which the Seth
sprite, changed to a glowing red head as he was about to launch his Magic. Or
you could create directional magic graphics and just make the sp_distance()
value 600 this might work, but Ive never tried it. I use a modified version
of the seth.c script in which I directly change the ENEMYs graphics in the
attack procedure.
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
sp_pseq(&current_sprite, 927);
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
sp_pframe(&current_sprite, 2);
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
wait(200);
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
sp_pseq(&current_sprite, 927);
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
sp_pframe(&current_sprite, 1);
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
wait(200);
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
sp_pseq(&current_sprite, 927);
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
sp_pframe(&current_sprite, 2);
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
wait(200);
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
sp_pseq(&current_sprite, 927);
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
sp_pframe(&current_sprite, 1);
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
wait(200);
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
say("'0<Casts evil spell>", &current_sprite);
style='font-size:10.0pt;mso-bidi-font-size:12.0pt;font-family:"Courier New";
mso-bidi-font-family:"Times New Roman"'>
//start the five bombs off
This is an excerpt from the boss of SOB, which is just like
Seth, in that he changes between normal face and a red face. Then casts his
magic. That is show frame 2 of a graphic sequence, wait 200 milliseconds, then
show frame 1 of the sequence
I have been experimenting with using whole sequences instead
of sp_pseq() and sp_pframe() for a Dragon as part of my next epic DMOD
Pilgrims Quest and then combining this magic attack with a standard
base_attack() as well. It isnt working properly yet so I wont include my
faulty code. But basically instead of loading a different series of frames I
set a new sequence for the sprite using the sp_seq(); command and determine
which direction (I have a seq for each of the four 1, 3, 7, 9 directions) to
face based on calculating Dinks position relative to the Dragon each time it
attacks.
At the moment the Dragon spends most of its time using
magic to attack instead of the fire spitting and I want to get a more even
balance. I figure it just means more adjusting between attack_wait() and
sp_timing() commands. But here are some of the graphics for the Dragon to show
you want Im trying to do:
![]()
src="./esprite11_files/image007.gif" v:shapes="_x0000_i1029">
And that is it. Hope this document has been interesting if
not helpful. Email me at simonk@australiamail.com
if you have any questions or comments.
Thanks to Manauser for proof reading and providing feedback
on this tutorial.
Thanks to Mimifish and Kyle for providing feedback on this
tutorial.