Spastic fantastic, Enemy movement problems part 3

3-17-2018
In my last article detailing the enemy movements, I laid out the ideas for this article, here we will try to smooth out our enemy movements using our new variables. We will also try to inject the enemy bias to travel in the direction of our player.

This article will be a bit code heavy but I will try my best to make the logic easy to understand.

Before we start coding, we will need to know some of the basic framework and logic that will help guide the enemy toward the player.

First, we are storing the enemy direction, enemy steps and a flag to indicate if the enemy is supposed to move toward the player or away from an obstacle. As I detailed in the last enemy movement article, we are storing those three variables in the variable EnemyF. EnemyF is a special variable that has an assignment per enemy just like enemyX, enemyY and enemyImage with one distinction, it has no direct effect on the enemy in the engine.

That makes the variable EnemyF a perfect container to use to tell the enemy how to move indirectly.

Going back to the function that we will be using, I am going to rewrite the entire thing, I am going to reuse a lot of code, so I’m not going to toss the code we already have. Rather, I am going to copy it to another document and reference the prior code.

The other thing that we must always keep in mind is that this function is called once per enemy, per pixel in an endless loop. That, of course, means we are moving the enemy one pixel at a time in the direction we choose.

The other thing to remember is that this code will take up a considerable amount of memory, which is understandable as moving the enemy is possibly one of the most important aspects of the game. What fun is a game if there is not some sense of danger.

In this case, the danger is that the enemy is trying to constantly chase you and kill the player. The barriers in the game prevent the enemies from coming straight at you. The enemy must try to navigate the barriers without having prior knowledge of how the barriers are laid out to save on speed and memory. Our prior attempts have a jerky enemy that cannot navigate their way out of a paper bag. Here we are going to put theory (laid out in the last article) to code.

First, we are going to have to have an outline of what we want to accomplish and then continue to expand on the outline before we commit any code.

Here’s our logic outline.

//called once per enemy and once per pixel
void enemyCollision(unsigned char playerX, unsigned char playerY, unsigned char enemyXX, unsigned char enemyYY, unsigned char enemyFF)
{
    // create variables

    // process enemyFF into 3 separate variables enemySteps enemyDirection enemyRetreate
    
    // initialize variables
    
    //determine direction of any obstacles    
    
    //decisions on barriers    
    //0 = no barriers
    //1 = barrier to north
    //2 = barrier to south
    //3 = barrier to north and south
    //4 = barrier to east
    //5 = barrier to north and east
    //6 = barrier to south and east
    //7 = barrier to north, south and east
    //8 = barrier to west
    //9 = barrier to north and west
    //10 = barrier to south and west
    //11 = barrier to north, south and west
    //12 = barrier to east and west
    //13 = barrier to north, east and west
    //14 = barrier to south, east and west
    //15 = barriers to north, south, east and west
    
    // if barrier > 0 set enemyRetreat to 1 and enemySteps to 15
        
    // enemySteps move the enemy X number of steps away from the obstacle
    // enemyDirection which direction our enemy should move to retreat from an obstacle
    // enemyRetreat retreat or advance
    
    //if enemyRetreat == 1
    {
        //retreat from obstacle
        
        //create random number 1 to 4
        
        //decide on direction of travel    
        //             1
        //           3   4
        //             2
        
        // if enemySteps > 0
        {
            // move in direction of travel decided by random number and allowable directions
            // in certain circumstances bias travel toward player 
            // //decrease enemySteps
        }
        
        // if enemySteps == 0
        {
            //then set enemyRetreat to 0
        }
    }

    //if enemyRetreat = 0
    {
        //advance toward player
    }
    
    //keep enemies from going past border
    
    //recombine 3 variables enemySteps, enemyDirection and enemyRetreate into enemyFF    
}

This is our overhead view of the routine. We call the routine, once per enemy and once per pixel.

Our steps are:

1) We create the variables.
2) Disassemble the EnemyF variable
3) Initialize the remaining variables.
4) Determine if there is an obstacle and if so which direction the obstacle is in.
5) If no obstacle skip to step 10
6) If there is an obstacle, set enemyRetreat to 1 and enemySteps to 15
7) If enemyRetreat is 1, then we retreat from the obstacle by creating a random number.
8) If enemySteps is greater than 0, move in the direction determined by the random number and allowable direction. In certain circumstances bias the movement to the player. Decrease the enemySteps variable by 1.
9) If enemySteps is equal to 0 then we set the enemyRetreat to 0.
10) If enemyRetreat is equal to 0, move enemy toward the player.
11) Keep the enemy from going past the border.
12) Recombine the enemySteps, enemyDirection and enemyRetreat back into the enemyFF variable and pass it back to the enemy Sprite.

