///////////////////////////////////////////////////////////////////////// // Game Programming All In One, Second Edition // Source Code Copyright (C)2004 by Jonathan S. Harbour // Chapter 7 - Tank War Game (Enhancement 2) ///////////////////////////////////////////////////////////////////////// #include "tankwar.h" ///////////////////////////////////////////////////////////////////////// // drawtank function // construct the tank using drawing functions ///////////////////////////////////////////////////////////////////////// void drawtank(int num) { int x = 15; //tanks[num].x; int y = 15; //tanks[num].y; int dir = tanks[num].dir; //draw tank body and turret rectfill(tank_bmp[num][dir], x-11, y-11, x+11, y+11, tanks[num].color); rectfill(tank_bmp[num][dir], x-6, y-6, x+6, y+6, 7); //draw the treads based on orientation if (dir == 0 || dir == 2) { rectfill(tank_bmp[num][dir], x-16, y-16, x-11, y+16, 8); rectfill(tank_bmp[num][dir], x+11, y-16, x+16, y+16, 8); } else if (dir == 1 || dir == 3) { rectfill(tank_bmp[num][dir], x-16, y-16, x+16, y-11, 8); rectfill(tank_bmp[num][dir], x-16, y+16, x+16, y+11, 8); } //draw the turret based on direction switch (dir) { case 0: rectfill(tank_bmp[num][dir], x-1, y, x+1, y-16, 8); break; case 1: rectfill(tank_bmp[num][dir], x, y-1, x+16, y+1, 8); break; case 2: rectfill(tank_bmp[num][dir], x-1, y, x+1, y+16, 8); break; case 3: rectfill(tank_bmp[num][dir], x, y-1, x-16, y+1, 8); break; } } ///////////////////////////////////////////////////////////////////////// // erasetank function // erase the tank using rectfill ///////////////////////////////////////////////////////////////////////// void erasetank(int num) { //calculate box to encompass the tank int left = tanks[num].x - 17; int top = tanks[num].y - 17; int right = tanks[num].x + 17; int bottom = tanks[num].y + 17; //erase the tank rectfill(screen, left, top, right, bottom, 0); } ///////////////////////////////////////////////////////////////////////// // movetank function // move the tank in the current direction ///////////////////////////////////////////////////////////////////////// void movetank(int num) { int dir = tanks[num].dir; int speed = tanks[num].speed; //update tank position based on direction switch(dir) { case 0: tanks[num].y -= speed; break; case 1: tanks[num].x += speed; break; case 2: tanks[num].y += speed; break; case 3: tanks[num].x -= speed; } //keep tank inside the screen if (tanks[num].x > SCREEN_W-22) { tanks[num].x = SCREEN_W-22; tanks[num].speed = 0; } if (tanks[num].x < 22) { tanks[num].x = 22; tanks[num].speed = 0; } if (tanks[num].y > SCREEN_H-22) { tanks[num].y = SCREEN_H-22; tanks[num].speed = 0; } if (tanks[num].y < 22) { tanks[num].y = 22; tanks[num].speed = 0; } } ///////////////////////////////////////////////////////////////////////// // explode function // display random boxes to simulate an explosion ///////////////////////////////////////////////////////////////////////// void explode(int num, int x, int y) { int n; //retrieve location of enemy tank int tx = tanks[!num].x; int ty = tanks[!num].y; //is bullet inside the boundary of the enemy tank? if (x > tx-16 && x < tx+16 && y > ty-16 && y < ty+16) score(num); //draw some random circles for the "explosion" for (n = 0; n < 10; n++) { rectfill(screen, x-16, y-16, x+16, y+16, rand() % 16); rest(1); } //clear the area of debris rectfill(screen, x-16, y-16, x+16, y+16, 0); } ///////////////////////////////////////////////////////////////////////// // updatebullet function // update the position of a bullet ///////////////////////////////////////////////////////////////////////// void updatebullet(int num) { int x = bullets[num].x; int y = bullets[num].y; if (bullets[num].alive) { //erase bullet rect(screen, x-1, y-1, x+1, y+1, 0); //move bullet bullets[num].x += bullets[num].xspd; bullets[num].y += bullets[num].yspd; x = bullets[num].x; y = bullets[num].y; //stay within the screen if (x < 5 || x > SCREEN_W-5 || y < 20 || y > SCREEN_H-5) { bullets[num].alive = 0; return; } //draw bullet x = bullets[num].x; y = bullets[num].y; rect(screen, x-1, y-1, x+1, y+1, 14); //look for a hit if (getpixel(screen, bullets[num].x, bullets[num].y)) { bullets[num].alive = 0; explode(num, x, y); } //print the bullet's position textprintf(screen, font, SCREEN_W/2-50, 1, 2, "B1 %-3dx%-3d B2 %-3dx%-3d", bullets[0].x, bullets[0].y, bullets[1].x, bullets[1].y); } } ///////////////////////////////////////////////////////////////////////// // checkpath function // check to see if a point on the screen is black ///////////////////////////////////////////////////////////////////////// int checkpath(int x1,int y1,int x2,int y2,int x3,int y3) { if (getpixel(screen, x1, y1) || getpixel(screen, x2, y2) || getpixel(screen, x3, y3)) return 1; else return 0; } ///////////////////////////////////////////////////////////////////////// // clearpath function // verify that the tank can move in the current direction ///////////////////////////////////////////////////////////////////////// void clearpath(int num) { //shortcut vars int dir = tanks[num].dir; int speed = tanks[num].speed; int x = tanks[num].x; int y = tanks[num].y; switch(dir) { //check pixels north case 0: if (speed > 0) { if (checkpath(x-16, y-20, x, y-20, x+16, y-20)) tanks[num].speed = 0; } else //if reverse dir, check south if (checkpath(x-16, y+20, x, y+20, x+16, y+20)) tanks[num].speed = 0; break; //check pixels east case 1: if (speed > 0) { if (checkpath(x+20, y-16, x+20, y, x+20, y+16)) tanks[num].speed = 0; } else //if reverse dir, check west if (checkpath(x-20, y-16, x-20, y, x-20, y+16)) tanks[num].speed = 0; break; //check pixels south case 2: if (speed > 0) { if (checkpath(x-16, y+20, x, y+20, x+16, y+20 )) tanks[num].speed = 0; } else //if reverse dir, check north if (checkpath(x-16, y-20, x, y-20, x+16, y-20)) tanks[num].speed = 0; break; //check pixels west case 3: if (speed > 0) { if (checkpath(x-20, y-16, x-20, y, x-20, y+16)) tanks[num].speed = 0; } else //if reverse dir, check east if (checkpath(x+20, y-16, x+20, y, x+20, y+16)) tanks[num].speed = 0; break; } } ///////////////////////////////////////////////////////////////////////// // fireweapon function // configure a bullet's direction and speed and activate it ///////////////////////////////////////////////////////////////////////// void fireweapon(int num) { int x = tanks[num].x; int y = tanks[num].y; //ready to fire again? if (!bullets[num].alive) { bullets[num].alive = 1; //fire bullet in direction tank is facing switch (tanks[num].dir) { //north case 0: bullets[num].x = x; bullets[num].y = y-22; bullets[num].xspd = 0; bullets[num].yspd = -BULLETSPEED; break; //east case 1: bullets[num].x = x+22; bullets[num].y = y; bullets[num].xspd = BULLETSPEED; bullets[num].yspd = 0; break; //south case 2: bullets[num].x = x; bullets[num].y = y+22; bullets[num].xspd = 0; bullets[num].yspd = BULLETSPEED; break; //west case 3: bullets[num].x = x-22; bullets[num].y = y; bullets[num].xspd = -BULLETSPEED; bullets[num].yspd = 0; } } } ///////////////////////////////////////////////////////////////////////// // forward function // increase the tank's speed ///////////////////////////////////////////////////////////////////////// void forward(int num) { tanks[num].speed++; if (tanks[num].speed > MAXSPEED) tanks[num].speed = MAXSPEED; } ///////////////////////////////////////////////////////////////////////// // backward function // decrease the tank's speed ///////////////////////////////////////////////////////////////////////// void backward(int num) { tanks[num].speed--; if (tanks[num].speed < -MAXSPEED) tanks[num].speed = -MAXSPEED; } ///////////////////////////////////////////////////////////////////////// // turnleft function // rotate the tank counter-clockwise ///////////////////////////////////////////////////////////////////////// void turnleft(int num) { tanks[num].dir--; if (tanks[num].dir < 0) tanks[num].dir = 3; } ///////////////////////////////////////////////////////////////////////// // turnright function // rotate the tank clockwise ///////////////////////////////////////////////////////////////////////// void turnright(int num) { tanks[num].dir++; if (tanks[num].dir > 3) tanks[num].dir = 0; } ///////////////////////////////////////////////////////////////////////// // getinput function // check for player input keys (2 player support) ///////////////////////////////////////////////////////////////////////// void getinput() { //hit ESC to quit if (key[KEY_ESC]) gameover = 1; //WASD / SPACE keys control tank 1 if (key[KEY_W]) forward(0); if (key[KEY_D]) turnright(0); if (key[KEY_A]) turnleft(0); if (key[KEY_S]) backward(0); if (key[KEY_SPACE]) fireweapon(0); //arrow / ENTER keys control tank 2 if (key[KEY_UP]) forward(1); if (key[KEY_RIGHT]) turnright(1); if (key[KEY_DOWN]) backward(1); if (key[KEY_LEFT]) turnleft(1); if (key[KEY_ENTER]) fireweapon(1); //short delay after keypress rest(20); } ///////////////////////////////////////////////////////////////////////// // score function // add a point to the specified player's score ///////////////////////////////////////////////////////////////////////// void score(int player) { //update score int points = ++tanks[player].score; //display score textprintf(screen, font, SCREEN_W-70*(player+1), 1, BURST, "P%d: %d", player+1, points); } ///////////////////////////////////////////////////////////////////////// // setuptanks function // set up the starting condition of each tank ///////////////////////////////////////////////////////////////////////// void setuptanks() { int n; //player 1 tanks[0].x = 30; tanks[0].y = 40; tanks[0].speed = 0; tanks[0].color = 9; tanks[0].score = 0; for (n=0; n<4; n++) { tank_bmp[0][n] = create_bitmap(32, 32); clear_bitmap(tank_bmp[0][n]); tanks[0].dir = n; drawtank(0); } tanks[0].dir = 1; //player 2 tanks[1].x = SCREEN_W-30; tanks[1].y = SCREEN_H-30; tanks[1].dir = 3; tanks[1].speed = 0; tanks[1].color = 12; tanks[1].score = 0; for (n=0; n<4; n++) { tank_bmp[1][n] = create_bitmap(32, 32); clear_bitmap(tank_bmp[1][n]); tanks[1].dir = n; drawtank(1); } } ///////////////////////////////////////////////////////////////////////// // setupdebris function // set up the debris on the battlefield ///////////////////////////////////////////////////////////////////////// void setupdebris() { int n,x,y,size,color; //fill the battlefield with random debris for (n = 0; n < BLOCKS; n++) { x = BLOCKSIZE + rand() % (SCREEN_W-BLOCKSIZE*2); y = BLOCKSIZE + rand() % (SCREEN_H-BLOCKSIZE*2); size = (10 + rand() % BLOCKSIZE)/2; color = makecol(rand()%255, rand()%255, rand()%255); rectfill(screen, x-size, y-size, x+size, y+size, color); } } ///////////////////////////////////////////////////////////////////////// // setupscreen function // set up the graphics mode and game screen ///////////////////////////////////////////////////////////////////////// void setupscreen() { //set video mode int ret = set_gfx_mode(MODE, WIDTH, HEIGHT, 0, 0); if (ret != 0) { allegro_message(allegro_error); return; } //print title textprintf(screen, font, 1, 1, BURST, "Tank War - %dx%d", SCREEN_W, SCREEN_H); //draw screen border rect(screen, 0, 12, SCREEN_W-1, SCREEN_H-1, TAN); rect(screen, 1, 13, SCREEN_W-2, SCREEN_H-2, TAN); } ///////////////////////////////////////////////////////////////////////// // main function // start point of the program ///////////////////////////////////////////////////////////////////////// void main(void) { //initialize everything allegro_init(); install_keyboard(); install_timer(); srand(time(NULL)); setupscreen(); setupdebris(); setuptanks(); //game loop while(!gameover) { //erase the tanks erasetank(0); erasetank(1); //check for collisions clearpath(0); clearpath(1); //move the tanks movetank(0); movetank(1); //draw the tanks blit(tank_bmp[0][tanks[0].dir], screen, 0, 0, tanks[0].x-16, tanks[0].y-16, 32, 32); blit(tank_bmp[1][tanks[1].dir], screen, 0, 0, tanks[1].x-16, tanks[1].y-16, 32, 32); //update the bullets updatebullet(0); updatebullet(1); //check for keypresses if (keypressed()) getinput(); //slow the game down (adjust as necessary) rest(30); } //end program allegro_exit(); } END_OF_MAIN();