Shooting yourself in the foot, dealing with Bullets

6-9-2018

In order for our poor little bubble to defend itself against the onslaught of enemies, we need to be able to shoot. The FASE engine can handle up to 8 bullets at a time, but we don’t need that many. 8 bullets for 4 enemies seems overly excessive. How about 1 bullet at a time. This way we can defend our bubble, yet still provide a challenge.

We will need to think about a few things first and accommodate them in programming. This article may run a bit longer as there are a number of settings that we will explore. I have for the most part gutted the bullet routines from the sample FASE game and the started reintroducing them back in. The bullets gave me some issues, not that it’s all that difficult, I just had some difficulty wrapping my head around them.

So here is our list of tasks that we need to address.

Making sure to limit to one bullet at a time, which includes, adjust our loop for 1 bullet and adjusting the FASE engine for 1 bullet.

Making sure that you cannot fire another bullet until the first one disappears, which include, making the bullet disappear when it hits an obstacle or the side of the screen and making the bullet disappear when it hits an enemy.

Finally, making the enemy disappear when it is hit by a bullet.

It does seem like a long list, but it’s not too bad.

First, let’s open config.def to adjust the FASE engine to 1 bullet.

tmode   3
smooth  1
clipup  2
clipdn  2
cliphr  1
safevr  1
safehr  1
offsex  1
offsey  3
notabl  1
bullet  1
bulmax  1
sprmax  5

The bullet setting (in blue), is 1 to activate bullets, 0 is to deactivate the bullets, here we set to 1.
The bulmax setting is the maximum number of bullets, here we set to 1.

Now let’s open up the variables.h and near the variables num_bullets, let’s add a new variable singleBullet.

char num_bullets;
char k;
unsigned char singleBullet;

If these 3 are not included, well we will need to add them.

Let’s now open main.c, we will need to initialize our variables.

//initialize variables
num_bullets = 0;
singleBullet = 1; //true

Our next section in main.c initializes the bullet structure (typedef struct).

for ( i = 0; i < 4; i++ )
{
    bullets[i].y = 255;
}

However, this is a bit of overkill for a single bullet, let’s replace it with.

bullets[0].y = 255;

Our next section in main.c, we need not mess with, as it works just fine as written. Its found just below the game-over check. I’m included here for reference.

// movement of bullets
for ( i = 0; i < num_bullets; i++ )
{
    if( dirbul[i]&3 )
    {
        if( dirbul[i]&1 )
        {
            if( bullets[i].x  4 )
                bullets[i].x -= 4;
            else
                remove_bullet(i);
        }
    }
			
    if( dirbul[i]&12 )
    {
        if( dirbul[i]&4 )
        {
            if( bullets[i].y4 )
                bullets[i].y-= 4;
            else
                remove_bullet(i);
        }
    }
}

The final thing we are going to do in main.c is adding a call to a function: bulletHits(); This is where we are going to make a few things happen to take care of bullet collision. This is placed in our enemy sprite loop. More on this in a bit.

// movement of enemy sprites
for ( i = 1; i < 5; i++ )
{
    bulletHits();
……
}

Now, let’s open up movement.h, here I moved a function that was in the original listing, this part was just a move function. The function is actually an overkill for a single bullet, but I’m going to leave it alone for now, it works well and really causes no harm for a single bullet. So I’m showing it for reference only.

void remove_bullet(char k)
{
    //char k;
    //k = *k;
    if( num_bullets )
    {
        num_bullets --;
        while ( k < num_bullets )
        {
            dirbul[k] = dirbul[k + 1],
            bullets[k].x = bullets[k + 1].x;
            bullets[k].y = bullets[++k].y;
        }
        bullets[k].y = 255;
    }
}

Still, while in movement.h, find the function moveMainCharacter() and find the IF fire command, it should look like this (look for the !spacepressed). Below shows our modification to the series of commands.

if( Input() & 0x10 && !spacepressed && singleBullet == 1 )
{ // Space (FIRE)
    singleBullet = 0;//cannot fire flag			
    bullets[num_bullets].x= sprites[0].x;
    bullets[num_bullets].y= sprites[0].y;
    i= Input() & 0x0f;
    dirbul[num_bullets]= i ? i : 1;
    num_bullets++;
}
spacepressed= Input() & 0x10;

Notice how I’ve got the singleBullet variable snuck in there. Here’s how this works. When the game is initialized, the singleBullet is set to 1, what I am using for true. IF you press space and IF singleBullet is true, then execute. The first thing we do is set it to false, so you cannot press fire twice in a row.

This happens with.

singleBullet = 0;//cannot fire flag

Next, our starting point of the bullet is set with:

bullets[num_bullets].x= sprites[0].x;
bullets[num_bullets].y= sprites[0].y;

This makes the starting point of the bullet the X and Y of the player.

The next command:

dirbul[num_bullets]= i ? i : 1;

I’m not quite certain, what it does specifically, but I’m leaving it alone since it does seem to count the number of bullets on screen at once. It causes no harm to leave it alone, and that’s what I’m doing for now.

Finally, the variable:

num_bullets++;

Increases the number of bullets, since it doesn’t seem to cause any harm, I’m going to leave it alone.

So, this all happens when you press the space-bar. In another section of code, we will set the singleBullet variable to TRUE again, but only after the bullet hits something. So in effect, we have created a single fire mechanism that will only reload once you hit something. In our case that something can be an enemy sprite, an obstacle or the edge of the game screen.