Quite a few steps involved and there are going to be more details inserted along the way. This, of course, will mean that this will be the largest routine in the whole game. But it’s one of the most important routines as this makes the enemy smarter.

Steps 1 to 3 are pretty straightforward, so let’s examine step 4 closer (determine if there is an obstacle and the direction the obstacle is in).

Steps 4-9 will include many more sub-steps than is immediately obvious. No worries, we’ see what details are needed.

Here are our details and pseudo steps.

Warning, a big chunk of pseudo-code coming up.

    //determine which way the obstacle lies in and direction of travel
    {
        //reuse tile detection method to see which tiles are near
        
        // create a random number 1 to 4
        
            //             1
            //           3   4
            //             2
            
        //1 = barrier to north
        //include bias
        {
            //can only travel south, east or west
            // if random number == 1
            {
                //travel south
                //set enemyDirection to 2
            }
            // if random number == 2
            {
                //travel east
                //set enemyDirection to 4
            }
            // if random number == 3
            {
                //travel west
                //set enemyDirection to 3
            }
            
            //bias toward player
            // enemyX and enemyY
            // playerX and playerY
            // if random number == 4 and enemyX > playerX
            {
                //bias toward the player
                //travel west toward player
                //set enemyDirection to 3
            }        
            // if random number == 4 and enemyX  playerY
            {
                //condition cannot happen because of a north barrier
                //travel perpendicular from obstacle (south)
                //set enemyDirection to 2
            }
            // if random number == 4 and enemyY  playerX
            {
                //bias toward the player
                //travel west toward player
                //set enemyDirection to 3
            }        
            // if random number == 4 and enemyX  playerY
            {
                //bias toward the player
                //travel north toward player
                //set enemyDirection to 1
            }
            // if random number == 4 and enemyY  playerX
            {
                //bias toward the player
                //travel west toward player
                //set enemyDirection to 3
            }        
            // if random number == 4 and enemyX  playerY
            {
                //bias toward the player
                //travel north toward player
                //set enemyDirection to 1
            }
            // if random number == 4 and enemyY  playerX
            {
                //condition cannot happen because of a west barrier
                //travel perpendicular from obstacle (east)
                //set enemyDirection to 4
            }        
            // if random number == 4 and enemyX  playerY
            {
                //bias toward the player
                //travel north toward player
                //set enemyDirection to 1
            }
            // if random number == 4 and enemyY < playerY
            {
                //bias toward the player
                //travel south toward player
                //set enemyDirection to 2
            }
            //set enemyRetreat to 1        
            //set enemySteps to 15        
        }
        
        //9 = barrier to north and west
        {
            //can only travel south or east
            // if random number == 1 or random number == 2
            {
                //travel south
                //set enemyDirection to 2
            }
            // if random number == 3 or random number == 4
            {
                //travel east
                //set enemyDirection to 4
            }
            //set enemyRetreat to 1        
            //set enemySteps to 15        
        }
        
        //10 = barrier to south and west
        {
            //can only travel north or east
            // if random number == 1 or random number == 2
            {
                //travel north
                //set enemyDirection to 1
            }
            // if random number == 3 or random number == 4
            {
                //travel east
                //set enemyDirection to 4
            }
            //set enemyRetreat to 1        
            //set enemySteps to 15        
        }
            
        //11 = barrier to north, south and west
        {
            //can only travel east
            //set enemyRetreat to 1
            //set enemyDirection to 4
            //set enemySteps to 15
        }

        //12 = barrier to east and west
        {
            //can only travel north or south
            // if random number == 1 or random number == 2
            {
                //travel north
                //set enemyDirection to 1
            }
            // if random number == 3 or random number == 4
            {
                //can only travel south
                //set enemyDirection to 2
            }
            //set enemyRetreat to 1        
            //set enemySteps to 15        
        }
        
        //13 = barrier to north, east and west
        {
            //can only travel south
            //set enemyRetreat to 1
            //set enemyDirection to 2
            //set enemySteps to 15
        }

        //14 = barrier to south, east and west
        {
            //can only travel north
            //set enemyRetreat to 1
            //set enemyDirection to 1
            //set enemySteps to 15
        }
        
    }

