2020-07-21 16:36:15 +00:00
|
|
|
/*
|
|
|
|
Text-based version of a Blockade-style game.
|
|
|
|
For more information, see "Making Arcade Games in C".
|
|
|
|
*/
|
2020-05-24 17:18:27 +00:00
|
|
|
|
2022-08-09 19:39:27 +00:00
|
|
|
#include "common.h"
|
2020-05-24 17:18:27 +00:00
|
|
|
|
2022-08-09 19:39:27 +00:00
|
|
|
// get the character at a specfic x/y position
|
|
|
|
byte readcharxy(byte x, byte y) {
|
2024-10-21 02:04:21 +00:00
|
|
|
return PEEK(SCRNADR(0x400, x, y));
|
2020-05-24 17:18:27 +00:00
|
|
|
}
|
|
|
|
|
2024-10-14 17:28:24 +00:00
|
|
|
// delay for 'count' frames
|
2020-05-24 17:18:27 +00:00
|
|
|
void delay(byte count) {
|
|
|
|
while (count--) {
|
2022-08-09 19:39:27 +00:00
|
|
|
waitvsync();
|
2020-05-24 17:18:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////// GAME DATA
|
|
|
|
|
|
|
|
typedef struct {
|
2024-10-14 17:28:24 +00:00
|
|
|
byte x; // x coordinate
|
|
|
|
byte y; // y coordinate
|
|
|
|
byte dir; // direction (0-3)
|
2024-10-21 02:04:21 +00:00
|
|
|
byte score; // current score
|
2024-10-14 17:28:24 +00:00
|
|
|
char head_attr; // char to draw player
|
|
|
|
char tail_attr; // char to draw trail
|
2024-10-21 02:04:21 +00:00
|
|
|
bool collided; // did we collide?
|
|
|
|
bool human; // is this player a human?
|
2020-05-24 17:18:27 +00:00
|
|
|
} Player;
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
Player players[2]; // player #0 and #1 data
|
2020-05-24 17:18:27 +00:00
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
byte frames_per_move; // speed of game
|
|
|
|
byte gameover; // = 1 if game is over
|
2020-05-24 17:18:27 +00:00
|
|
|
|
|
|
|
#define START_SPEED 12
|
|
|
|
#define MAX_SPEED 5
|
|
|
|
#define MAX_SCORE 7
|
|
|
|
|
|
|
|
///////////
|
|
|
|
|
|
|
|
const char BOX_CHARS[8] = { '+', '+', '+', '+',
|
|
|
|
'-', '-', '!', '!'};
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// draw a box from coordinate (x,y) to (x2,y2)
|
2020-05-24 17:18:27 +00:00
|
|
|
void draw_box(byte x, byte y, byte x2, byte y2, const char* chars) {
|
|
|
|
byte x1 = x;
|
|
|
|
cputcxy(x, y, chars[2]);
|
|
|
|
cputcxy(x2, y, chars[3]);
|
|
|
|
cputcxy(x, y2, chars[0]);
|
|
|
|
cputcxy(x2, y2, chars[1]);
|
|
|
|
while (++x < x2) {
|
|
|
|
cputcxy(x, y, chars[5]);
|
|
|
|
cputcxy(x, y2, chars[4]);
|
|
|
|
}
|
|
|
|
while (++y < y2) {
|
|
|
|
cputcxy(x1, y, chars[6]);
|
|
|
|
cputcxy(x2, y, chars[7]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// draw the playfield border and score
|
2020-05-24 17:18:27 +00:00
|
|
|
void draw_playfield() {
|
|
|
|
draw_box(0,1,COLS-1,ROWS-1,BOX_CHARS);
|
2024-10-14 17:28:24 +00:00
|
|
|
cputsxy( 0, 0, "plyr1:");
|
|
|
|
cputsxy(20, 0, "plyr2:");
|
2020-05-24 17:18:27 +00:00
|
|
|
cputcxy( 7, 0, players[0].score+'0');
|
|
|
|
cputcxy(27, 0, players[1].score+'0');
|
|
|
|
}
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// constants for the 4 cardinal directions
|
2020-05-24 17:18:27 +00:00
|
|
|
typedef enum { D_RIGHT, D_DOWN, D_LEFT, D_UP } dir_t;
|
|
|
|
const sbyte DIR_X[4] = { 1, 0, -1, 0 };
|
|
|
|
const sbyte DIR_Y[4] = { 0, 1, 0, -1 };
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// initialize game and player data
|
2020-05-24 17:18:27 +00:00
|
|
|
void init_game() {
|
|
|
|
memset(players, 0, sizeof(players));
|
|
|
|
players[0].head_attr = '1';
|
|
|
|
players[1].head_attr = '2';
|
|
|
|
players[0].tail_attr = '#';
|
|
|
|
players[1].tail_attr = '*';
|
|
|
|
frames_per_move = START_SPEED;
|
|
|
|
}
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// reset players to initial conditions
|
2020-05-24 17:18:27 +00:00
|
|
|
void reset_players() {
|
|
|
|
players[0].x = players[0].y = 5;
|
|
|
|
players[0].dir = D_RIGHT;
|
|
|
|
players[1].x = COLS-6;
|
|
|
|
players[1].y = ROWS-6;
|
|
|
|
players[1].dir = D_LEFT;
|
|
|
|
players[0].collided = players[1].collided = 0;
|
|
|
|
}
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// draw player character at head
|
2020-05-24 17:18:27 +00:00
|
|
|
void draw_player(Player* p) {
|
|
|
|
cputcxy(p->x, p->y, p->head_attr);
|
|
|
|
}
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// move player and detect collision
|
2020-05-24 17:18:27 +00:00
|
|
|
void move_player(Player* p) {
|
|
|
|
cputcxy(p->x, p->y, p->tail_attr);
|
|
|
|
p->x += DIR_X[p->dir];
|
|
|
|
p->y += DIR_Y[p->dir];
|
2022-08-09 19:39:27 +00:00
|
|
|
if ((readcharxy(p->x, p->y) & 0x7f) != ' ')
|
2020-05-24 17:18:27 +00:00
|
|
|
p->collided = 1;
|
|
|
|
draw_player(p);
|
|
|
|
}
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// read joystick and move player
|
2020-05-24 17:18:27 +00:00
|
|
|
void human_control(Player* p) {
|
|
|
|
byte dir = 0xff;
|
|
|
|
char joy;
|
|
|
|
if (!p->human) return;
|
|
|
|
if (!kbhit()) return;
|
2024-10-30 18:58:10 +00:00
|
|
|
joy = joy_read(1);
|
2020-05-24 17:18:27 +00:00
|
|
|
if (JOY_UP(joy)) dir = D_UP;
|
|
|
|
if (JOY_LEFT(joy)) dir = D_LEFT;
|
|
|
|
if (JOY_RIGHT(joy)) dir = D_RIGHT;
|
|
|
|
if (JOY_DOWN(joy)) dir = D_DOWN;
|
|
|
|
// don't let the player reverse direction
|
|
|
|
if (dir < 0x80 && dir != (p->dir ^ 2)) {
|
|
|
|
p->dir = dir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// AI player: try to move in direction 'dir'
|
|
|
|
// the number of squares (1 << shift)
|
|
|
|
// return 1 if successful, and change p->dir
|
2020-05-24 17:18:27 +00:00
|
|
|
byte ai_try_dir(Player* p, dir_t dir, byte shift) {
|
|
|
|
byte x,y;
|
|
|
|
dir &= 3;
|
|
|
|
x = p->x + (DIR_X[dir] << shift);
|
|
|
|
y = p->y + (DIR_Y[dir] << shift);
|
2022-08-09 19:39:27 +00:00
|
|
|
if (x < COLS && y < ROWS
|
|
|
|
&& (readcharxy(x, y) & 0x7f) == ' ') {
|
2020-05-24 17:18:27 +00:00
|
|
|
p->dir = dir;
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// AI computer player routine
|
2020-05-24 17:18:27 +00:00
|
|
|
void ai_control(Player* p) {
|
|
|
|
dir_t dir;
|
|
|
|
if (p->human) return;
|
|
|
|
dir = p->dir;
|
|
|
|
if (!ai_try_dir(p, dir, 0)) {
|
|
|
|
ai_try_dir(p, dir+1, 0);
|
|
|
|
ai_try_dir(p, dir-1, 0);
|
|
|
|
} else {
|
|
|
|
ai_try_dir(p, dir-1, 0) && ai_try_dir(p, dir-1, 1+(rand() & 3));
|
|
|
|
ai_try_dir(p, dir+1, 0) && ai_try_dir(p, dir+1, 1+(rand() & 3));
|
|
|
|
ai_try_dir(p, dir, rand() & 3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// flash player(s) that collided
|
2020-05-24 17:18:27 +00:00
|
|
|
void flash_colliders() {
|
|
|
|
byte i;
|
|
|
|
for (i=0; i<56; i++) {
|
|
|
|
delay(2);
|
|
|
|
revers(players[0].collided && (i&1));
|
|
|
|
draw_player(&players[0]);
|
|
|
|
revers(players[1].collided && (i&1));
|
|
|
|
draw_player(&players[1]);
|
|
|
|
}
|
|
|
|
revers(0);
|
|
|
|
}
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// move both players
|
2020-05-24 17:18:27 +00:00
|
|
|
void make_move() {
|
|
|
|
byte i;
|
|
|
|
for (i=0; i<frames_per_move; i++) {
|
|
|
|
human_control(&players[0]);
|
|
|
|
delay(1);
|
|
|
|
}
|
|
|
|
ai_control(&players[0]);
|
|
|
|
ai_control(&players[1]);
|
|
|
|
// if players collide, 2nd player gets the point
|
2020-05-25 18:04:04 +00:00
|
|
|
textcolor(COLOR_CYAN);
|
2020-05-24 17:18:27 +00:00
|
|
|
move_player(&players[1]);
|
2020-05-25 18:04:04 +00:00
|
|
|
textcolor(COLOR_YELLOW);
|
2020-05-24 17:18:27 +00:00
|
|
|
move_player(&players[0]);
|
2020-05-25 18:04:04 +00:00
|
|
|
textcolor(COLOR_WHITE);
|
2020-05-24 17:18:27 +00:00
|
|
|
}
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// end of game, show the winner
|
2020-05-24 17:18:27 +00:00
|
|
|
void declare_winner(byte winner) {
|
|
|
|
byte i;
|
|
|
|
clrscr();
|
|
|
|
for (i=0; i<ROWS/2-3; i++) {
|
|
|
|
draw_box(i,i,COLS-1-i,ROWS-1-i,BOX_CHARS);
|
|
|
|
delay(1);
|
|
|
|
}
|
|
|
|
cputsxy(12,10,"WINNER:");
|
|
|
|
cputsxy(12,13,"PLAYER ");
|
|
|
|
cputcxy(12+7, 13, '1'+winner);
|
|
|
|
delay(200);
|
|
|
|
gameover = 1;
|
|
|
|
}
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// play a round of the game
|
|
|
|
// if someone got MAX_SCORE points, show the winner
|
2020-05-24 17:18:27 +00:00
|
|
|
void play_round() {
|
|
|
|
reset_players();
|
|
|
|
clrscr();
|
2020-05-25 18:04:04 +00:00
|
|
|
textcolor(COLOR_WHITE);
|
2020-05-24 17:18:27 +00:00
|
|
|
draw_playfield();
|
|
|
|
while (1) {
|
|
|
|
make_move();
|
|
|
|
if (players[0].collided || players[1].collided) break;
|
|
|
|
}
|
|
|
|
flash_colliders();
|
|
|
|
// add scores to players that didn't collide
|
|
|
|
if (players[0].collided) players[1].score++;
|
|
|
|
if (players[1].collided) players[0].score++;
|
|
|
|
// increase speed
|
|
|
|
if (frames_per_move > MAX_SPEED) frames_per_move--;
|
|
|
|
// game over?
|
|
|
|
if (players[0].score != players[1].score) {
|
|
|
|
if (players[0].score >= MAX_SCORE)
|
|
|
|
declare_winner(0);
|
|
|
|
else if (players[1].score >= MAX_SCORE)
|
|
|
|
declare_winner(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// play a complete game until someone wins
|
2020-05-24 17:18:27 +00:00
|
|
|
void play_game() {
|
|
|
|
gameover = 0;
|
|
|
|
init_game();
|
|
|
|
players[0].human = 1;
|
|
|
|
while (!gameover) {
|
|
|
|
play_round();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-30 18:58:10 +00:00
|
|
|
// main routine
|
2020-05-24 17:18:27 +00:00
|
|
|
void main() {
|
|
|
|
joy_install (joy_static_stddrv);
|
|
|
|
play_game();
|
|
|
|
}
|