Jump to content
  • entries
    650
  • comments
    2,609
  • views
    872,048

Homing in on you

SpiceWare

2,265 views

  • 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.

frenzy-19.png frenzy-20.png

 

 

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.

Share this comment


Link to comment

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.

Share this comment


Link to comment

Just tested this in my calculator:

0xCCCCCCCD * 0A = 0x800000002

 

the leading 8 is beyond the 32 bits so would drop off, leaving 10/5 = 2. Looks promising

0xCCCCCCCD * 0F = 0x800000003

 

It works for 15/5 as well. However, values that are not evenly divisible by 5 don't appear to work:

0xCCCCCCCD * 02 = 0x19999999A

0xCCCCCCCD * 0B = 0x8CCCCCCCF

Share this comment


Link to comment

Well, I'm stumped. I'll have to bug a professor about that.

 

Edit: oh, it's (probably) because there is no "correct" answer for integer division in those cases. Darned mathematicians... Sorry for wasting your time.

Share this comment


Link to comment

Although it probably won't make a whit of difference, you can push it to the following - verified as same as INT([0-185]/5.0):

#define DIV_BY_5                0x19999A
#define DIV_BY_5_SHIFT          23

Share this comment


Link to comment
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...