Whew, and that’s just the pseudo code. It’s long, and the actual code will be about the same, give or take 20-40 lines or so. With all of that, it’s pretty easy to understand, especially when you come up with the pseudo code first and place them as comments.

We are going to read the tiles near the enemy and make 1 of 14 different decisions, starting at 1. Remember, decision 0 means there is no obstacle and the enemy can proceed toward the player, so is not accounted for in this routine.

In order for me to come up with this long series of IF’s, we have to build them up one at a time. I started off with the easiest first. When the enemy is surrounded on 3 sides, there is only one way to go, that is toward the opening. The next ones to come up with are when the enemy is surrounded on the top and bottom or to the left and right. We just make a random decision and then go in that direction. The same went for other objects that were corners (top and right, top and left, bottom and right, bottom and left).

The most complex ones to come up with were times when there was just one obstacle. In those instances, we introduce a bias as well. First, we choose a random number between 1 and 4, the first 3 number just send the enemy in a particular direction other than in the direction of the obstacle. If 4 is chosen, we make an additional 4 decisions. If the enemy is to the left, right, above or below a player, move in that direction unless an obstacle is in the way in that direction. If an obstacle is in that direction, then we move perpendicular to the obstacle.

So, let’s start with the easy one, you can only travel in one direction.

//7 = barrier to north, south and east
{
    //can only travel west
    //set enemyRetreat to 1
    //set enemyDirection to 3
    //set enemySteps to 15
}

The direction our enemy sprite can travel is north, south, east and west and is numbered like so.

N 1
W E 3 4
S 2

With 1 being north, 2 being south, 3 being west and 4 being east.

Is our pseudo-code, if a 7 is detected while checking for barriers, then we can only travel west. We’ll set our retreat to 1, tell the enemy they can only travel west (indicated by 3) and we tell the enemy to continue 15 steps before making another decision.

So, now that we have the pseudo-code steps that we want to take, let’s translate this into the real code. It’s pretty straight-forward since the small steps are already written.

//7 = barrier to north, south and east
{
    //can only travel west
    enemyRetreat = 1;//set enemyRetreat to 1
    enemyDirection = 3;//set enemyDirection to 3
    enemySteps = 15;//set enemySteps to 15			
}

Almost embarrassingly easy, but that’s because we detailed it prior.

Now, let’s do a quick examination of when we encounter 2 barriers, in this case, a barrier to the north and south. Here’s our pseudo-code.

//3 = barrier to north and south
{
    //can only travel east or west
    // if random number == 1 or random number == 2
    {
        //travel east
        //set enemyDirection to 4
    }
    // if random number == 3 or random number == 4
    {
        //travel west
        //set enemyDirection to 3
    }
    //set enemyRetreat to 1        
    //set enemySteps to 15        
}

Again, since we laid out our pseudo-code, it’s amazingly simple to code.

//3 = barrier to north and south
{
    //can only travel east or west
    if (decision == 1 || decision == 2)
    {
        //travel east
        enemyDirection = 4;//set enemyDirection to 4
    }
    if (decision == 3 || decision == 4)
    {
        //travel west
        enemyDirection = 3;//set enemyDirection to 3
        }
    enemyRetreat = 1;//set enemyRetreat to 1		
    enemySteps = 15;//set enemySteps to 15	
}

This is almost the exact same as the last code snippet. The only difference is the inclusion of the decision variable and the OR (||) operator. We give the enemy sprite a 50/50 chance to travel in one direction or another.

This works the exact same anytime we encounter 2 obstacles.  If the obstacles are corners from one another, such as North + East, we really do the same thing other than the direction we push the enemy sprite.

The most difficult scenario is when we encounter a single obstacle.  Here we’re going to introduce a limited bias toward the player, but only if the random number hits 4.

Since, this bias is heavy in the If statements, we are limiting this to one obstacle.  We could introduce this to the 2 obstacle scenario, but it could possibly hog up more memory than I want to put in.

Here’s the pseudo-code.


