Enemies and AI
Now let's add an enemy, but firs we need a game design. I already have an idea for that.
You are a psycho killer and want to kill badly!
So we actually need to ad victim, not an enemy. Let's draw one
Frame 0:
Frame 1:
Now let's add variables for the victim.
BITMAP *buffer,*player_bmp[2],*victim_bmp[2];
volatile int tim;
int player_x,player_y,player_frame,player_frame_counter;
int victim_x,victim_y,victim_frame,victim_frame_counter;
And modify loading function to load it.
void load_stuffs()
{
player_bmp[0] = load_bmp("player.bmp",NULL); // load player bitmap
player_bmp[1] = load_bmp("player2.bmp",NULL); // bitmap second frame
player_x=80; // set initial position
player_y=60;
player_frame=0; // set current frame to 0
player_frame_counter=0;
victim_bmp[0] = load_bmp("victim.bmp",NULL); // load victim bitmap
victim_bmp[1] = load_bmp("victim2.bmp",NULL); // bitmap second frame
victim_x=240; // set initial position
victim_y=180;
victim_frame=0; // set current frame to 0
victim_frame_counter=0;
}
As you can see we modified the initial positions, so that they do not overlap.
Now let it display the victim as well as the player
void draw()
{
// draw player bitmap at its position
draw_sprite(buffer,victim_bmp[victim_frame],victim_x-(victim_bmp[victim_frame]->w/2),victim_y-(victim_bmp[victim_frame]->h/2));
draw_sprite(buffer,player_bmp[player_frame],player_x-(player_bmp[player_frame]->w/2),player_y-(player_bmp[player_frame]->h/2));
}
You should notice that the victim is drawn BEFORE the player, so that when they overlap, player will cover the victim.
Now let's add AI to this victim. We will need additional variables for that, add them where the global variables are.
int victim_xv,victim_yv,victim_step_counter;
xv and yv is a vector, the direction that our character will move. step counter will count after how many cycles to regenerate the vector. You should also set the step counter to 0 in the loading function
Now let's head to timer_proc and add the AI
void timer_proc()
{
if (key[KEY_UP] && player_y>0) {player_y--;player_frame_counter++;}
if (key[KEY_DOWN] && player_y<HEIGHT) {player_y++;player_frame_counter++;}
if (key[KEY_LEFT] && player_x>0) {player_x--;player_frame_counter++;}
if (key[KEY_RIGHT] && player_x<WIDTH) {player_x++;player_frame_counter++;}
player_frame=1 & (player_frame_counter>>4); //change the frame
victim_frame_counter++;
victim_frame=1 & (victim_frame_counter>>3); //change the frame
if (victim_step_counter)
{
victim_x+=victim_xv;
victim_y+=victim_yv;
victim_step_counter--;
} else {
victim_xv=3-(rand()%7);
victim_yv=3-(rand()%7);
victim_step_counter=20+(rand()%20);
}
}
The frame flipper for the victim is just a copy off player's one, but it will flip all the time.
Now the if-part.
if (victim_step_counter) means the same as
if (victim_step_counter!=0) (not equal 0). So if it's not equal 0, it will add vectors to the position values and decrease the counter. When the couunter reaches 0, it will randomize the values.
rand()%7 will return a random value from 0 to 6.
You can compile now and see that our victim can get away from being killed waaay too easy, because he can go past screen boundaries. Let's change that.
But instead of blocking it's way out of the screen, we will flip the vector values, so that he will bounce off the screen boundaries.
void timer_proc()
{
if (key[KEY_UP] && player_y>0) {player_y--;player_frame_counter++;}
if (key[KEY_DOWN] && player_y<HEIGHT) {player_y++;player_frame_counter++;}
if (key[KEY_LEFT] && player_x>0) {player_x--;player_frame_counter++;}
if (key[KEY_RIGHT] && player_x<WIDTH) {player_x++;player_frame_counter++;}
player_frame=1 & (player_frame_counter>>4); //change the frame
victim_frame_counter++;
victim_frame=1 & (victim_frame_counter>>3); //change the frame
if (victim_step_counter)
{
victim_x+=victim_xv;
victim_y+=victim_yv;
if (victim_x<0 || victim_x>WIDTH) {victim_xv*=-1;victim_step_counter+=10;} //this line
if (victim_y<0 || victim_y>HEIGHT) {victim_yv*=-1;victim_step_counter+=10;} //this line
victim_step_counter--;
} else {
victim_xv=3-(rand()%7);
victim_yv=3-(rand()%7);
victim_step_counter=20+(rand()%20);
}
}
We added two lines. They read as follows
'if victim is out the screen,make him go the opposite direction and inscrease step counter by 10'.
Why increase the step counter? because when we flip the direction he's going, he's is still out of the screen, but 'about' to go the opposite way. Now when this happens when counter reaches 0, he might get stuck in screen boundaries, so apart from setting his direction back to the screen, we have to make sure he actually goes there.
Ok, so now we have to be able to actually murder him. We will need a new function for that
int distance(int x1,int y1,int x2,int y2,int dist)
{
int xx,yy,d;
xx=x2-x1; // x distance
yy=y2-y1; // y distance
d=xx*xx+yy*yy;
if (d>(dist*dist)) return 0; else return 1;
}
Add this just before the first function, so that this is the first function. The function takes two points and a distance and calculate whether the point distance is closer or further than the given distance.
Now let's check it this work. We want the victim to bleed when caught. Let's make bleeding function then.
void bleed(BITMAP *bmp,int howmuch)
{
int c,x,y;
// it will make howmuch blood spots on the bmp bitmap
for (c=0;c<howmuch;c++)
{
x=rand()%bmp->w; // randomize point on bitmap
y=rand()%bmp->h;
// if pixel at x,y is not magic pink it will put a randomnly red dot there
if (getpixel(bmp,x,y)!=makecol(255,0,255)) putpixel(bmp,x,y,makecol(128+(rand()%127),0,0));
}
}
Put this function right after distance function.
It uses the for loop to make an amount of spots at random places on the bmp bitmap. It also checks not to draw on the transparent parts of the bitmap.
Now let's call it from
timer_proc.
Add this at the end of the
timer_proc function
if (distance(player_x,player_y,victim_x,victim_y,32))
{
bleed(victim_bmp[0],20);
bleed(victim_bmp[1],20);
}
When you compile you'll see that it actually works, and victim bleeds when near the killer.
Ok, now let's add some eye-candy. We desperadetly need background, let's steal one.
Let's make a variable for it.
BITMAP *buffer,*player_bmp[2],*victim_bmp[2],*bg;
And load it into the variable (in the loading function)
bg = load_bmp("street.bmp",NULL);
Now let's display it as well.
Add this to the beggining of
draw function or it will cover everything.
stretch_blit(bg,buffer,0,0,bg->w,bg->h,0,0,WIDTH,HEIGHT);
You can compile now and see that it works pretty well. BUT WAIT! Why the character is not bleeding onto the street? let's make some mess here.
We'll make a function that draws random red circles on the
bg bitmap.
void mess(BITMAP *bmp,int x,int y)
{
circlefill(bmp,x+(rand()%64),y+(rand()%64),4+(rand()%4),makecol(128+(rand()%127),0,0));
}
This will draw some circles nearby x and y on the bmp bitmap. Now let's this happen when we are near the victim, so change this is your timer_proc
if (distance(player_x,player_y,victim_x,victim_y,32))
{
bleed(victim_bmp[0],20);
bleed(victim_bmp[1],20);
}
To this:
if (distance(player_x,player_y,victim_x,victim_y,32))
{
bleed(victim_bmp[0],20);
bleed(victim_bmp[1],20);
mess(bg,victim_x-(victim_bmp[0]->w/2),victim_y-(victim_bmp[0]->h/2));
}
You can see that we're subtracting half of bitmap width from the position, this is because we also add this half of width when drawing it.
Now let's check that out! You should see it's working, but the circles look weird on the bg, let's change it to ellipses!
void mess(BITMAP *bmp,int x,int y)
{
int r;
r= 4+(rand()%4);
ellipsefill(bmp,x+(rand()%64),y+(rand()%64),r,r/2,makecol(128+(rand()%127),0,0));
}
You should notice that we precalculate the ellipse radius, this is because we want vertical radius to be half of the horizontal radius, instead of two random values.
That would conclude the tutorial.
PS. To change killer to rapist, you should writte your
mess and
bleed functions like this:
void bleed(BITMAP *bmp,int howmuch)
{
int c,x,y,col;
// it will make howmuch blood spots on the bmp bitmap
for (c=0;c<howmuch;c++)
{
col=192+(rand()%63);
x=rand()%bmp->w; // randomize point on bitmap
y=rand()%bmp->h;
// if pixel at x,y is not magic pink it will put a randomnly red dot there
if (getpixel(bmp,x,y)!=makecol(255,0,255)) putpixel(bmp,x,y,makecol(col,col,col));
}
}
void mess(BITMAP *bmp,int x,int y)
{
int r,col;
r= 4+(rand()%4);
col=192+(rand()%63);
ellipsefill(bmp,x+(rand()%64),y+(rand()%64),r,r/2,makecol(col,col,col));
}
MAK SOME ALLEGRO GAMS, NAO!!!