Files
uiuc-ece391-mp3/missile/missile-command.c

280 lines
7.3 KiB
C

/* missile-command.c
* Mark Murphy 2007
* This is the user-space side of the ECE391 MP1 Missile Command game. Enjoy.
*/
#include <stdint.h>
#include "ece391support.h"
#include "ece391syscall.h"
#include "mp1.h"
/* screen writing routines - defined in vga.c */
extern void init_screen();
extern void write_char(char, int x, int y);
extern void write_string(char*, int x, int y);
extern void clear_screen();
/* Static data */
int rtc_fd = -1; // RTC file descriptor
int rng_fd = -1; // RNG file descriptor
int tux_fd = -1; // Tux Controller file descriptor
int mouse_fd = -1; // Mouse file descriptor
int fired = 0; /* Count of user-fired missiles */
int score = 0; /* score, as reported by ioctl(GETSTATUS) */
int bases_left = 0; /* number of bases remaining */
/* This command_t enum encodes the input keys we care about */
typedef enum { NOKEY, QUIT, LEFT, RIGHT, UP, DOWN, FIRE } command_t;
int rand() {
int ret;
if(-1 == ece391_read(rng_fd, &ret, sizeof(int))) {
return 0;
}
return ret;
}
/* get_command_tux()
* Checks if a meaningful key on Tux was pressed, and returns it;
*/
command_t get_command_tux() {
static command_t tux_mapping[8] = {FIRE, LEFT, FIRE, RIGHT, UP, DOWN, LEFT, RIGHT};
static uint8_t tux_allow_long_press[8] = {0, 1, 0, 1, 1, 1, 1, 1};
static uint8_t prev_status = 0;
// After game start, START button quits game
if(bases_left) tux_mapping[0] = QUIT;
// Get key from Tux Controller
uint8_t status;
if(-1 == ece391_read(tux_fd, &status, sizeof(uint8_t))) {
return NOKEY;
}
int i;
for(i = 0; i < 8; i++) {
if(status & (1 << i)) {
// Long press fire detection
if(!tux_allow_long_press[i] && (prev_status & (1 << i))) continue;
prev_status = status;
return tux_mapping[i];
}
}
prev_status = status;
return NOKEY;
}
/* get_command_mouse()
* Checks if a meaningful move on mouse was done, and returns it;
*/
command_t get_command_mouse(int16_t* dx, int16_t* dy) {
int32_t buf[4];
static uint8_t is_fire_pressed = 0;
if(-1 == ece391_read(mouse_fd, buf, 4 * sizeof(int32_t))) {
return NOKEY;
}
*dx += buf[0] / 2;
*dy -= buf[1] / 2;
if(buf[2]) {
if(!is_fire_pressed) {
is_fire_pressed = 1;
return FIRE;
} else {
return NOKEY;
}
} else {
is_fire_pressed = 0;
return NOKEY;
}
}
/* get_command()
* Checks if a meaningful key was pressed, and returns it;
*/
command_t get_command(int16_t* dx, int16_t* dy) {
command_t ret = get_command_tux();
switch(ret) {
case LEFT: *dx = -1; *dy = 0; break;
case RIGHT: *dx = 1; *dy = 0; break;
case UP: *dx = 0; *dy = -1; break;
case DOWN: *dx = 0; *dy = 1; break;
default: *dx = 0; *dy = 0; break;
}
if(FIRE == get_command_mouse(dx, dy)) ret = FIRE;
return ret;
}
/* make_missile()
* Wrapper around the ioctl() to add a missile to the game.
*/
void make_missile(int sx, int sy, int dx, int dy, char c, int vel)
{
struct missile m[1];
int vx, vy, mag;
m->x = (sx<<16) | 0x8000;
m->y = (sy<<16) | 0x8000;
m->dest_x = dx;
m->dest_y = dy;
vx = (dx - sx);
vy = (dy - sy);
int abs_vx = (vx > 0) ? vx : -vx;
int abs_vy = (vy > 0) ? vy : -vy;
mag = (abs_vx > abs_vy ? abs_vx : abs_vy) << 8;
if(mag == 0) mag = 1 << 8;
m->vx = ((vx<<16)*vel)/mag;
m->vy = ((vy<<16)*vel)/mag;
m->c = c;
m->exploded = 0;
if(-1 == mp1_ioctl((unsigned long)m, RTC_ADDMISSILE)) {
ece391_fdputs(1, (uint8_t*) "add missile failed\n");
}
}
/* update_crosshairs()
* move the crosshairs via the ioctl()
*/
void update_crosshairs(command_t cmd, int16_t dx, int16_t dy){
if(crosshairs_x + dx > 79) dx = 79 - crosshairs_x;
if(crosshairs_x + dx < 0) dx = 0 - crosshairs_x;
if(crosshairs_y + dy > 24) dy = 24 - crosshairs_y;
if(crosshairs_y + dy < 0) dy = 0 - crosshairs_y;
if((dx != 0) || (dy != 0)){
unsigned long d;
d = (unsigned long)dx&0xFFFF;
d |= ((unsigned long)dy&0xFFFF)<<16;
mp1_ioctl(d, RTC_MOVEXHAIRS);
}
switch(cmd){
case FIRE:
make_missile(79, 24, crosshairs_x, crosshairs_y, '*', 200);
fired++;
default:
break;
}
}
void mp1_notify_user(int foobar){
unsigned long status_word;
if(!mp1_ioctl((unsigned long) &status_word, RTC_GETSTATUS)){
score = status_word&0xFFFF;
bases_left = ((status_word>>16)&1) + ((status_word>>17)&1)
+ ((status_word>>18)&1);
}
}
void draw_centered_string(char *s, int y){
write_string(s, (80-ece391_strlen((uint8_t*) s))/2, y);
}
#define DCS(str) draw_centered_string( str , line++)
void draw_starting_screen(){
int line = 5;
clear_screen();
DCS(" MISSILE COMMAND ");
DCS(" Mark Murphy, 2007 ");
DCS(" ");
DCS(" Commands: ");
DCS(" space ................. fire missile ");
DCS(" arrow keys ................. move crosshairs ");
DCS(" h,j,k,l ................. move crosshairs (vi-style");
DCS(" ` (backtick) ................. exit the game ");
DCS(" ");
DCS(" ");
DCS(" Protect your bases by destroying the enemy missiles (e's) ");
DCS(" with your missiles. You get 1 point for each enemy ");
DCS(" missile you destroy. The game ends when your bases are all ");
DCS(" dead or you hit the ` key. ");
DCS(" ");
DCS(" Press the space bar to continue. ");
}
void draw_status_bar(){
char buf[80] = "[score ] [fired ] [accuracy %]";
int percent = fired ? (100*score)/fired : 0;
ece391_itoa(score, (uint8_t*) (buf + (score >= 100 ? 7 : (score >= 10 ? 8 : 9))), 10);
buf[10] = ']';
ece391_itoa(fired, (uint8_t*) (buf + (fired >= 100 ? 19 : (fired >= 10 ? 20 : 21))), 10);
buf[22] = ']';
ece391_itoa(percent, (uint8_t*) (buf + (percent >= 100 ? 34 : (percent >= 10 ? 35 : 36))), 10);
buf[37] = '%';
ece391_status_msg(buf, 80, 0x02);
// write_string(buf, 0, 0);
}
static struct missile malloc_missiles[1000];
void* mp1_malloc(int size) {
int i;
for(i = 0; i < 1000; i++) {
if(!malloc_missiles[i].c) {
return (void*) &(malloc_missiles[i]);
}
}
return (void*) 0;
}
void mp1_free(void* ptr) {
ece391_memset(ptr, 0, sizeof(struct missile));
}
int main(){
command_t cmd;
mp1_ioctl(0, RTC_STARTGAME);
rng_fd = ece391_open((uint8_t*) "rng");
rtc_fd = ece391_open((uint8_t*) "rtc");
tux_fd = ece391_open((uint8_t*) "tux");
mouse_fd = ece391_open((uint8_t*) "mouse");
int rtc_interval = 32;
ece391_write(rtc_fd, &rtc_interval, sizeof(int));
/* On with the game! */
draw_starting_screen();
int16_t dx = 0, dy = 0;
while(FIRE != get_command(&dx, &dy));
clear_screen();
bases_left = 3;
while(bases_left && QUIT != (cmd = get_command(&dx, &dy))) {
draw_status_bar();
update_crosshairs(cmd, dx, dy);
mp1_rtc_tasklet();
ece391_read(rtc_fd, (void*) 0, 0);
if(rand() % 127 == 0) {
make_missile(rand()%80, 0, 20*(rand()%3+1), 24, 'e', rand()%5 + 10);
}
}
draw_centered_string("+------------+", (25/2)-1);
draw_centered_string("| Game over. |", 25/2);
draw_centered_string("+------------+", (25/2)+1);
ece391_close(rng_fd);
ece391_close(rtc_fd);
ece391_close(tux_fd);
ece391_close(mouse_fd);
return 0;
}