//1 = barrier to north
//include bias
{
    //can only travel south, east or west
    // if random number == 1
    {
        //travel south
        //set enemyDirection to 2
    }
    // if random number == 2
    {
        //travel east
        //set enemyDirection to 4
    }
    // if random number == 3
    {
        //travel west
        //set enemyDirection to 3
    }
            
    //bias toward player
    // if random number == 4 and enemyX > playerX
    {
        //travel west toward player
        //set enemyDirection to 3
        }        
        // if random number == 4 and enemyX  playerY
        {
        //cannot travel because of a north barrier
        //travel perpendicular from obstacle (south)
        //set enemyDirection to 2
        }
        // if random number == 4 and enemyY < playerY
        {
        //travel south toward player
        //set enemyDirection to 2
        }
        //set enemyRetreat to 1        
        //set enemySteps to 15        
    }
}

In here, we see that if the random number is between 1 and 3, there is no difference from the other routines other than the choices of direction. Where this routine really show it’s differences is when the random number 4 is chosen. When that happens, we are thrown into bias mode.

Once we are in bias mode, then we will send the enemy directly toward the player unless the player is n the other side of an obstacle. If that happens, we move the enemy perpendicular to the obstacle.

Now let’s try that in real code.

//1 = barrier to north
//include bias
if (barrierPosition == 1)
{
    //can only travel south, east or west
    if (decision == 1)
    {
        //travel south
        enemyDirection = 2;//set enemyDirection to 2
    }
    if (decision == 2)
    {
        //travel east
        enemyDirection = 4;//set enemyDirection to 4
    }
    if (decision == 3)
    {
        //travel west
        enemyDirection = 3;//set enemyDirection to 3
    }
			
    //bias toward player
    if (decision == 4 && enemyX > playerX)
    {
        //travel west toward player				
        enemyDirection = 3;//set enemyDirection to 3
    }
    if (decision == 4 && enemyX  playerY)
    {
        //cannot travel because of a north barrier
        //travel perpendicular from obstacle (south)
        enemyDirection = 2;//set enemyDirection to 2
    }
    if (decision == 4 && enemyY < playerY)
    {
        //travel south toward player
        enemyDirection = 2;//set enemyDirection to 2
    }
    enemyRetreat = 1;//set enemyRetreat to 1		
    enemySteps = 15;//set enemySteps to 15
}

Sure, it’s a little more trouble writing this routine, but it will make the game a little more difficult to complete. The enemy doesn't always come after you, so it’s a bit unpredictable intermittently. That introduces danger, which should make the game more frantic and exciting. Well, that’s the theory anyway.

Once all the directional controls are done, and yes it takes up quite a bit of space to do it all, then what we have remaining is actually moving the enemy.

As you may remember, the movement defined above is only when the enemy encounters an obstacle. If there is no obstacle, then the enemy simply moves toward the player. So, the next thing we need to account for is the enemy movement when near an obstacle and the movements define once the obstacle is encountered. We only define these steps when the variable enemyRetreat is in process.

That’s where our next bit of code comes in.

Here’s our pseudo-code.

//if enemyRetreat == 1
{
    //retreat from obstacle		
    //if enemyDirection == 3
    {
        //go west
    }
    //if enemyDirection == 4
    {
        //go east
    }
    //if enemyDirection == 2
    {
        //go south
    }
    //if enemyDirection == 1
    {
        //go north
    }
     //reduce enemy steps using enemySteps --;	
}

All that is happening here is IF we are in retreat mode (which happens when an obstacle is encountered), we move in our predefined axis. Our direction is set when we encounter an obstacle. We move the enemy by one pixel in that direction and then reduce the number of steps by one.

Our actual code is extremely close to what we had in the pseudo-code.

if (enemyRetreat == 1)
{
    if (enemyDirection == 3)
    {
        enemyX--;//go west
    }
    if (enemyDirection == 4)
    {
        enemyX++;//go east			
    }
    if (enemyDirection == 2)
    {
        enemyY++;//go south
    }
    if (enemyDirection == 1)
    {
        enemyY--;//go north
    }
    enemySteps --;//reduce enemy steps by one
}

Our next condition we need to check is if the enemy has taken the required steps and if it has taken all of them, then we need to start attacking the player.

if (enemySteps == 0)
{
    enemyRetreat = 0;//then set enemyRetreat to 0 and advance toward player
}

Our next condition is directly related to the above code, IF we are not retreating, then we are attacking.