So, remember earlier in our main.c, I said we would get to the bulletHits() function? We are going to find this in enemy.h.

Here I have moved part of a code block from main.c to here to reduce our original program size in main.c. I’ve also made some changes and additions to the function to help us with firing bullets.

void bulletHits()
{
    if( sprites[i].n < 128 )
    //does your bullet hit a baddie, 10 is an offset from center of baddie sprite
    for ( j= 0; j < num_bullets; j++ )
    {		
        if( abs(bullets[j].x-sprites[i].x) + abs(bullets[j].y-sprites[i].y) > 4;
        tmpy1 = bullets[j].y >> 4;
        center = tmpx1 + scrw * tmpy1;
		
        if (tiles[center] > 0)
        {
            remove_bullet(j);
            singleBullet = 1;//can now fire again
        }
		
        if (bullets[j].x > 232)
        {
            remove_bullet(j);
            singleBullet = 1;//can now fire again
        }
        if (bullets[j].x < 8)
        {
            remove_bullet(j);
            singleBullet = 1;//can now fire again
        }
        if (bullets[j].y  152)
        {
            remove_bullet(j);
            singleBullet = 1;//can now fire again
        }
    }
}

The first bit of code.

if( sprites[i].n < 128 )

Checks to make sure the sprite is visible.

Our next bit of code:

if( abs(bullets[j].x-sprites[i].x) + abs(bullets[j].y-sprites[i].y) < 10 )
{
    // go subroutine enemy death
    sprites[i].n += 128;//make sprite disappear 
    remove_bullet(j);
    singleBullet = 1;//can now fire again	
    ////////////////////		
    transportEnemy();
    ////////////////////
}

Checks to see if we hit an enemy. Notice that the IF is the basically the Pythagoras theorem abs (x-x1) + abs (y-y1). So if the bullet is within 10 pixels of the center, then we execute the code block.

When we enter the code block, the first thing we do is make the sprite disappear.

sprites[i].n += 128;//make sprite disappear

We then remove the bullet from the screen.

remove_bullet(j);

We set our ability to fire again to true.

singleBullet = 1;//can now fire again

And then finally, we move the SPRITE to a predetermined location.

transportEnemy();

More on that function a little later. For now, we have met one of our goals, make the bullet disappear when it hits an enemy and make the enemy disappear.

Back to our code block.

tmpx1 = bullets[j].x >> 4;
tmpy1 = bullets[j].y >> 4;
center = tmpx1 + scrw * tmpy1;

Here we are converting the bullets, from x,y (0-240 and 0 to 160 respectively) to see if a bullet is in a particular correct tile.

if (tiles[center] > 0)
{
    remove_bullet(j);
    singleBullet = 1;//can now fire again
}

If that particular tile has an obstacle in it, we are going to remove the bullet from the play screen by making the bullet disappear and we are going to allow the player to fire again. Here we have met another one of our goals, making a bullet disappear when it hits an obstacle.

Finally, we have this code block.

if (bullets[j].x > 232)
{
    remove_bullet(j);
    singleBullet = 1;//can now fire again
}
if (bullets[j].x < 8)
{
    remove_bullet(j);
    singleBullet = 1;//can now fire again
}
if (bullets[j].y  152)
{
    remove_bullet(j);
    singleBullet = 1;//can now fire again
}

This block checks to see if the bullets hit the edges of the screen. If they do then remove the bullet from the screen and allow the player to fire again. We have now met our goals to make the bullet disappear.

Now, let’s go back to that transportEnemy() function I had talked about a little earlier. We actually covered this in a prior article, but I want to post it again.

Here’s the function.

void transportEnemy()
{
    M_OUTP(0xfe, 5);//flashes to show that sprite has touched a barrier
    enemyX = 24;
    enemyY = 24;
			
    sprites[i].x = enemyX;
    sprites[i].y = enemyY;
	
    tilepaint(2, 2, 2, 2);
    FRAME;
    sprites[i].n -= 128;//make sprite reappear
	
    killed ++;
    //update the scoreboard
}

When we hit an enemy (or the enemy runs into a wall), we don’t destroy the data out of memory, we simply hide the sprite.

The sprite is hidden by adding a 128 to the sprite.n field.

So let’s delve into the code.

M_OUTP(0xfe, 5);

Here, I’m just making the border flash with CYAN when the enemy is transported, so there is another visual display when an enemy dies.

enemyX = 24;
enemyY = 24;
sprites[i].x = enemyX;
sprites[i].y = enemyY;

Here, we are choosing a spot for the enemy to be transported to. It’s important to transport the enemy to the center of a tile.

Tilepaint(2, 2, 2, 2);
FRAME;
sprites[i].n -= 128;//make sprite reappear

Here we are making the Sprite reappear. We paint the tile where the enemy is being transported to. FRAME updates the tile during the next frame sync, and finally, we make the SPRITE reappear again by subtracting 128 from the sprites.n field.

killed ++;

Finally, we update our score.

Whew, bullets do seem like a bit of work to get accomplished, but it is certainly worth it, we want our player to be able to defend the fragile bubble we are playing.

Next article, we are going to mess around with displaying the scoreboard as well as taking care of a couple of bugs found in the sample FASE game dealing with text display.

Until then, happy coding.

Advertisements

Author: andydansby

I'm a hobbyist coder working with the ZX Spectrum. Living in New York state near the Syracuse area. I grew up in Virgina. The first computer my parents bought for me was a Timex Sinclair 2068.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s