1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-11-26 10:49:17 +00:00
8bitworkshop/presets/astrocade/cosmic.c
2018-09-02 17:03:17 -04:00

586 lines
18 KiB
C

/*
* An Astrocade port of the Cosmic Impalas game
* described in the book
* "Making 8-bit Arcade Games in C"
*/
//#link "acheader.s"
#include <string.h>
#define EXIT_CLIPY(y) if (((unsigned char)y)>=VHEIGHT) return
#define EXIT_CLIPDEST(addr) if ((((word)addr)&0xfff) >= 0xe10) return
//#link "acheader.s"
typedef unsigned char byte;
typedef signed char sbyte;
typedef unsigned short word;
/// HARDWARE
__sfr __at(0x00) hw_col0r; // palette 0
__sfr __at(0x01) hw_col1r;
__sfr __at(0x02) hw_col2r;
__sfr __at(0x03) hw_col3r;
__sfr __at(0x04) hw_col0l;
__sfr __at(0x05) hw_col1l;
__sfr __at(0x06) hw_col2l;
__sfr __at(0x07) hw_col3l; // palette 7
__sfr __at(0x09) hw_horcb; // horiz color boundary
__sfr __at(0x0a) hw_verbl; // vertical blanking line * 2
__sfr __at(0x0c) hw_magic; // magic register
__sfr __at(0x19) hw_xpand; // expander register
__sfr __at(0x08) hw_intst; // intercept test feedback
__sfr __at(0x10) hw_p1ctrl; // player controls
__sfr __at(0x11) hw_p2ctrl; // player controls
__sfr __at(0x12) hw_p3ctrl; // player controls
__sfr __at(0x13) hw_p4ctrl; // player controls
#define M_SHIFT0 0x00
#define M_SHIFT1 0x01
#define M_SHIFT2 0x02
#define M_SHIFT3 0x03
#define M_XPAND 0x08
#define M_MOVE 0x00
#define M_OR 0x10
#define M_XOR 0x20
#define M_FLOP 0x40
#define M_SHIFT(x) ((x)&3)
#define XPAND_COLORS(off,on) (((off)&3) | (((on)&3)<<2))
/// GRAPHICS FUNCTIONS
#define VHEIGHT 89 // number of scanlines
#define VBWIDTH 40 // number of bytes per scanline
#define PIXWIDTH 160 // 4 pixels per byte
byte __at (0x0000) vmagic[VHEIGHT][VBWIDTH];
byte __at (0x4000) vidmem[VHEIGHT][VBWIDTH];
// clear screen and set graphics mode
void clrscr() {
memset(vidmem, 0, VHEIGHT*VBWIDTH); // clear page 1
}
// draw vertical line
void vline(byte x, byte y1, byte y2, byte col, byte op) {
byte xb = x>>2; // divide x by 4
byte* dest = &vmagic[y1][xb]; // destination address
byte y;
hw_magic = M_SHIFT(x) | op; // set magic register
col <<= 6; // put color in high pixel
for (y=y1; y<=y2; y++) {
EXIT_CLIPDEST(dest);
*dest = col; // shift + xor color
dest += VBWIDTH; // dest address to next scanline
}
}
// draw a pixel
void pixel(byte x, byte y, byte col, byte op) {
vline(x, y, y, col, op); // draw line with 1-pixel height
}
// render a sprite with the given graphics operation
void render_sprite(const byte* src, byte x, byte y, byte op) {
byte i,j;
byte w = *src++; // get width from 1st byte of sprite
byte h = *src++; // get height from 2nd byte of sprite
byte xb = x>>2; // divide x by 4
byte* dest = &vmagic[y][xb]; // destination address
hw_magic = M_SHIFT(x) | op; // set magic register
for (j=0; j<h; j++) {
EXIT_CLIPDEST(dest);
for (i=0; i<w; i++) {
*dest++ = *src++;
}
*dest = 0; // rest of shifted byte
dest += VBWIDTH-w; // dest address to next scanline
}
}
// erase a sprite
void erase_sprite(const byte* src, byte x, byte y) {
byte i,j;
byte w = *src++; // get width from 1st byte of sprite
byte h = *src++; // get height from 2nd byte of sprite
byte xb = x>>2; // divide x by 4
byte* dest = &vidmem[y][xb]; // destination address
for (j=0; j<h; j++) {
EXIT_CLIPDEST(dest);
for (i=0; i<w; i++) {
*dest++ = 0;
}
*dest = 0; // rest of shifted byte
dest += VBWIDTH-w; // dest address to next scanline
}
}
// FONT FUNCTIONS
#define LOCHAR 32
#define HICHAR 127
#define FONT_BWIDTH 1
#define FONT_HEIGHT 8
const char FONT[HICHAR-LOCHAR+1][FONT_HEIGHT*FONT_BWIDTH] = {
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, },{ 0x00,0x00,0x00,0x20,0x20,0x20,0x00,0x20, },{ 0x00,0x00,0x50,0x50,0x50,0x00,0x00,0x00, },{ 0x00,0x00,0x00,0x50,0xF8,0x50,0xF8,0x50, },{ 0x00,0x00,0x00,0xF8,0xA0,0xF8,0x28,0xF8, },{ 0x00,0x00,0x00,0xC8,0xD0,0x20,0x58,0x98, },{ 0x00,0x00,0x00,0xE0,0xA8,0xF8,0x90,0xF8, },{ 0x00,0x00,0x40,0x40,0x40,0x00,0x00,0x00, },{ 0x00,0x00,0x30,0x20,0x20,0x20,0x20,0x20, },{ 0x00,0x00,0x60,0x20,0x20,0x20,0x20,0x20, },{ 0x00,0x00,0x00,0x20,0xA8,0x70,0xA8,0x20, },{ 0x00,0x00,0x00,0x20,0x20,0xF8,0x20,0x20, },{ 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60, },{ 0x00,0x00,0x00,0x00,0x00,0xF8,0x00,0x00, },{ 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60, },{ 0x00,0x00,0x00,0x08,0x10,0x20,0x40,0x80, },{ 0x00,0x00,0x00,0xF8,0x88,0xE8,0x88,0xF8, },{ 0x00,0x00,0x00,0x10,0x30,0x50,0x10,0x10, },{ 0x00,0x00,0x00,0xF8,0x08,0xF8,0x80,0xF8, },{ 0x00,0x00,0x00,0xF8,0x08,0xF8,0x08,0xF8, },{ 0x00,0x00,0x00,0x38,0x48,0x88,0xF8,0x08, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x08,0xF8, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x08,0x10,0x20,0x40, },{ 0x00,0x00,0x00,0xF8,0x88,0xF8,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x88,0xF8,0x08,0xF8, },{ 0x00,0x00,0x00,0x30,0x30,0x00,0x30,0x30, },{ 0x00,0x00,0x00,0x30,0x30,0x00,0x30,0x30, },{ 0x00,0x00,0x08,0x10,0x20,0x40,0x20,0x10, },{ 0x00,0x00,0x00,0x00,0xF8,0x00,0xF8,0x00, },{ 0x00,0x00,0x40,0x20,0x10,0x08,0x10,0x20, },{ 0x00,0x00,0x00,0xF8,0x08,0x78,0x00,0x60, },{ 0x00,0x00,0x00,0xF8,0xA8,0xB8,0x80,0xF8, },{ 0x00,0x00,0x00,0xF8,0x88,0xF8,0x88,0x88, },{ 0x00,0x00,0x00,0xF0,0x90,0xF8,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x80,0x80,0x80,0xF8, },{ 0x00,0x00,0x00,0xE0,0x90,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x80,0xF8, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x80,0x80, },{ 0x00,0x00,0x00,0xF8,0x80,0xB8,0x88,0xF8, },{ 0x00,0x00,0x00,0x88,0x88,0xF8,0x88,0x88, },{ 0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40, },{ 0x00,0x00,0x00,0x08,0x08,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0x88,0x90,0xA0,0x90,0x88, },{ 0x00,0x00,0x00,0x80,0x80,0x80,0x80,0xF8, },{ 0x00,0x00,0x00,0xFE,0x92,0x92,0x92,0x92, },{ 0x00,0x00,0x00,0x88,0xC8,0xA8,0x98,0x88, },{ 0x00,0x00,0x00,0xF8,0x88,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x88,0x88,0xF8,0x80, },{ 0x00,0x00,0x00,0xF8,0x88,0xA8,0xA8,0xF8, },{ 0x00,0x00,0x00,0xF8,0x88,0xF8,0x90,0x88, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x08,0xF8, },{ 0x00,0x00,0x00,0xF8,0x20,0x20,0x20,0x20, },{ 0x00,0x00,0x00,0x88,0x88,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0x88,0x88,0x90,0xA0,0xC0, },{ 0x00,0x00,0x00,0x92,0x92,0x92,0x92,0xFE, },{ 0x00,0x00,0x00,0x88,0x50,0x20,0x50,0x88, },{ 0x00,0x00,0x00,0x88,0x88,0xF8,0x08,0xF8, },{ 0x00,0x00,0x00,0xF8,0x10,0x20,0x40,0xF8, },{ 0x00,0x00,0x38,0x20,0x20,0x20,0x20,0x20, },{ 0x00,0x00,0x00,0x80,0x40,0x20,0x10,0x08, },{ 0x00,0x00,0x70,0x10,0x10,0x10,0x10,0x10, },{ 0x00,0x00,0x00,0x20,0x50,0x88,0x00,0x00, },{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, },{ 0x00,0x00,0x40,0x20,0x10,0x00,0x00,0x00, },{ 0x00,0x00,0x00,0xF8,0x88,0xF8,0x88,0x88, },{ 0x00,0x00,0x00,0xF0,0x90,0xF8,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x80,0x80,0x80,0xF8, },{ 0x00,0x00,0x00,0xE0,0x90,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x80,0xF8, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x80,0x80, },{ 0x00,0x00,0x00,0xF8,0x80,0xB8,0x88,0xF8, },{ 0x00,0x00,0x00,0x88,0x88,0xF8,0x88,0x88, },{ 0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x40, },{ 0x00,0x00,0x00,0x08,0x08,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0x88,0x90,0xA0,0x90,0x88, },{ 0x00,0x00,0x00,0x80,0x80,0x80,0x80,0xF8, },{ 0x00,0x00,0x00,0xFE,0x92,0x92,0x92,0x92, },{ 0x00,0x00,0x00,0x88,0xC8,0xA8,0x98,0x88, },{ 0x00,0x00,0x00,0xF8,0x88,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0xF8,0x88,0x88,0xF8,0x80, },{ 0x00,0x00,0x00,0xF8,0x88,0xA8,0xA8,0xF8, },{ 0x00,0x00,0x00,0xF8,0x88,0xF8,0x90,0x88, },{ 0x00,0x00,0x00,0xF8,0x80,0xF8,0x08,0xF8, },{ 0x00,0x00,0x00,0xF8,0x20,0x20,0x20,0x20, },{ 0x00,0x00,0x00,0x88,0x88,0x88,0x88,0xF8, },{ 0x00,0x00,0x00,0x88,0x88,0x90,0xA0,0xC0, },{ 0x00,0x00,0x00,0x92,0x92,0x92,0x92,0xFE, },{ 0x00,0x00,0x00,0x88,0x50,0x20,0x50,0x88, },{ 0x00,0x00,0x00,0x88,0x88,0xF8,0x08,0xF8, },{ 0x00,0x00,0x00,0xF8,0x10,0x20,0x40,0xF8, },{ 0x00,0x00,0x38,0x20,0x20,0xE0,0x20,0x20, },{ 0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20, },{ 0x00,0x00,0xE0,0x20,0x20,0x38,0x20,0x20, },{ 0x00,0x00,0x00,0xE8,0xB8,0x00,0x00,0x00, },{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, },};
// draw a letter
void draw_char(byte ch, byte x, byte y, byte op) {
const byte* src = &FONT[(ch-LOCHAR)][0];
byte xb = x>>2; // divide x by 4
byte* dest = &vmagic[y][xb]; // destination address
hw_magic = M_SHIFT(x) | M_XPAND | op;
for (byte i=0; i<8; i++) {
char b = *src++;
EXIT_CLIPDEST(dest);
*dest++ = b; // expand lower nibble -> 1st byte
*dest++ = b; // expand upper nibble -> 2nd byte
*dest++ = 0; // leftover -> 3rd byte
*dest = 0; // reset upper/lower flag
dest += VBWIDTH-3; // we incremented 3 bytes for this line
}
}
void draw_string(const char* str, byte x, byte y) {
do {
byte ch = *str++;
if (!ch) break;
draw_char(ch, x, y, M_MOVE);
x += 8;
} while (1);
}
void draw_bcd_word(word bcd, byte x, byte y, byte op) {
byte j;
x += 3*8;
for (j=0; j<4; j++) {
draw_char('0'+(bcd&0xf), x, y, op);
x -= 8;
bcd >>= 4;
}
}
// add two 16-bit BCD values
word bcd_add(word a, word b) {
a; b; // to avoid warning
__asm
ld hl,#4
add hl,sp
ld iy,#2
add iy,sp
ld a,0 (iy)
add a, (hl)
daa
ld c,a
ld a,1 (iy)
inc hl
adc a, (hl)
daa
ld b,a
ld l, c
ld h, b
__endasm;
}
/////
//
// GAME GRAPHICS
//
const byte player_bitmap[] =
{3,14,/*{w:12,h:16,bpp:2,brev:1}*/0x00,0x3C,0x00,0x00,0x18,0x00,0x00,0x3C,0x00,0x00,0x18,0x00,0x04,0x18,0x20,0x0C,0x3C,0x30,0x3C,0x3C,0x3C,0x1F,0xE7,0xF4,0x1F,0x66,0xF4,0x17,0xE7,0xE4,0x17,0xE7,0xE4,0x1C,0x7E,0x34,0x1C,0xFF,0x34,0x3C,0x18,0x3C,0x0C,0x18,0x30,0x04,0x18,0x20};
const byte bomb_bitmap[] =
{1,5,/*{w:8,h:5,bpp:2,brev:1}*/0x88,0x55,0x77,0x55,0x88};
const byte bullet_bitmap[] =
{1,5,/*{w:8,h:5,bpp:2,brev:1}*/0x14,0x28,0x14,0x14,0x28};
const byte enemy1_bitmap[] =
{2,8,/*{w:16,h:8,bpp:2,brev:1}*/0x00,0x00,0x70,0x38,0xF8,0x7C,0xFC,0xFC,0xFE,0xFC,0xFE,0xFF,0xFC,0xFF,0xF8,0x7F,0xF0,0x3F,0x88,0x47,0xF0,0x3F,0xF0,0x3F,0xD0,0x2F,0x8C,0xC7,0x48,0x48,0x80,0x04};
const byte enemy2_bitmap[] =
{2,8,/*{w:16,h:8,bpp:2,brev:1}*/0x00,0x00,0x30,0x0C,0x14,0x28,0x2E,0x74,0x08,0x10,0x20,0x04,0xE0,0x07,0xD0,0x0B,0xB0,0x0D,0xB2,0x4D,0x19,0x98,0x8E,0x71,0x82,0x41,0xB1,0x8D,0x59,0x9A,0x4A,0x52};
const byte enemy3_bitmap[] =
{2,8,/*{w:16,h:8,bpp:2,brev:1}*/0x00,0x00,0x00,0x00,0x04,0x20,0x05,0xA0,0x05,0xA0,0x25,0xA4,0xA7,0xE5,0xF7,0xEF,0xF7,0xEF,0xFE,0x7F,0xFC,0x3F,0xBC,0x3D,0xE4,0x27,0x20,0x00,0x00,0x00,0x00,0x00};
const byte enemy4_bitmap[] =
{2,8,/*{w:16,h:8,bpp:2,brev:1}*/0x00,0x00,0x00,0x00,0xF0,0x0F,0xF8,0x1F,0xD8,0x1B,0xF8,0x1F,0xF8,0x1F,0xF8,0x1F,0xF0,0x0F,0xA8,0x15,0xCC,0x33,0xE8,0x17,0x66,0x66,0x33,0xCC,0x61,0x86,0x40,0x02};
const byte* const enemy_bitmaps[4] = {
enemy1_bitmap,
enemy2_bitmap,
enemy3_bitmap,
enemy4_bitmap
};
#define COLOR_BUNKER 1
#define COLOR_GROUND 2
#define COLOR_SCORE 3
//
// GAME CODE
//
#define MAXLIVES 5
#define PLYRHEIGHT 14
#define ENEMY_SPACING_X 17
#define ENEMY_SPACING_Y 11
#define ENEMY_MARCH_X 1
#define ENEMY_MARCH_Y 2
typedef struct {
byte x,y;
const byte* shape; // need const here
} Enemy;
#define MAX_ENEMIES 28
Enemy enemies[MAX_ENEMIES];
byte enemy_index;
byte num_enemies;
typedef struct {
int right:1;
int down:1;
} MarchMode;
MarchMode this_mode, next_mode;
byte attract;
word score;
byte lives;
const byte player_y = VHEIGHT-PLYRHEIGHT-1;
byte player_x;
byte bullet_x;
byte bullet_y;
byte bomb_x;
byte bomb_y;
void draw_lives() {
byte i;
byte n = lives;
byte y = 0;
byte x = PIXWIDTH-4*5;
hw_xpand = XPAND_COLORS(0, COLOR_SCORE);
for (i=0; i<MAXLIVES; i++) {
draw_char(i<n?'|':' ', x, y, M_MOVE);
x += 4;
}
}
void draw_score() {
byte x = 10*8;
byte y = 0;
hw_xpand = XPAND_COLORS(0, COLOR_SCORE);
draw_bcd_word(score, x, y, M_MOVE);
}
void draw_bunker(byte x, byte y, byte y2, byte h, byte w) {
byte i,a=0,b=0;
for (i=0; i<h; i++) {
a = y-y2-i*2;
b = y-i;
vline(x+i, a, b, M_XOR, COLOR_BUNKER);
vline(x+h*2+w-i-1, a, b, M_XOR, COLOR_BUNKER);
}
for (i=0; i<w; i++) {
vline(x+h+i, a, b, M_XOR, COLOR_BUNKER);
}
}
void draw_playfield() {
byte i;
clrscr();
hw_xpand = XPAND_COLORS(0, COLOR_SCORE);
draw_string("PLAYER 1", 0, 0);
draw_score();
draw_lives();
for (i=0; i<PIXWIDTH; i++)
pixel(i, VHEIGHT-1, COLOR_GROUND, M_OR);
// TODO: const
draw_bunker(20, 65, 15, 15, 20);
draw_bunker(100, 65, 15, 15, 20);
}
void add_score(word pts) {
if (attract) return;
score = bcd_add(score, pts);
draw_score();
}
void xor_player_derez() {
byte i,j;
byte x = player_x+13;
byte y = player_y+PLYRHEIGHT-1;
byte* rand = (byte*) &clrscr; // use code as random #'s
for (j=1; j<=0x1f; j++) {
for (i=0; i<50; i++) {
signed char xx = x + (*rand++ & 0xf) - 15;
signed char yy = y - (*rand++ & j);
pixel(xx, yy, *rand++, M_XOR);
}
}
}
void destroy_player() {
xor_player_derez(); // xor derez pattern
render_sprite(player_bitmap, player_x, VHEIGHT-PLYRHEIGHT, M_XOR); // erase ship via xor
xor_player_derez(); // xor 2x to erase derez pattern
player_x = 0xff;
lives--;
}
void init_enemies() {
byte i,x,y,bm;
x = 0;
y = ENEMY_SPACING_Y;
bm=0;
for (i=0; i<MAX_ENEMIES; i++) {
Enemy* e = &enemies[i];
e->x = x;
e->y = y;
e->shape = enemy_bitmaps[bm];
x += ENEMY_SPACING_X;
if (x >= PIXWIDTH-ENEMY_SPACING_X*2) {
x = 0;
y += ENEMY_SPACING_Y;
bm++; // TODO: can overflow
}
}
enemy_index = 0;
num_enemies = MAX_ENEMIES;
this_mode.right = 1;
this_mode.down = 0;
next_mode.right = 1;
next_mode.down = 0;
}
void delete_enemy(Enemy* e) {
erase_sprite(e->shape, e->x, e->y);
memmove(e, e+1, sizeof(Enemy)*(enemies-e+MAX_ENEMIES-1));
num_enemies--; // update_next_enemy() will check enemy_index
}
void update_next_enemy() {
Enemy* e;
if (enemy_index >= num_enemies) {
enemy_index = 0;
memcpy(&this_mode, &next_mode, sizeof(this_mode));
}
e = &enemies[enemy_index];
erase_sprite(e->shape, e->x, e->y);
if (this_mode.down) {
e->y += ENEMY_MARCH_Y;
// if too close to ground, end game
if (e->y > VHEIGHT-ENEMY_SPACING_Y) {
destroy_player();
lives = 0;
}
next_mode.down = 0;
} else {
if (this_mode.right) {
e->x += ENEMY_MARCH_X;
if (e->x >= PIXWIDTH-ENEMY_SPACING_X) {
next_mode.down = 1;
next_mode.right = 0;
}
} else {
e->x -= ENEMY_MARCH_X;
if (e->x == 0) {
next_mode.down = 1;
next_mode.right = 1;
}
}
}
render_sprite(e->shape, e->x, e->y, M_XOR);
enemy_index++;
}
char in_rect(Enemy* e, byte x, byte y, byte w, byte h) {
byte ew = e->shape[0]*8;
byte eh = e->shape[1];
return (x >= e->x-w && x <= e->x+ew && y >= e->y-h && y <= e->y+eh);
}
Enemy* find_enemy_at(byte x, byte y) {
byte i;
for (i=0; i<num_enemies; i++) {
Enemy* e = &enemies[i];
if (in_rect(e, x, y, 0, 2)) {
return e;
}
}
return NULL;
}
void check_bullet_hit(byte x, byte y) {
Enemy* e = find_enemy_at(x,y);
if (e) {
delete_enemy(e);
add_score(0x25);
}
}
void fire_bullet() {
bullet_x = player_x + 4;
bullet_y = VHEIGHT-PLYRHEIGHT-6;
render_sprite(bullet_bitmap, bullet_x, bullet_y, M_XOR); // draw
}
void move_bullet() {
byte leftover;
hw_intst; // reset intercept counters
render_sprite(bullet_bitmap, bullet_x, bullet_y, M_XOR); // erase
leftover = (hw_intst & 0xf0); // any pixels leftover?
if (leftover || bullet_y < 10) {
erase_sprite(bullet_bitmap, bullet_x, bullet_y);
check_bullet_hit(bullet_x, bullet_y+2);
bullet_y = 0;
} else {
bullet_y -= 4;
render_sprite(bullet_bitmap, bullet_x, bullet_y, M_XOR); // draw
}
}
void drop_bomb() {
Enemy* e = &enemies[enemy_index];
bomb_x = e->x + 7;
bomb_y = e->y + 16;
render_sprite(bomb_bitmap, bomb_x, bomb_y, M_XOR);
}
void move_bomb() {
hw_intst; // reset intercept counters
render_sprite(bomb_bitmap, bomb_x, bomb_y, M_XOR); // erase
if (bomb_y > VHEIGHT-12) {
bomb_y = 0;
} else if (hw_intst & 0xf0) { // any pixels leftover?
erase_sprite(bomb_bitmap, bomb_x, bomb_y); // erase bunker
if (bomb_y > VHEIGHT-23) {
// player was hit (probably)
destroy_player();
}
bomb_y = 0;
} else {
bomb_y += 3;
render_sprite(bomb_bitmap, bomb_x, bomb_y, M_XOR);
}
}
byte frame;
signed char player_dir = 0;
void move_player() {
if (attract) {
if (bullet_y == 0) fire_bullet();
player_dir = 0;
} else {
byte mask = hw_p1ctrl;
if (mask & 0x4) {
if (player_x > 0)
player_x--;
}
if (mask & 0x8) {
if (player_x < PIXWIDTH-16)
player_x++;
}
if (mask & 0x10) {
if (bullet_y == 0) {
fire_bullet();
}
}
}
// move player
render_sprite(player_bitmap, player_x, player_y, M_MOVE);
}
void play_round() {
draw_playfield();
player_x = PIXWIDTH/2-8;
bullet_y = 0;
bomb_y = 0;
frame = 0;
while (player_x != 0xff && num_enemies) {
move_player();
if (bullet_y) {
move_bullet();
}
update_next_enemy();
if (frame & 1) {
if (bomb_y == 0) {
drop_bomb();
} else {
move_bomb();
}
}
frame++;
}
}
void init_game() {
score = 0;
lives = 5;
}
void game_over_msg() {
byte i;
byte x=16;
byte y=10;
hw_xpand = XPAND_COLORS(0, COLOR_SCORE);
for (i=0; i<50; i++) {
draw_string(" *************** ", x, y+0*8);
draw_string("*** ***", x, y+1*8);
draw_string("** GAME OVER **", x, y+2*8);
draw_string("*** ***", x, y+3*8);
draw_string(" *************** ", x, y+4*8);
}
}
void play_game() {
attract = 0;
init_game();
init_enemies();
while (lives) {
play_round();
if (num_enemies == 0) {
init_enemies();
}
}
game_over_msg();
}
void attract_mode() {
attract = 1;
while (1) {
init_enemies();
play_round();
}
}
void setup_registers() {
hw_col0r = 0x00;
hw_col1r = 0x20;
hw_col2r = 0xe0;
hw_col3r = 0xa0;
hw_col0l = 0x10;
hw_col1l = 0x30;
hw_col2l = 0xc0;
hw_col3l = 0xf0;
hw_horcb = 0;
hw_verbl = VHEIGHT*2;
}
void main() {
setup_registers();
// NOTE: initializers don't get run, so we init here
while (1) {
//attract_mode();
play_game();
}
}