if (enemyRetreat == 0)//then we are not retreating any more, now attack
{
    //advance toward player
    //move south, east, west		
    if (enemyX  playerX)
    {
        enemyX--;//go west
    }
    if (enemyY  playerY)
    {
        enemyY--;//go north
    }
}

Our final condition we need to check is to make sure that the enemy sprites will not go past the edges of the screen, this is easily done with some hard numbers.

//keep enemies from going past border
//need hard numbers
if (enemyX > 232)
{
    enemyX--;
}
if (enemyX  152)
{
    enemyY--;
}
if (enemyY < 8)
{
    enemyY++;
}

The final bit that we need to do for this function is to return the numbers back to the individual enemy sprite.

//recombine 3 variables enemySteps, enemyDirection and enemyRetreate into enemyFF
//take the 3 variables
//enemySteps, enemyDirection and enemyRetreat and shift back to their proper spots	
// enemySteps should already be in it's proper place
// enemyDirection needs to be shifted left 4 bits
enemyDirection = enemyDirection << 4;
//enemyRetreat needs to be shifted left 7 bits
enemyRetreat = enemyRetreat << 7;
//enemyF now has our 3 variables stored and is returned back to main loop
enemyF = enemySteps | enemyDirection | enemyRetreat;

When all is created and compiled, what do we get? Almost the same darn thing, we are deciding per pixel rather than moving x number of spaces before deciding again. Arrrgh. Time for some debugging.

OK, I did find a few bugs and I’ve restructured the code a bit to accommodate for the little bugs. One of them was a result of testing. I had placed in

enemyF = 127;

To test to make sure my variable was kicking back a number I was expecting. I forgot about it and left it in there. Whoops.

I also placed my barrier detection routine outside of the decision process.

A few other issues and it now works properly.

Our long code is below.

First our random number macro

