Jump to content
  • entries
    658
  • comments
    2,707
  • views
    906,906

Homing in on you


  • Tanks now launch Homing Missiles
  • changeable wall color
  • menu is back
  • lots of ROM savings

Menu's back in place. All options should be functional, so let me know if something doesn't work as expected.

blogentry-3056-0-58661100-1392509407_thumb.png

 

A couple Tanks launched Homing Missiles. They're shaped differently than the normal shots. The menu option sets the number of redirects each Homing Missile can make. Values range from 0-3 (0=no redirect). The "remove brick" routine needs to be updated to cope with the homing missiles. If it lines up with 2 bricks it only takes 1 out. If the tank keeps shooting, the same brick is taken out so the tank won't be able to create an opening it can shoot thru.

blogentry-3056-0-36883700-1392509401_thumb.png

 

The wall color used to be hard coded. I've seen purple and red walls in Frenzy, so I've added the ability for the C code to control the wall color. At the moment it only uses blue for Berzerk and purple for Frenzy.

 

Found over 800 bytes worth of savings!

 

First savings was 172 bytes by changing how I divide by 5 for dealing with the playfield pixels (each "brick" is 5 scanlines tall). The ARM supports multiplication, but not division, so I had been using a 185 byte table.

const unsigned char div_5[] = 
{
     0,  0,  0,  0,  0,
     1,  1,  1,  1,  1,
     2,  2,  2,  2,  2,...
     35, 35, 35, 35, 35,
     36, 36, 36, 36, 36
};

     if (y < 36*5)
          y5 = div_5[y];
     else
          y5 = 36;
 

Omegamatrix's first(!) blog entry referenced reciprocal multiplication which is basically using x * 0.2 to get the result. We only have integers, so we use an implied decimal point like when doing subpixel positioning.

#define DIV_BY_5                0xCCCD
#define DIV_BY_5_SHIFT          18

     y5 = (y * DIV_BY_5) >> DIV_BY_5_SHIFT;
 

My next find was spotting this in the "player shoots" routine:

switch (direction)
{
     case 0:
          gMissileX[0] = gSpriteX[0] + 7;
          gMissileY[0] = gSpriteY[0] + 3;
          gMissileControl[0] |= 0;
          break;
          
     case 1:
          gMissileX[0] = gSpriteX[0] + 6;
          gMissileY[0] = gSpriteY[0] + 9;
          gMissileControl[0] |= 1;
          break;
          
     case 2:
          gMissileX[0] = gSpriteX[0] + 3;
          gMissileY[0] = gSpriteY[0] + 16;
          gMissileControl[0] |= 2;
          break;
          
     case 3:
          gMissileX[0] = gSpriteX[0] - 3;
          gMissileY[0] = gSpriteY[0] + 9;
          gMissileControl[0] |= 1;
          break;
          
     case 4:
          gMissileX[0] = gSpriteX[0] - 4;
          gMissileY[0] = gSpriteY[0] + 3;
          gMissileControl[0] |= 0;
          break;
          
     case 5:
          gMissileX[0] = gSpriteX[0] - 4;
          gMissileY[0] = gSpriteY[0] + 1;
          gMissileControl[0] |= 1;
          break;
          
     case 6:
          gMissileX[0] = gSpriteX[0] + 3;
          gMissileY[0] = gSpriteY[0] - 5;
          gMissileControl[0] |= 2;
          break;
          
     case 7:
          gMissileX[0] = gSpriteX[0] + 7;
          gMissileY[0] = gSpriteY[0] + 1;
          gMissileControl[0] |= 1;
          break;
          
     default:
          break;
}
 

and realizing I could change it (and the corresponding "robot/tank shoots") to use a few tables:

const signed char missile_x_offset[] =
{
     7, 6, 3,-1,-4,-2, 3, 7,       // humanoid
     7, 6, 3, 0,-4, 0, 3, 7        // robot and tank
};

const signed char missile_y_offset[] =
{
     3, 9,16, 9, 3, 1,-5, 1,       // humanoid
     6,13,13,13, 6,-3,-4,-3,       // robot and tank
};

const char missile_shape[] =
{
     0, 1, 2, 1, 0, 1, 2, 1        // humanoid and robot (tank is always 3)
};

...

     gMissileX[i]        = gSpriteX[0] + missile_x_offset[direction];
     gMissileY[i]        = gSpriteY[0] + missile_y_offset[direction];
     gMissileControl[i] |= missile_shape[direction];
 

That saved 188 bytes. I bet it's also faster.

 

 

The next was realizing I had similar switch/case logic for the humanoid movement:

switch (direction)
{
     case 0:
          gSpriteX[0]++;
          break;
          
     case 1:
          gSpriteX[0]++;
          gSpriteY[0]++;
          break;
          
     case 2:
          gSpriteY[0]++;
          break;
          
     case 3:
          gSpriteX[0]--;
          gSpriteY[0]++;
          break;
          
     case 4:
          gSpriteX[0]--;
          break;
          
     case 5:
          gSpriteX[0]--;
          gSpriteY[0]--;
          break;
          
     case 6:
          gSpriteY[0]--;
          break;
               
     case 7:
          gSpriteX[0]++;
          gSpriteY[0]--;
          break;
          
     default:
          break;
} 
 

And changed it to this:

const signed char dir_x[] = { 1, 1, 0,-1,-1,-1, 0, 1 };
const signed char dir_y[] = { 0, 1, 1, 1, 0,-1,-1,-1 };

...

     gSpriteX[0] += dir_x[direction];
     gSpriteY[0] += dir_y[direction];
 

The same logic was used for moving Otto, the robots, shots, and doing tests of "can the robot safely move here". I changed all of those to use the tables for 376 bytes of savings.

 

Finally, when I added back the menu I decided to make the Continue feature always enabled. Removing the menu logic saved another 68 bytes.

 

Note: the continue feature has not been added back yet.

 

Controls

  • RESET = start game
  • SELECT = return to menu
  • Right Difficulty, Test Mode*: B = Off, A = On

* Test Mode is infinite lives and max robots. Score will be red when active.

 

ROM

frantic20140215.bin

 

Source

Frantic20140215.zip

  • Like 2

7 Comments


Recommended Comments

The fractional bits are discarded. I excelled at college math, but it was so long ago that when I read the modular inverse page I didn't follow it enough to be able to code it.

Thanks, me too! I brought over the color shading from Space Rocks as the menu looked flat when I reimplemented it. I did notice the text is taller in Frantic - shrinking it to match Space Rocks would provide some ROM savings. I've added a note to my To Do list as something to consider.

The fractional bits are discarded. I excelled at college math, but it was so long ago that when I read the modular inverse page I didn't follow it enough to be able to code it.

For unsigned 32-bit numbers, I believe the answer is to multiply by 0xCCCCCCCD. (It looks pretty similar to your fixed-point constant...) Modulo arithmetic is the mathematician's way of simulating a fixed number of bits in an integer representation, so in this case I solved for for x*5 = 1 mod 2^32 (uisng Wolfram Alpha). For 8-bit ints it would be mod 2^8, and I think you're probably familar with how large numbers behave in that case. It's a neat bit of mathemagic, if you care to use it.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...