#define SHR3 (jz=jsr, jsr^=(jsr<>5), jsr^=(jsr<<3),jz+jsr)
void enemyCollision(unsigned char playerX, unsigned char playerY, unsigned char enemyXX, unsigned char enemyYY, unsigned char enemyFF)
{
    // create variables    
    unsigned char barrierPosition;
    unsigned char playerCharX, playerCharY;
    unsigned char enemyCharX, enemyCharY;
    unsigned char enemyXwest, enemyXeast;
    unsigned char enemyYnorth, enemyYsouth;    
    unsigned char barrierNorth, barrierSouth, barrierEast, barrierWest;
    
    
    // move the enemy X number of steps from the obstacle
    unsigned char enemyDirection;
    // which direction our enemy should move to retreat from an obstacle
    //unsigned char enemyRetreat;    // 1 = retreat from obstacle    // 0 = advance toward player
    
    // process enemyFF into 3 seperate variables enemySteps enemyDirection enemyRetreate
    // in order to split the first 4 bits from the last 4 bits, we need
    // some bit shifting tweaking
    
    //push the last 4 bits off of the 8 bit binary 
    //so that 0000 0001 becomes 0001 0000
    enemySteps = enemyFF <> 5;
    
    // perform bitshifting to determine which direction the enemy
    // should be facing
    // we need to strip off the left most bit by shifting once to the left.
    enemyDirection = enemyFF <> 5;
    
    //0000 0000 = 0 = north
    //0001 0000 = 1 = south
    //0010 0000 = 2 = east
    //0011 0000 = 3 = west
    //0100 0000 = 4 = northeast
    //0101 0000 = 5 = northwest
    //0110 0000 = 6 = southeast
    //0111 0000 = 7 = southwest
    
    
    //The final bit of information is if the enemy
    //is advancing toward the player for retreating from
    //an obstacle
    
    //This is stored in the leftmost bit
    //retrieve by bit shifting 7 bit to the right
    enemyRetreat = enemyFF >> 7;
    //1000 0000 = retreating from obstacle
    //0000 0000 = advance toward player
    
    // initilize variables
    //playerCharX character range 0 - 15
    //playerCharY character range 0 - 9    
    //enemyCharX character range 0 - 15
    //enemyCharY character range 0 - 9
    //playerX pixel range 0 - 240
    //playerY pixel range 0 - 160
    //enemyX pixel range 0 - 240
    //enemyY pixel range 0 - 160
    playerCharX = playerX >> 4;
    playerCharY = playerY >> 4;
    enemyCharX = enemyXX >> 4;
    enemyCharY = enemyYY >> 4;    
    enemyYnorth = (enemyYY + 6) >> 4;
    enemyYsouth = (enemyYY - 6) >> 4;
    enemyXeast = (enemyXX + 6) >> 4;
    enemyXwest = (enemyXX - 6) >> 4;    
    
    decision = 0;
    barrierPosition = 0;

    //determine direction of any obsticles
    barrierNorth = tiles[(enemyYnorth - 1) * scrw + enemyCharX];
    barrierSouth = tiles[(enemyYsouth + 1) * scrw + enemyCharX];
    barrierWest = tiles[enemyCharY * scrw + (enemyXeast - 1)];
    barrierEast = tiles[enemyCharY * scrw + (enemyXwest + 1)];
    //if a tile to the north + 1
    //if a tile to the south + 2
    //if a tile to the east + 4
    //if a tile to the west + 8    
    
    //find which barriers are around
    if (barrierNorth > 0)
    {
        barrierPosition = barrierPosition + 1;
    }
    if (barrierSouth > 0)
    {
        barrierPosition = barrierPosition + 2;
    }
    if (barrierEast > 0)
    {
        barrierPosition = barrierPosition + 4;
    }
    if (barrierWest > 0)
    {
        barrierPosition = barrierPosition + 8;
    }
    
    if (barrierPosition == 0 && enemySteps == 0)
    {
        enemyRetreat = 0;
    }
        
    //determine which way the obstacle lies in and direction of travel

    if (barrierPosition > 0)
    {        
        enemyRetreat = 1;//set enemyRetreat to 1        
        enemySteps = 8;//set enemySteps to 15
        //decisions on barriers    
        //0 = no barriers
        //1 = barrier to north
        //2 = barrier to south
        //3 = barrier to north and south
        //4 = barrier to east
        //5 = barrier to north and east
        //6 = barrier to south and east
        //7 = barrier to north, south and east
        //8 = barrier to west
        //9 = barrier to north and west
        //10 = barrier to south and west
        //11 = barrier to north, south and west
        //12 = barrier to east and west
        //13 = barrier to north, east and west
        //14 = barrier to south, east and west
        //15 = barriers to north, south, east and west    
        
        // create a random number 1 to 4
        decision = (SHR3 % 4);//our random number from the macro
        //range is 1 to 4
            //             1
            //           3   4
            //             2

        //1 = barrier to north
        //include bias
        if (barrierPosition == 1)
        {
            //can only travel south, east or west
            if (decision == 1)
            {
                //travel south
                enemyDirection = 2;//set enemyDirection to 2
            }
            if (decision == 2)
            {
                //travel east
                enemyDirection = 4;//set enemyDirection to 4
            }
            if (decision == 3)
            {
                //travel west
                enemyDirection = 3;//set enemyDirection to 3
            }
            
            //bias toward player
            if (decision == 4 && enemyX > playerX)
            {
                //travel west toward player                
                enemyDirection = 3;//set enemyDirection to 3
            }
            if (decision == 4 && enemyX  playerY)
            {
                //cannot travel because of a north barrier
                //travel perpendicular from obstacle (south)
                enemyDirection = 2;//set enemyDirection to 2
            }
            if (decision == 4 && enemyY  playerX)
            {
                //travel west toward player
                enemyDirection = 3;//set enemyDirection to 3
            }
            if (decision == 4 && enemyX  playerY)
            {
                //travel north toward player
                enemyDirection = 1;//set enemyDirection to 1
            }
            if (decision == 4 && enemyY  playerX
            if (decision == 4 && enemyX > playerX)
            {
                //travel west toward player
                enemyDirection = 3;//set enemyDirection to 3
            }
            if (decision == 4 && enemyX  playerY)
            {
                //bias toward the player
                //travel north toward player
                enemyDirection = 1;//set enemyDirection to 1
            }
            if (decision == 4 && enemyY  playerX)
            {
                //cannot travel because of a west barrier
                //travel perpendicular from obstacle (east)
                enemyDirection = 4;//set enemyDirection to 4
            }        
            if (decision == 4 && enemyX  playerY)
            {
                //travel north toward player
                enemyDirection = 1;//set enemyDirection to 1
            }
            if (decision == 4 && enemyY < playerY)
            {
                //travel south toward player
                enemyDirection = 2;//set enemyDirection to 2
            }
            enemyRetreat = 1;//set enemyRetreat to 1
        }
        
        //9 = barrier to north and west
        if (barrierPosition == 9)
        {
            //can only travel south or east
            if (decision == 1 || decision == 2)
            {
                //travel south
                enemyDirection = 2;//set enemyDirection to 2
            }
            if (decision == 3 || decision == 4)
            {
                //travel east
                enemyDirection = 4;//set enemyDirection to 4
            }
            enemyRetreat = 1;//set enemyRetreat to 1
        }
        
        //10 = barrier to south and west
        if (barrierPosition == 10)
        {
            //can only travel north or east
            if (decision == 1 || decision == 2)
            {
                //travel north
                enemyDirection = 1;//set enemyDirection to 1
            }
            if (decision == 3 || decision == 4)
            {
                //travel east
                enemyDirection = 4;//set enemyDirection to 4
            }
            enemyRetreat = 1;//set enemyRetreat to 1
        }
            
        //11 = barrier to north, south and west
        if (barrierPosition == 11)
        {
            //can only travel east
            enemyDirection = 4;//set enemyDirection to 4
            enemyRetreat = 1;//set enemyRetreat to 1
        }

        //12 = barrier to east and west
        if (barrierPosition == 12)
        {
            //can only travel north or south
            if (decision == 1 || decision == 2)
            {
                //travel north
                enemyDirection = 1;//set enemyDirection to 1
            }
            if (decision == 3 || decision == 4)
            {
                //can only travel south
                enemyDirection = 2;//set enemyDirection to 2
            }
            enemyRetreat = 1;//set enemyRetreat to 1
        }
        
        //13 = barrier to north, east and west
        if (barrierPosition == 13)
        {
            //can only travel south
            enemyDirection = 2;//set enemyDirection to 2
            enemyRetreat = 1;//set enemyRetreat to 1
        }
        //14 = barrier to south, east and west
        if (barrierPosition == 14)
        {
            //can only travel north
            enemyDirection = 1;//set enemyDirection to 1
            enemyRetreat = 1;//set enemyRetreat to 1
        }        
    }

    if (enemyRetreat == 0)//then we are not retreating any more, now attack
    {
        //advance toward player
        //move south, east, west        
        if (enemyX  playerX)
        {
            enemyX--;//go west
        }
        if (enemyY  playerY)
        {
            enemyY--;//go south
        }
    }
    
    //back away from obstacles
    if(enemyRetreat == 1)
    {
        if (enemyDirection == 3)
        {
            enemyX--;//go west
        }
        if (enemyDirection == 4)
        {
            enemyX++;//go east            
        }
        if (enemyDirection == 2)
        {
            enemyY++;//go south
        }
        if (enemyDirection == 1)
        {
            enemyY--;//go north
        }
        enemySteps --;//reduce enemy steps by one
    }
        
    if (enemySteps == 0)
    {
        enemyRetreat = 0;//then set enemyRetreat to 0 and advance toward player
    }
        
    //keep enemies from going past border
    //need hard numbers
    {
        if (enemyX > 232)
        {
            enemyX--;
        }
        if (enemyX  152)
        {
            enemyY--;
        }
        if (enemyY < 8)
        {
            enemyY++;
        }
    }
    
    //recombine 3 variables enemySteps, enemyDirection and enemyRetreate into enemyFF
    //take the 3 variables
    //enemySteps, enemyDirection and enemyRetreat and shift back to their proper spots
    
    // enemySteps should already be in it's proper place
    
    // enemyDirection needs to be shifted left 4 bits
    enemyDirection = enemyDirection << 4;
    
    //enemyRetreat needs to be shifted left 7 bits
    enemyRetreat = enemyRetreat << 7;

    //enemyF now has our 3 variables stored and is returned back to main loop
    enemyF = enemySteps | enemyDirection | enemyRetreat;
}

The whole thing is called via.

enemyX = sprites[i].x;
enemyY = sprites[i].y;
enemyImage = sprites[i].n;
enemyF = sprites[i].f;

enemyCollision(playerXpos, playerYpos, enemyX, enemyY, enemyF);

sprites[i].x = enemyX;
sprites[i].y = enemyY;
sprites[i].n = enemyImage;
sprites[i].f = enemyF;

The enemies now will look like this when pursuing you.

The whole routine measures in at 2500 bytes. That’s heftier than I originally thought it would be. Time for some optimizations, as this is much too large.

Happy St. Patrick's day to everyone

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