// VazTank a 3D OpenGL game by PhilVaz // July 15, 2004 // INCLUDES /////////////////////////////////////////////// #define WIN32_LEAN_AND_MEAN #include // include all the windows headers #include // include useful macros #include // for Win multimedia #include // for rand functions #include #include #include // OpenGL32 library #include // GLU32 library #include // GLAUX library #include // include DirectSound #include "dsutil.h" // for LoadSoundBuffer() #include "VazTank.h" // sounds and icon resources // DEFINES //////////////////////////////////////////////// // defines for windows #define WINDOW_CLASS_NAME "WINCLASS1" #define WINDOW_WIDTH 640 // size of game window #define WINDOW_HEIGHT 480 #define WINDOW_BPP 16 #define GAME_SPEED 15 // speed of game approx 60 frames/sec (increase to slow down) #define GAME_STATE_DEMO_INIT 0 // demo/menu initialization state #define GAME_STATE_DEMO_RUN 1 // demo/menu running state #define GAME_STATE_GAME_INIT 2 // game initialization state #define GAME_STATE_GAME_RUN 3 // game running state #define GAME_STATE_GAME_RESET 4 // game reset state (new level) #define GAME_STATE_GAME_PAUSE 5 // game pause state (no movement/no sound?) #define TEX_FLOOR 0 #define TEX_SKY 1 #define TEX_WALL 2 #define TEX_CAMO 3 #define TEX_AMMO 4 #define TEX_FUEL 5 #define TEX_GUN 6 #define TEX_MASK 7 #define TANKS_TOTAL 5 // total number of tanks #define AMMO_TOTAL 25 // total initial ammo #define FUEL_TOTAL 9999 // total initial fuel #define TYPE_CAMO 1 #define TYPE_AMMO 2 #define TYPE_FUEL 3 #define ENEMY_TANK 1 #define ENEMY_COPTER 2 #define MAX_TEXS 8 // max textures #define MAX_LISTS 8 // max display lists #define MAX_OBJECTS 100 // max objects (camo and fuel and ammo) #define MAX_ENEMIES 25 // 4,8,12 5,10,15 6,12,18 ? #define MAX_COPTERS 4 // number of copters allowed per level #define MAX_BULLETS 5 // max player bullets #define MAX_EBULLETS 10 // max enemy bullets #define MAX_PARTICLES 2500 // max explosion particles #define MAX_VERT 5000 // max vertices and triangles allowed #define MAX_BUF 255 // max buffer chars for line reading #define MAX_LINES 61 // max lines per page (for ASC model) #define COLOR_BLK 0 // black particle explosion colors #define COLOR_WHT 1 // white #define COLOR_RED 2 // red #define COLOR_TAN 3 // tan (wood) #define COLOR_MAR 4 // maroon (dark red) #define COLOR_DGR 5 // dark green #define COLOR_BRN 6 // brown #define COLOR_YEL 7 // yellow #define EXPLODE_WALL 1 #define EXPLODE_CAMO 2 #define EXPLODE_AMMO 3 #define EXPLODE_FUEL 4 #define EXPLODE_ENEMY 5 #define EXPLODE_GROUND 6 #define TANK_SPEED_INC .001f // increment tank speed #define TANK_SPEED_MAX .7f // max speed #define TANK_SPEED_MIN .05f // min speed #define ROTATE_SPEED .2f // rotation speed #define BULLET_SPEED 3.0f // player and enemy bullet speed #define BULLET_DURATION 200 // bullet duration in frames #define GRAVITY_SPEED 0.1f // gravity for particle effects #define BULLET_PAUSE 75 // pause time between bullets #define MENU_PAUSE 6 // pause time between menu selections #define LEVEL_PAUSE 400 // pause time between levels #define EMOVE_PAUSE 50 // pause time between enemy move changes #define EFIRE_PAUSE 900 // pause time between enemy fire on reset #define MENU_SELECT_PLAY 0 #define MENU_SELECT_CONTROLS 1 #define MENU_SELECT_DIFFICULTY 2 #define MENU_SELECT_AMBIANCE 3 #define MENU_SELECT_SCORING 4 #define MENU_SELECT_QUIT 5 #define MENU_SELECT_KEYBOARD 6 #define MENU_SELECT_MOUSE 7 #define MENU_SELECT_JOY 8 #define MENU_SELECT_DIFFEASY 9 #define MENU_SELECT_DIFFNORMAL 10 #define MENU_SELECT_DIFFHARD 11 #define MENU_SELECT_AMBOFF 12 #define MENU_SELECT_AMBON 13 #define CONTROL_KEYBOARD 6 #define CONTROL_MOUSE 7 #define CONTROL_JOY 8 #define DIFFICULTY_EASY 9 // difficulties #define DIFFICULTY_NORMAL 10 #define DIFFICULTY_HARD 11 #define AMBIANCE_OFF 12 // background ambiance sound #define AMBIANCE_ON 13 #define LIMIT_FORWARD 300 // auto-camera limits during menu #define LIMIT_TURN 40 #define LIMIT_MENU 60 // MACROS ///////////////////////////////////////////////// #define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0) #define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1) // GLOBALS //////////////////////////////////////////////// HWND game_window = NULL; // global game window handle HINSTANCE game_instance = NULL; // global game instance handle HDC game_dc = NULL; // global device context (GDI) handle HGLRC game_rc = NULL; // global rendering context (OpenGL) handle bool draw_ok; // whether OpenGL init ok LPDIRECTSOUND game_sound_main = NULL; // global direct sound object and buffers LPDIRECTSOUNDBUFFER game_sound_bomb1 = NULL; LPDIRECTSOUNDBUFFER game_sound_bomb2 = NULL; LPDIRECTSOUNDBUFFER game_sound_bullet1 = NULL; LPDIRECTSOUNDBUFFER game_sound_bullet2 = NULL; LPDIRECTSOUNDBUFFER game_sound_bullet3 = NULL; LPDIRECTSOUNDBUFFER game_sound_bullet4 = NULL; LPDIRECTSOUNDBUFFER game_sound_bullet5 = NULL; LPDIRECTSOUNDBUFFER game_sound_bullet6 = NULL; LPDIRECTSOUNDBUFFER game_sound_bullet7 = NULL; LPDIRECTSOUNDBUFFER game_sound_bullet8 = NULL; LPDIRECTSOUNDBUFFER game_sound_bullet9 = NULL; LPDIRECTSOUNDBUFFER game_sound_bullet10 = NULL; LPDIRECTSOUNDBUFFER game_sound_flyby1 = NULL; LPDIRECTSOUNDBUFFER game_sound_flyby2 = NULL; LPDIRECTSOUNDBUFFER game_sound_flyby3 = NULL; LPDIRECTSOUNDBUFFER game_sound_flyby4 = NULL; LPDIRECTSOUNDBUFFER game_sound_flyby5 = NULL; LPDIRECTSOUNDBUFFER game_sound_gun1 = NULL; LPDIRECTSOUNDBUFFER game_sound_gun2 = NULL; LPDIRECTSOUNDBUFFER game_sound_gun3 = NULL; LPDIRECTSOUNDBUFFER game_sound_gun4 = NULL; LPDIRECTSOUNDBUFFER game_sound_tankgun1 = NULL; LPDIRECTSOUNDBUFFER game_sound_tankgun2 = NULL; LPDIRECTSOUNDBUFFER game_sound_tankgun3 = NULL; LPDIRECTSOUNDBUFFER game_sound_battlezone = NULL; LPDIRECTSOUNDBUFFER game_sound_engine = NULL; LPDIRECTSOUNDBUFFER game_sound_explode0 = NULL; LPDIRECTSOUNDBUFFER game_sound_explode1 = NULL; LPDIRECTSOUNDBUFFER game_sound_explode2 = NULL; LPDIRECTSOUNDBUFFER game_sound_explode3 = NULL; LPDIRECTSOUNDBUFFER game_sound_explode4 = NULL; LPDIRECTSOUNDBUFFER game_sound_explode5 = NULL; LPDIRECTSOUNDBUFFER game_sound_explode6 = NULL; LPDIRECTSOUNDBUFFER game_sound_explode7 = NULL; LPDIRECTSOUNDBUFFER game_sound_level = NULL; LPDIRECTSOUNDBUFFER game_sound_menu1 = NULL; LPDIRECTSOUNDBUFFER game_sound_menu2 = NULL; LPDIRECTSOUNDBUFFER game_sound_bonus = NULL; bool sound_ok; // whether DirectSound set okay int width = WINDOW_WIDTH; int height = WINDOW_HEIGHT; double PI = 3.14159265359; // define PI constant GLuint texture[MAX_TEXS]; // storage for textures GLuint RoomList; // storage for room display list GLuint Sky1List; // storage for sky 1 list GLuint Sky2List; // storage for sky 2 list GLuint CamoList; // storage for camo box list GLuint FuelList; // storage for fuel box list GLuint AmmoList; // storage for ammo box list GLuint EnemyList; // storage for enemy tank list GLuint CopterList; // storage for enemy copter list FILE *model_file = NULL; // file name handle GLfloat CopterVerts[MAX_VERT][3]; // holds all the vertex points of model GLint CopterTris[MAX_VERT][3]; // holds all the indices for triangles (polygons) int total_copter_tris = 0; // total number of copter triangles (polygons) int total_copter_vert = 0; // total number of copter vertices int total_copters = 0; // total number of copters on level GLuint font_base; // for game font char line[MAX_BUF]; // file line buffer int page_lines = 0; // model page line count char text[80]; // for display text output 80 chars max char menu_text[14][80]; // 14 menu selections and 80 chars GLuint Xmouse = width - 2; // mouse X position GLuint Ymouse = height / 2; // mouse Y position GLfloat sky_degrees = 0.0f; // sky rotate degrees GLfloat tank_speed = 0.0f; // tank speed to move forward/back int bullet_wait; // wait time between bullets int menu_wait; // wait time between menu selections int level_wait; // wait time between new levels int fire_wait; // wait time between enemy fire on reset int menu_select = MENU_SELECT_PLAY; // current menu selection int game_state = GAME_STATE_DEMO_INIT; // initial game state, level, total score int game_level = 1; int game_score = 0; int game_difficulty = DIFFICULTY_NORMAL; // selected difficulty and ambiance volume int game_volume = AMBIANCE_ON; int game_control = CONTROL_KEYBOARD; GLUquadricObj *game_sphere = NULL; // pointer for bullet spheres int tank_fuel = 0; // tank fuel, ammo, and player tanks left int tank_ammo = 0; int tanks_left = TANKS_TOTAL; int enemy_left; // enemy tanks to clear each level int tank_damage; // 0 = no damage, 1 = radar damaged (flicker), 2 = radar out, > 2 dead int frames_damage; // delay for damage flicker int tank_fire = 0; // tank gun sound buffers 1,2,3 int last_explode_box = 0; // last sound of box explosion int last_explode_tank = 0; // last sound of tank explosion int cam_forward, cam_turn, frames_forward, frames_turn; // auto-camera during menu int color_menu, frames_menu; // to change menu color int FPS = 0; // running frames per second int current_FPS = 0; // current calculated FPS float last_time = 0.0f; // time elapsed from last frame float current_time; // new current time typedef struct // Struct for player tank and weapon position/view { GLfloat x; GLfloat y; GLfloat z; GLfloat Look; double Angle; } PLAYER_STRUCT; PLAYER_STRUCT Player; // Player info typedef struct // Struct for bullets { bool alive; GLfloat x; GLfloat y; GLfloat z; GLfloat dx; GLfloat dy; GLfloat dz; int count; } BULLET_STRUCT; BULLET_STRUCT Bullets[MAX_BULLETS]; // up to 5 bullets at a time ? typedef struct // Struct for Enemy tanks { bool alive; GLfloat x; GLfloat y; GLfloat z; GLfloat dx; GLfloat dy; GLfloat dz; GLfloat Angle; // rotate angle on y (facing direction) int type; // type 1 = tank, 2 = copter, etc int move; // move wait to adjust position } ENEMY_STRUCT; ENEMY_STRUCT Enemy[MAX_ENEMIES]; // 4,8,12 5,10,15 6,12,18 typedef struct // Struct for enemy bullets { bool alive; GLfloat x; GLfloat y; GLfloat z; GLfloat dx; GLfloat dy; GLfloat dz; int count; } EBULLET_STRUCT; EBULLET_STRUCT EBullets[MAX_EBULLETS]; typedef struct // Struct for Object boxes { bool alive; GLfloat x; GLfloat y; GLfloat z; int type; } OBJECT_STRUCT; OBJECT_STRUCT Objects[MAX_OBJECTS]; // Object info typedef struct // Struct for Particles { bool alive; GLfloat x; GLfloat y; GLfloat z; GLfloat dx; GLfloat dy; GLfloat dz; int color; int count; } PARTICLE_STRUCT; PARTICLE_STRUCT Particles[MAX_PARTICLES]; // Particle info // global joystick variables bool joy_ok; // whether joystick found ok UINT joy_num; // number of joys JOYINFO joy_info; // joy init info UINT joy_ID; // joy ID JOYCAPS joy_caps; // joy capture info RECT joy_trip; // joy trip info DWORD joy_xcenter; // joy x axis center DWORD joy_ycenter; // joy y axis center // joy directions (1=true, 0=false) int joy_left, joy_right, joy_up, joy_down, joy_but1, joy_but2, joy_but3, joy_but4; // FUNCTIONS ////////////////////////////////////////////// bool GameInit(); void GameMain(); void GameQuit(); void MoveCamera(GLdouble steps); // move camera in position void ProcessInput(); // process all keyboard and mouse input void PlayRandomSound(); // adds background war noise void PlayRandomExplosion(int); // explosion noise (1=loud tank explosion, 0=soft) AUX_RGBImageRec *LoadBMP(char *Filename); // load BMP requires glaux library bool LoadTextures(); void CreateWorld(); void FireBullet(); void DisplayScore(); void DisplayText(int, int); void DisplayRadar(); void DisplayMenu(); void ProcessMenu(); void RenderScene(); void SetTank(); void SetEnemy(); void SetObjects(); // set camos, fuels, ammos boxes void MoveEnemy(); void MoveBullets(); void MoveEBullets(); void FireEBullet(GLfloat, GLfloat, GLfloat); void InsertParticles(GLfloat, GLfloat, GLfloat, int); void MoveParticles(); void CheckCollisions(); bool JoyInit(); void JoyQuit(); void JoyStatus(); bool SoundInit(); void SoundQuit(); bool LoadASCModel(); // loads ASC 3D model file void ReadALine(); // reads a single line from file void CalculateFPS(); // WINPROC //////////////////////////////////////////////// LRESULT CALLBACK WinProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { // this is the main message handler of the system HDC hdc; // handle to a device context PAINTSTRUCT ps; // used in WM_PAINT switch(msg) // what is the message { case WM_CREATE: { // do initialization stuff here return(0); // return success } break; case WM_PAINT: { hdc = BeginPaint(hwnd, &ps); // validate the window EndPaint(hwnd, &ps); return(0); // return success } break; case WM_DESTROY: { PostQuitMessage(0); // kill the application, sends a WM_QUIT message return(0); // return success } break; case WM_MOUSEMOVE: // check for mouse movement { if (game_state == GAME_STATE_GAME_RUN) { Xmouse = LOWORD(lparam); // Loword is X pos Ymouse = HIWORD(lparam); // Hiword is Y pos Player.Angle = (width - 1 - Xmouse) / 101.7f; // X view angle based on X pos Player.Look = (height / 2 - (float)Ymouse) / 6; // Y view angle based on Y pos if ((int)Xmouse > width - 2) SetCursorPos(1, Ymouse); // too far right wrap left if (Xmouse < 1) SetCursorPos(width - 2, Ymouse); // too far left wrap right } return(0); // return success } break; default:break; } // end switch // process any messages that we didn't take care of return (DefWindowProc(hwnd, msg, wparam, lparam)); } // end WinProc // WINMAIN //////////////////////////////////////////////// int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow) { WNDCLASSEX winclass; // this will hold the class we create HWND hwnd; // generic window handle MSG msg; // generic message // first fill in the window class structure winclass.cbSize = sizeof(WNDCLASSEX); winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW; winclass.lpfnWndProc = WinProc; winclass.cbClsExtra = 0; winclass.cbWndExtra = 0; winclass.hInstance = hinstance; winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); winclass.hCursor = LoadCursor(NULL, IDC_ARROW); winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); winclass.lpszMenuName = NULL; winclass.lpszClassName = WINDOW_CLASS_NAME; // save the game instance handle game_instance = hinstance; // register the window class if (!RegisterClassEx(&winclass)) return(0); // create the window if (!(hwnd = CreateWindowEx(NULL, // extended style WINDOW_CLASS_NAME, // class "VazTank", // title WS_POPUP | WS_VISIBLE, // use POPUP for full screen 0,0, // initial game window x,y width, // initial game width height, // initial game height NULL, // handle to parent NULL, // handle to menu hinstance, // instance of this application NULL))) // extra creation parms return(0); // save the game window handle game_window = hwnd; draw_ok = GameInit(); // game initialization function called here // enter main event loop using PeekMessage() to retrieve messages while(TRUE) { // get initial tick count to keep game speed constant DWORD start_tick = GetTickCount(); // is there a message in queue, if so get it if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { // test if this is a quit if (msg.message == WM_QUIT) break; // translate any accelerator keys TranslateMessage(&msg); // send the message to WinProc DispatchMessage(&msg); } // end if GameMain(); // game main processing function called here // check for key and send quit game if (KEYDOWN(VK_ESCAPE) || !draw_ok) SendMessage (game_window, WM_CLOSE, 0, 0); // wait until we hit correct game speed frame rate while ((GetTickCount() - start_tick) < GAME_SPEED); } // end while GameQuit(); // game quit function and clean up before exit called here return(msg.wParam); // return to Windows } // end WinMain // BEGIN GAME CODE //////////////////////////////////////// /////////////////////////////////////////////////////////// // // GAME INITIALIZATION // /////////////////////////////////////////////////////////// bool GameInit() { int i; int pf; // pixel format DEVMODE game_screen; // full screen mode HFONT game_font; // for game font chars srand(GetTickCount()); // seed the random numbers // temporary change to full screen mode ZeroMemory(&game_screen, sizeof(game_screen)); // clear out size of DEVMODE struct game_screen.dmSize = sizeof(game_screen); game_screen.dmPelsWidth = width; game_screen.dmPelsHeight = height; game_screen.dmBitsPerPel = WINDOW_BPP; game_screen.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL; ChangeDisplaySettings(&game_screen, CDS_FULLSCREEN); game_dc = GetDC(game_window); // get the GDI device context // set up the pixel format desc struct PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // size of this PFD 1, // version number PFD_DRAW_TO_WINDOW | // supports window PFD_SUPPORT_OPENGL | // supports OpenGL PFD_DOUBLEBUFFER, // support double buff PFD_TYPE_RGBA, // request RGBA format WINDOW_BPP, // select color depth 0, 0, 0, 0, 0, 0, // color bits ignored 0, // no alpha buff 0, // shift bit ignored 0, // no accum buff 0, 0, 0, 0, // accum bits ignored 16, // 16-bit Z-buff (depth buff) 0, // no stencil buff 0, // no aux buff PFD_MAIN_PLANE, // main drawing layer 0, // reserved 0, 0, 0 // layer masks ignored }; if (!(pf = ChoosePixelFormat(game_dc, &pfd))) // match the pixel format { MessageBox(game_window, "OpenGL could not be initialized -- ChoosePixelFormat Error","OpenGL Error",MB_OK); return FALSE; // error returned } if (!SetPixelFormat(game_dc, pf, &pfd)) // set the pixel format { MessageBox(game_window, "OpenGL could not be initialized -- SetPixelFormat Error","OpenGL Error",MB_OK); return FALSE; // error returned } if (!(game_rc = wglCreateContext(game_dc))) // create the rendering context { MessageBox(game_window, "OpenGL could not be initialized -- CreateContext Error","OpenGL Error",MB_OK); return FALSE; // error returned } if (!wglMakeCurrent(game_dc, game_rc)) // make it current { MessageBox(game_window, "OpenGL could not be initialized -- MakeCurrent Error","OpenGL Error",MB_OK); return FALSE; // error returned } // OpenGL Initialized Okay // set focus and set up viewport 3D perspective SetFocus(game_window); glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 1.0f, 5000.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); SetCursorPos(width - 2, height / 2); // set pointer start location MoveCamera(-10.0f); // move closer to center if (!LoadTextures()) // attempt to load all textures { MessageBox(game_window, "Sorry, Textures did not load properly","OpenGL Error",MB_OK); return FALSE; } if (!LoadASCModel()) // attempt to load the ASC model { MessageBox(game_window, "Sorry, model was not loaded properly or not a valid asc model","LoadASCModel Error",MB_OK); return FALSE; } sound_ok = SoundInit(); // Initialize DirectSound joy_ok = JoyInit(); // Initialize Joystick glClearColor(0.75f,0.75f,1.0f,0.0f); // clear screen to light blue glEnable(GL_DEPTH_TEST); // enable depth testing glClearDepth(1.0); // set depth CreateWorld(); // create world objects font_base = glGenLists(96); // create storage for 96 chars game_font = CreateFont(16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, FF_DONTCARE | DEFAULT_PITCH, "Courier"); SelectObject(game_dc, game_font); // select and set the font wglUseFontBitmaps(game_dc, 32, 96, font_base); game_sphere = gluNewQuadric(); // create storage for bullet spheres Player.y = 3.5f; // constant tank Y height position bullet_wait = BULLET_PAUSE; // reset bullet wait time menu_wait = MENU_PAUSE; // reset menu wait time // set up menu auto-camera cam_forward = 1; cam_turn = 0; frames_forward = 0; frames_turn = 0; // set up menu color color_menu = 1; frames_menu = 0; // reset particles (only needed once during init) for (i = 0; i < MAX_PARTICLES; i++) Particles[i].alive = FALSE; // store all the menu description text sprintf(menu_text[0], "Play a new game -- Press Space or Enter"); sprintf(menu_text[1], "Keyboard | Mouse | Joystick all available"); sprintf(menu_text[2], "Select the difficulty level"); sprintf(menu_text[3], "Volume of background sounds"); sprintf(menu_text[4], "Enemy Tank = points based on distance"); sprintf(menu_text[5], "Quit this game (ESC also exits)"); sprintf(menu_text[6], "Arrow Keys=Move A/Z=Look CTRL=Fire X=Stop End=Ctr"); sprintf(menu_text[7], "Mouse=Look Left=Fire Right=Fwd"); sprintf(menu_text[8], "Joystick=Look But1=Fwd But2=Fire But3=Rev But4=Stop"); sprintf(menu_text[9], "fewer enemies and less enemy accuracy"); sprintf(menu_text[10], "normal enemies and accuracy"); sprintf(menu_text[11], "most enemies and best enemy accuracy"); sprintf(menu_text[12], "Background sounds Off"); sprintf(menu_text[13], "Background sounds On"); ShowCursor(FALSE); if (sound_ok) IDirectSoundBuffer_Play(game_sound_battlezone,0,0,NULL); return TRUE; // success since previous IFs all TRUE } // END OF GameInit /////////////////////////////////////////////////////////// // // GAME MAIN LOOP AND PROCESSING // /////////////////////////////////////////////////////////// void GameMain() { int r; // random reset of objects if (!draw_ok) return; // make sure OpenGL and textures loaded properly switch(game_state) { case GAME_STATE_DEMO_INIT: { SetTank(); SetEnemy(); SetObjects(); game_state = GAME_STATE_DEMO_RUN; } break; case GAME_STATE_DEMO_RUN: { MoveEnemy(); MoveParticles(); RenderScene(); } break; case GAME_STATE_GAME_INIT: { // reset score, level, tanks, enemies to initial values game_score = 0; game_level = 1; tanks_left = TANKS_TOTAL; SetTank(); SetEnemy(); SetObjects(); game_state = GAME_STATE_GAME_RUN; } break; case GAME_STATE_GAME_RUN: { MoveBullets(); MoveEnemy(); MoveEBullets(); MoveParticles(); CheckCollisions(); RenderScene(); // if 'p' key pressed, set state to GAME_PAUSE if (KEYDOWN(80)) { game_state = GAME_STATE_GAME_PAUSE; if (sound_ok) IDirectSoundBuffer_Stop(game_sound_engine); } } break; case GAME_STATE_GAME_RESET: { game_level++; SetEnemy(); // normal reset of objects at levels 5 10 if (game_level == 5 || game_level == 10) SetObjects(); if (game_level > 10) // after level 10 make 1 in 10 chance of reset objects { r = rand()%10; if (r == 5) SetObjects(); } game_state = GAME_STATE_GAME_RUN; } break; case GAME_STATE_GAME_PAUSE: { // if key pressed resume game if (KEYDOWN(VK_SPACE)) game_state = GAME_STATE_GAME_RUN; } break; } // end switch } // END OF GameMain /////////////////////////////////////////////////////////// // // GAME QUIT AND CLEAN UP // /////////////////////////////////////////////////////////// void GameQuit() { SoundQuit(); // turn off DirectSound and release buffers JoyQuit(); // release Joystick if (font_base) glDeleteLists(font_base, 96); // delete the game font if (game_sphere) gluDeleteQuadric(game_sphere); // delete bullet sphere if (game_rc) // check if rendering context (OpenGL) exists { glDeleteLists(RoomList, MAX_LISTS); // delete the world call lists wglMakeCurrent(NULL,NULL); wglDeleteContext(game_rc); // delete rendering context } // release the device context (GDI) from the game window ReleaseDC(game_window, game_dc); // return to original display settings ChangeDisplaySettings(NULL,NULL); } // END OF GameQuit ///////////////////////////////////////// // Moves camera in current direction ///////////////////////////////////////// void MoveCamera(GLdouble steps) { GLdouble xd,zd; // x and z delta xd = steps * cos(Player.Angle); // moves on X plane in player direction zd = -steps * sin(Player.Angle); // moves on Z plane in player direction Player.x += (GLfloat)xd; // do the move on X Player.z += (GLfloat)zd; // do the move on Z if (Player.x > 990.0f) Player.x = 990.0f; // limit on +X if (Player.x < -990.0f) Player.x = -990.0f; // limit on -X if (Player.z > 990.0f) Player.z = 990.0f; // limit on +Z if (Player.z < -990.0f) Player.z = -990.0f; // limit on -Z } // END OF MoveCamera //////////////////////////////////////////// // Process key and mouse input //////////////////////////////////////////// void ProcessInput() { int r; // for menu random camera if (joy_ok) JoyStatus(); // get status and position of Joystick if (game_state == GAME_STATE_DEMO_RUN) ProcessMenu(); if (frames_forward > LIMIT_FORWARD) // reached auto-cam forward limit? { frames_forward = 0; r = rand()%9; // pick random direction change if (r == 0 || r == 1) cam_turn = 1; if (r == 2 || r == 3) cam_turn = -1; if (r == 4) cam_forward = 1; if (r == 5) cam_forward = -1; } if (frames_turn > LIMIT_TURN) // reached auto-cam turn limit? { frames_turn = 0; cam_turn = 0; } // up arrow or right mouse increases speed forward if ((game_state == GAME_STATE_GAME_RUN && (KEYDOWN(VK_UP) || KEYDOWN(VK_RBUTTON) || joy_but1)) || (cam_forward == 1 && game_state == GAME_STATE_DEMO_RUN)) { if (tank_speed == 0) tank_speed = TANK_SPEED_MIN; tank_speed += TANK_SPEED_INC; if (tank_speed > TANK_SPEED_MAX) tank_speed = TANK_SPEED_MAX; frames_forward++; } // down arrow decreases speed back else if ((game_state == GAME_STATE_GAME_RUN && (KEYDOWN(VK_DOWN) || joy_but3)) || (cam_forward == -1 && game_state == GAME_STATE_DEMO_RUN)) { if (tank_speed == 0) tank_speed = -TANK_SPEED_MIN; tank_speed -= TANK_SPEED_INC; if (tank_speed < -TANK_SPEED_MAX) tank_speed = -TANK_SPEED_MAX; frames_forward++; } if (game_state == GAME_STATE_GAME_RUN && (KEYDOWN(88) || joy_but4)) // X or But4 is Stop { tank_speed = 0; if (sound_ok) IDirectSoundBuffer_Stop(game_sound_engine); } // if we have speed, move and play engine sound, unless out of fuel or in demo state if ((tank_fuel > 0 && tank_speed != 0) || game_state == GAME_STATE_DEMO_RUN) { MoveCamera(tank_speed); if (game_state == GAME_STATE_GAME_RUN) { if (sound_ok) IDirectSoundBuffer_Play(game_sound_engine,0,0,DSBPLAY_LOOPING); tank_fuel--; if (tank_fuel==900 || tank_fuel==800 || tank_fuel==700 || tank_fuel==600 || tank_fuel==500 || tank_fuel==400 || tank_fuel==300 || tank_fuel==200 || tank_fuel==100) { if (sound_ok) IDirectSoundBuffer_Play(game_sound_menu1,0,0,NULL); } } } else { tank_speed = 0; if (sound_ok) IDirectSoundBuffer_Stop(game_sound_engine); } // end else if ((game_state == GAME_STATE_GAME_RUN && (KEYDOWN(VK_RIGHT) || joy_right)) || (cam_turn == 1 && game_state == GAME_STATE_DEMO_RUN)) // right arrow turns player right { Player.Angle -= ROTATE_SPEED / (PI*PI); if (Player.Angle < 0.0f) Player.Angle = 6.27f; // avoids errors SetCursorPos(width - 1 - ((int)(Player.Angle * 101.7f)), Ymouse); // reset cursor frames_turn++; } else if ((game_state == GAME_STATE_GAME_RUN && (KEYDOWN(VK_LEFT) || joy_left)) || (cam_turn == -1 && game_state == GAME_STATE_DEMO_RUN)) // left arrow turns player left { Player.Angle += ROTATE_SPEED / (PI*PI); if (Player.Angle > (2.0f * PI)) Player.Angle = 0.03f; // avoids errors SetCursorPos(width - 1 - ((int)(Player.Angle * 101.7f)), Ymouse); // reset cursor frames_turn++; } if (game_state == GAME_STATE_GAME_RUN && (KEYDOWN(90) || joy_down)) // Z looks down { Player.Look -= 1.0f; if (Player.Look < -40.0f) Player.Look = -40.0f; // limit angle down SetCursorPos(Xmouse, height/2 - ((int)Player.Look * 6)); // reset cursor } else if (game_state == GAME_STATE_GAME_RUN && (KEYDOWN(65) || joy_up)) // A looks up { Player.Look += 1.0f; if (Player.Look > 40.0f) Player.Look = 40.0f; // limit amgle up SetCursorPos(Xmouse, height/2 - ((int)Player.Look * 6)); // reset cursor } else if (game_state == GAME_STATE_GAME_RUN && KEYDOWN(VK_END)) // end key is center { Player.Look = 0.0f; // center the view SetCursorPos(Xmouse, height/2 - ((int)Player.Look * 6)); // reset cursor } if (game_state == GAME_STATE_GAME_RUN && (KEYDOWN(VK_CONTROL) || KEYDOWN(VK_LBUTTON) || joy_but2)) FireBullet(); // fire a bullet } // END OF ProcessInput /////////////////////////////// // Load a BMP Image /////////////////////////////// AUX_RGBImageRec *LoadBMP(char *Filename) { FILE *File = NULL; // file handle if (!Filename) return NULL; // make sure there is a file name File = fopen(Filename,"r"); // attempt to read the file if (File) // if file exists, close and return the handle { fclose(File); return auxDIBImageLoad(Filename); } return NULL; // load failed } // END OF LoadBMP ///////////////////////////////////////// // Load and convert BMPs to Textures ///////////////////////////////////////// bool LoadTextures() { bool Status = FALSE; // status indicator int i; AUX_RGBImageRec *TextureImage[MAX_TEXS]; // create storage space memset(TextureImage,0,sizeof(void *) * MAX_TEXS); // initialize pointer to NULL // load BMPs checking for errors if ((TextureImage[TEX_FLOOR] = LoadBMP("Floor.bmp")) && (TextureImage[TEX_SKY] = LoadBMP("Sky.bmp")) && (TextureImage[TEX_WALL] = LoadBMP("Wall.bmp")) && (TextureImage[TEX_CAMO] = LoadBMP("Camo.bmp")) && (TextureImage[TEX_FUEL] = LoadBMP("Fuel.bmp")) && (TextureImage[TEX_AMMO] = LoadBMP("Ammo.bmp")) && (TextureImage[TEX_GUN] = LoadBMP("Gun.bmp")) && (TextureImage[TEX_MASK] = LoadBMP("Mask.bmp"))) { Status = TRUE; // success in reading in BMPs glGenTextures(MAX_TEXS, &texture[0]); // create the texture for (i = 0; i < MAX_TEXS; i++) { glBindTexture(GL_TEXTURE_2D, texture[i]); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[i]->sizeX, TextureImage[i]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[i]->data); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); if (TextureImage[i]) // if texture exists, free image and struct { if (TextureImage[i]->data) free(TextureImage[i]->data); free(TextureImage[i]); } // end if } // end for } // end if LoadBMP return Status; } // END OF LoadTextures ////////////////////////////////////////////////////////// // Create all world objects and generate display lists ////////////////////////////////////////////////////////// void CreateWorld() { int i; // loop for model triangles // Generate 8 display lists (room, 2 sky layers, objects, enemies) RoomList = glGenLists(MAX_LISTS); Sky1List = RoomList + 1; Sky2List = Sky1List + 1; CamoList = Sky2List + 1; FuelList = CamoList + 1; AmmoList = FuelList + 1; EnemyList = AmmoList + 1; CopterList = EnemyList + 1; // create the room, 4 walls and a floor glNewList(RoomList,GL_COMPILE); glEnable(GL_CULL_FACE); // remove hidden faces glEnable(GL_TEXTURE_2D); // enable texture map glBindTexture(GL_TEXTURE_2D, texture[TEX_FLOOR]); // use floor texture glBegin(GL_QUADS); // floor (clockwise) glNormal3f( 0.0f, 1.0f, 0.0f); // normal points up Y axis glTexCoord2f( 0.0f, 0.0f); glVertex3f(-1000.0f,-2.0f, 1000.0f); glTexCoord2f(50.0f, 0.0f); glVertex3f( 1000.0f,-2.0f, 1000.0f); glTexCoord2f(50.0f,50.0f); glVertex3f( 1000.0f,-2.0f,-1000.0f); glTexCoord2f( 0.0f,50.0f); glVertex3f(-1000.0f,-2.0f,-1000.0f); glEnd(); glBindTexture(GL_TEXTURE_2D, texture[TEX_WALL]); // use wall texture glBegin(GL_QUADS); // front wall (cw) glNormal3f( 0.0f, 0.0f, -1.0f); // normal points into screen glTexCoord2f( 0.0f, 0.0f); glVertex3f( 1000.0f,-2.0f, 1000.0f); glTexCoord2f(50.0f, 0.0f); glVertex3f(-1000.0f,-2.0f, 1000.0f); glTexCoord2f(50.0f, 1.0f); glVertex3f(-1000.0f, 6.0f, 1000.0f); glTexCoord2f( 0.0f, 1.0f); glVertex3f( 1000.0f, 6.0f, 1000.0f); // back wall (ccw) glNormal3f( 0.0f, 0.0f, 1.0f); // normal points toward viewer glTexCoord2f( 0.0f, 0.0f); glVertex3f(-1000.0f,-2.0f,-1000.0f); glTexCoord2f(50.0f, 0.0f); glVertex3f( 1000.0f,-2.0f,-1000.0f); glTexCoord2f(50.0f, 1.0f); glVertex3f( 1000.0f, 6.0f,-1000.0f); glTexCoord2f( 0.0f, 1.0f); glVertex3f(-1000.0f, 6.0f,-1000.0f); // right wall (cw) glNormal3f(-1.0f, 0.0f, 0.0f); // normal points left glTexCoord2f( 0.0f, 0.0f); glVertex3f( 1000.0f,-2.0f,-1000.0f); glTexCoord2f(50.0f, 0.0f); glVertex3f( 1000.0f,-2.0f, 1000.0f); glTexCoord2f(50.0f, 1.0f); glVertex3f( 1000.0f, 6.0f, 1000.0f); glTexCoord2f( 0.0f, 1.0f); glVertex3f( 1000.0f, 6.0f,-1000.0f); // left wall (ccw) glNormal3f( 0.0f, 1.0f, 0.0f); // normal points up glTexCoord2f( 0.0f, 0.0f); glVertex3f(-1000.0f,-2.0f, 1000.0f); glTexCoord2f(50.0f, 0.0f); glVertex3f(-1000.0f,-2.0f,-1000.0f); glTexCoord2f(50.0f, 1.0f); glVertex3f(-1000.0f, 6.0f,-1000.0f); glTexCoord2f( 0.0f, 1.0f); glVertex3f(-1000.0f, 6.0f, 1000.0f); glEnd(); glDisable(GL_TEXTURE_2D); // disable texturing glDisable(GL_CULL_FACE); // disable face culling glEndList(); // create sky layer 1 glNewList(Sky1List,GL_COMPILE); glEnable(GL_BLEND); // enable blending glEnable(GL_TEXTURE_2D); // enable texturing glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA); // set blend mode glBindTexture(GL_TEXTURE_2D, texture[TEX_SKY]); // use sky texture glBegin(GL_QUADS); glNormal3f(0.0f,-1.0f,0.0f); // normal points down glTexCoord2f(0.0f, 0.0f); glVertex3f( 25000.0f, 90.0f, 25000.0f); glTexCoord2f(2.0f, 0.0f); glVertex3f(-25000.0f, 90.0f, 25000.0f); glTexCoord2f(2.0f, 2.0f); glVertex3f(-25000.0f, 90.0f, -25000.0f); glTexCoord2f(0.0f, 2.0f); glVertex3f( 25000.0f, 90.0f, -25000.0f); glEnd(); glDisable(GL_TEXTURE_2D); // disable texturing glDisable(GL_BLEND); // disable blending glEndList(); // create sky layer 2, up higher than sky 1 glNewList(Sky2List,GL_COMPILE); glEnable(GL_TEXTURE_2D); // enable texturing glBindTexture(GL_TEXTURE_2D, texture[TEX_SKY]); // use sky texture glBegin(GL_QUADS); glNormal3f(0.0f,-1.0f,0.0f); // normal points down glTexCoord2f(0.0f, 0.0f); glVertex3f( 25000.0f, 100.0f, 25000.0f); glTexCoord2f(3.0f, 0.0f); glVertex3f(-25000.0f, 100.0f, 25000.0f); glTexCoord2f(3.0f, 3.0f); glVertex3f(-25000.0f, 100.0f, -25000.0f); glTexCoord2f(0.0f, 3.0f); glVertex3f( 25000.0f, 100.0f, -25000.0f); glEnd(); glDisable(GL_TEXTURE_2D); // disable texturing glEndList(); // draw some 3D ground objects // CAMOS glNewList(CamoList,GL_COMPILE); glEnable(GL_CULL_FACE); // remove hidden faces glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texture[TEX_CAMO]); // use camo texture glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f( 7, 7, 7); // face 1 glTexCoord2f(2.0f, 0.0f); glVertex3f(-7, 7, 7); glTexCoord2f(2.0f, 2.0f); glVertex3f(-7,-1, 7); glTexCoord2f(0.0f, 2.0f); glVertex3f( 7,-1, 7); glTexCoord2f(0.0f, 0.0f); glVertex3f( 7,-1,-7); // face 2 glTexCoord2f(2.0f, 0.0f); glVertex3f(-7,-1,-7); glTexCoord2f(2.0f, 2.0f); glVertex3f(-7, 7,-7); glTexCoord2f(0.0f, 2.0f); glVertex3f( 7, 7,-7); glTexCoord2f(0.0f, 0.0f); glVertex3f(-7, 7, 7); // face 3 glTexCoord2f(2.0f, 0.0f); glVertex3f(-7, 7,-7); glTexCoord2f(2.0f, 2.0f); glVertex3f(-7,-1,-7); glTexCoord2f(0.0f, 2.0f); glVertex3f(-7,-1, 7); glTexCoord2f(0.0f, 0.0f); glVertex3f( 7, 7,-7); // face 4 glTexCoord2f(2.0f, 0.0f); glVertex3f( 7, 7, 7); glTexCoord2f(2.0f, 2.0f); glVertex3f( 7,-1, 7); glTexCoord2f(0.0f, 2.0f); glVertex3f( 7,-1,-7); glEnd(); glDisable(GL_TEXTURE_2D); // disable texturing glDisable(GL_CULL_FACE); // disable face culling glEndList(); // FUELS glNewList(FuelList,GL_COMPILE); glEnable(GL_CULL_FACE); // remove hidden faces glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texture[TEX_FUEL]); // use fuel texture glBegin(GL_TRIANGLES); glTexCoord2f(0.0f, 0.0f); glVertex3f( 0, 7, 0); // pyramid face 1 glTexCoord2f(1.0f, 0.0f); glVertex3f(-7,-7, 7); glTexCoord2f(0.0f, 1.0f); glVertex3f( 7,-7, 7); glTexCoord2f(0.0f, 0.0f); glVertex3f( 0, 7, 0); // pyramid face 2 glTexCoord2f(1.0f, 0.0f); glVertex3f( 7,-7, 7); glTexCoord2f(0.0f, 1.0f); glVertex3f( 7,-7,-7); glTexCoord2f(0.0f, 0.0f); glVertex3f( 0, 7, 0); // pyramid face 3 glTexCoord2f(1.0f, 0.0f); glVertex3f( 7,-7,-7); glTexCoord2f(0.0f, 1.0f); glVertex3f(-7,-7,-7); glTexCoord2f(0.0f, 0.0f); glVertex3f( 0, 7, 0); // pyramid face 4 glTexCoord2f(1.0f, 0.0f); glVertex3f(-7,-7,-7); glTexCoord2f(0.0f, 1.0f); glVertex3f(-7,-7, 7); glEnd(); glDisable(GL_TEXTURE_2D); // disable texturing glDisable(GL_CULL_FACE); // disable face culling glEndList(); // AMMOS glNewList(AmmoList,GL_COMPILE); glEnable(GL_CULL_FACE); // remove hidden faces glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texture[TEX_AMMO]); // use ammo texture glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f( 7, 7, 7); // face 1 glTexCoord2f(1.0f, 0.0f); glVertex3f(-7, 7, 7); glTexCoord2f(1.0f, 1.0f); glVertex3f(-7,-1, 7); glTexCoord2f(0.0f, 1.0f); glVertex3f( 7,-1, 7); glTexCoord2f(0.0f, 0.0f); glVertex3f( 7,-1,-7); // face 2 glTexCoord2f(1.0f, 0.0f); glVertex3f(-7,-1,-7); glTexCoord2f(1.0f, 1.0f); glVertex3f(-7, 7,-7); glTexCoord2f(0.0f, 1.0f); glVertex3f( 7, 7,-7); glTexCoord2f(0.0f, 0.0f); glVertex3f(-7, 7, 7); // face 3 glTexCoord2f(1.0f, 0.0f); glVertex3f(-7, 7,-7); glTexCoord2f(1.0f, 1.0f); glVertex3f(-7,-1,-7); glTexCoord2f(0.0f, 1.0f); glVertex3f(-7,-1, 7); glTexCoord2f(0.0f, 0.0f); glVertex3f( 7, 7,-7); // face 4 glTexCoord2f(1.0f, 0.0f); glVertex3f( 7, 7, 7); glTexCoord2f(1.0f, 1.0f); glVertex3f( 7,-1, 7); glTexCoord2f(0.0f, 1.0f); glVertex3f( 7,-1,-7); glEnd(); glDisable(GL_TEXTURE_2D); // disable texturing glDisable(GL_CULL_FACE); // disable face culling glEndList(); // ENEMY TANK glNewList(EnemyList,GL_COMPILE); glEnable(GL_CULL_FACE); // remove hidden faces glBegin(GL_QUADS); // draw tank bottom glColor3f(0.37f, 0.37f, 0.0f); glVertex3f( 6, 4, 6); glVertex3f(-6, 4, 6); glVertex3f(-6,-1, 6); glVertex3f( 6,-1, 6); glColor3f(0.37f, 0.37f, 0.0f); glVertex3f( 6,-1,-6); glVertex3f(-6,-1,-6); glVertex3f(-6, 4,-6); glVertex3f( 6, 4,-6); glColor3f(0.33f, 0.41f, 0.0f); glVertex3f(-6, 4, 6); glVertex3f(-6, 4,-6); glVertex3f(-6,-1,-6); glVertex3f(-6,-1, 6); glColor3f(0.33f, 0.41f, 0.0f); glVertex3f( 6, 4,-6); glVertex3f( 6, 4, 6); glVertex3f( 6,-1, 6); glVertex3f( 6,-1,-6); // draw tank top (turret) glColor3f(0.33f, 0.41f, 0.0f); glVertex3f( 2, 6, 2); glVertex3f(-2, 6, 2); glVertex3f(-2, 4, 2); glVertex3f( 2, 4, 2); glColor3f(0.33f, 0.41f, 0.0f); glVertex3f( 2, 4,-2); glVertex3f(-2, 4,-2); glVertex3f(-2, 6,-2); glVertex3f( 2, 6,-2); glColor3f(0.37f, 0.37f, 0.0f); glVertex3f(-2, 6, 2); glVertex3f(-2, 6,-2); glVertex3f(-2, 4,-2); glVertex3f(-2, 4, 2); glColor3f(0.37f, 0.37f, 0.0f); glVertex3f( 2, 6,-2); glVertex3f( 2, 6, 2); glVertex3f( 2, 4, 2); glVertex3f( 2, 4,-2); glEnd(); glDisable(GL_CULL_FACE); // disable face culling glColor3f(1.0f, 1.0f, 1.0f); // reset color to white glEndList(); // ENEMY COPTER glNewList(CopterList,GL_COMPILE); glEnable(GL_CULL_FACE); // remove hidden faces glBegin(GL_TRIANGLES); for (i = 0; i < total_copter_tris; i++) { glVertex3fv(&CopterVerts[CopterTris[i][0]][0]); glVertex3fv(&CopterVerts[CopterTris[i][1]][0]); glVertex3fv(&CopterVerts[CopterTris[i][2]][0]); } glEnd(); glDisable(GL_CULL_FACE); // disable face culling glEndList(); } // END OF CreateWorld /////////////////////////////////////// // Initialize Joystick /////////////////////////////////////// bool JoyInit() { // clear joystick status joy_left = 0; joy_right = 0; joy_up = 0; joy_down = 0; joy_but1 = 0; joy_but2 = 0; joy_but3 = 0; joy_but4 = 0; // make sure joystick driver is present if ((joy_num = joyGetNumDevs()) == 0) return FALSE; // make sure the joystick is attached if (joyGetPos(JOYSTICKID1, &joy_info) != JOYERR_UNPLUGGED) joy_ID = JOYSTICKID1; else return FALSE; // calculate the trip values joyGetDevCaps(joy_ID, &joy_caps, sizeof(JOYCAPS)); joy_xcenter = ((DWORD)joy_caps.wXmin + joy_caps.wXmax) / 2; joy_ycenter = ((DWORD)joy_caps.wYmin + joy_caps.wYmax) / 2; joy_trip.left = (joy_caps.wXmin + (WORD)joy_xcenter) / 2; joy_trip.right = (joy_caps.wXmax + (WORD)joy_xcenter) / 2; joy_trip.top = (joy_caps.wYmin + (WORD)joy_ycenter) / 2; joy_trip.bottom = (joy_caps.wYmax + (WORD)joy_ycenter) / 2; // capture the joystick joySetCapture(game_window, joy_ID, NULL, TRUE); return TRUE; } // END OF JoyInit ///////////////////////////////////////////// // Quit and Release Joystick ///////////////////////////////////////////// void JoyQuit() { // release joystick if (joy_ok) joyReleaseCapture(joy_ID); } // END OF JoyQuit /////////////////////////////////////////////// // Get Joystick Status and Position /////////////////////////////////////////////// void JoyStatus() { if (joy_ok && joyGetPos(joy_ID, &joy_info) == JOYERR_NOERROR) { // if we have no errors check the joystick position // check horizontal movement joy_left = 0; joy_right = 0; if (joy_info.wXpos < (WORD)joy_trip.left) joy_left = 1; else if (joy_info.wXpos > (WORD)joy_trip.right) joy_right = 1; // check vertical movement joy_up = 0; joy_down = 0; if (joy_info.wYpos < (WORD)joy_trip.top) joy_up = 1; else if (joy_info.wYpos > (WORD)joy_trip.bottom) joy_down = 1; // check four buttons joy_but1 = 0; joy_but2 = 0; joy_but3 = 0; joy_but4 = 0; if (joy_info.wButtons & JOY_BUTTON1) joy_but1 = 1; if (joy_info.wButtons & JOY_BUTTON2) joy_but2 = 1; if (joy_info.wButtons & JOY_BUTTON3) joy_but3 = 1; if (joy_info.wButtons & JOY_BUTTON4) joy_but4 = 1; } // end of if joy_ok AND NOERROR } // END OF JoyStatus /////////////////////////////////////////////////////// // Initialize DirectSound and individual sound buffers /////////////////////////////////////////////////////// bool SoundInit() { if (DirectSoundCreate(NULL, &game_sound_main, NULL) != DS_OK) { MessageBox(game_window, "Sound could not be initialized -- Direct Sound Create Error","Sound Error",MB_OK); return FALSE; } // // Direct Sound object succeeded so set coop level // if (IDirectSound_SetCooperativeLevel(game_sound_main, game_window, DSSCL_PRIORITY) != DS_OK) { MessageBox(game_window, "Sound could not be initialized -- Cooperative Level Error","Sound Error",MB_OK); return FALSE; } // // Cooperative Level succeeded so set up // secondary buffers and load wave file sound resources // game_sound_bomb1 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_BOMB1)); game_sound_bomb2 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_BOMB2)); game_sound_bullet1 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_BULLET1)); game_sound_bullet2 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_BULLET2)); game_sound_bullet3 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_BULLET3)); game_sound_bullet4 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_BULLET4)); game_sound_bullet5 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_BULLET5)); game_sound_bullet6 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_BULLET6)); game_sound_bullet7 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_BULLET7)); game_sound_bullet8 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_BULLET8)); game_sound_bullet9 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_BULLET9)); game_sound_bullet10 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_BULLET10)); game_sound_flyby1 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_FLYBY1)); game_sound_flyby2 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_FLYBY2)); game_sound_flyby3 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_FLYBY3)); game_sound_flyby4 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_FLYBY4)); game_sound_flyby5 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_FLYBY5)); game_sound_gun1 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_GUN1)); game_sound_gun2 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_GUN2)); game_sound_gun3 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_GUN3)); game_sound_gun4 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_GUN4)); game_sound_tankgun1 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_TANKGUN1)); game_sound_tankgun2 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_TANKGUN2)); game_sound_tankgun3 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_TANKGUN3)); game_sound_battlezone = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_BATTLEZONE)); game_sound_engine = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_ENGINE)); game_sound_explode0 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_EXPLODE0)); game_sound_explode1 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_EXPLODE1)); game_sound_explode2 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_EXPLODE2)); game_sound_explode3 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_EXPLODE3)); game_sound_explode4 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_EXPLODE4)); game_sound_explode5 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_EXPLODE5)); game_sound_explode6 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_EXPLODE6)); game_sound_explode7 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_EXPLODE7)); game_sound_level = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_LEVEL)); game_sound_menu1 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_MENU1)); game_sound_menu2 = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_MENU2)); game_sound_bonus = DSLoadSoundBuffer(game_sound_main, MAKEINTRESOURCE(SOUND_BONUS)); // return TRUE since main DirectSound initialized ok return TRUE; } // END OF SoundInit ////////////////////////////////////////////////////// // Stop all sounds and close DirectSound and buffers ///////////////////////////////////////////////////// void SoundQuit() { // if sound was set okay, release DirectSound objects if (game_sound_main) { // FIRST RELEASE SECONDARY BUFFERS IDirectSound_Release(game_sound_bonus); IDirectSound_Release(game_sound_menu2); IDirectSound_Release(game_sound_menu1); IDirectSound_Release(game_sound_level); IDirectSound_Release(game_sound_explode7); IDirectSound_Release(game_sound_explode6); IDirectSound_Release(game_sound_explode5); IDirectSound_Release(game_sound_explode4); IDirectSound_Release(game_sound_explode3); IDirectSound_Release(game_sound_explode2); IDirectSound_Release(game_sound_explode1); IDirectSound_Release(game_sound_explode0); IDirectSound_Release(game_sound_engine); IDirectSound_Release(game_sound_battlezone); IDirectSound_Release(game_sound_tankgun3); IDirectSound_Release(game_sound_tankgun2); IDirectSound_Release(game_sound_tankgun1); IDirectSound_Release(game_sound_gun4); IDirectSound_Release(game_sound_gun3); IDirectSound_Release(game_sound_gun2); IDirectSound_Release(game_sound_gun1); IDirectSound_Release(game_sound_flyby5); IDirectSound_Release(game_sound_flyby4); IDirectSound_Release(game_sound_flyby3); IDirectSound_Release(game_sound_flyby2); IDirectSound_Release(game_sound_flyby1); IDirectSound_Release(game_sound_bullet10); IDirectSound_Release(game_sound_bullet9); IDirectSound_Release(game_sound_bullet8); IDirectSound_Release(game_sound_bullet7); IDirectSound_Release(game_sound_bullet6); IDirectSound_Release(game_sound_bullet5); IDirectSound_Release(game_sound_bullet4); IDirectSound_Release(game_sound_bullet3); IDirectSound_Release(game_sound_bullet2); IDirectSound_Release(game_sound_bullet1); IDirectSound_Release(game_sound_bomb2); IDirectSound_Release(game_sound_bomb1); // THEN RELEASE MAIN DIRECT SOUND OBJECT IDirectSound_Release(game_sound_main); } } // END OF SoundQuit ////////////////////////////////////////////////////////////// // Plays occasional random sound for background noise ////////////////////////////////////////////////////////////// void PlayRandomSound() { int r,s; // random numbers if (!sound_ok || game_state != GAME_STATE_GAME_RUN || game_volume == AMBIANCE_OFF) return; r = (rand()%1000); // pick a number 0-999 if (r > 8 && r < 12) // if 9-11 pick random gun sound 1-4 { s = (rand()%4); if (s == 0) IDirectSoundBuffer_Play(game_sound_gun1,0,0,NULL); if (s == 1) IDirectSoundBuffer_Play(game_sound_gun2,0,0,NULL); if (s == 2) IDirectSoundBuffer_Play(game_sound_gun3,0,0,NULL); if (s == 3) IDirectSoundBuffer_Play(game_sound_gun4,0,0,NULL); } if (r == 4) // if 4 pick flyby 1-5 { s = (rand()%10); if (s < 4) IDirectSoundBuffer_Play(game_sound_flyby3,0,0,NULL); // copter sound if (s == 4) IDirectSoundBuffer_Play(game_sound_flyby1,0,0,NULL); if (s == 5) IDirectSoundBuffer_Play(game_sound_flyby2,0,0,NULL); if (s == 6 || s == 7) IDirectSoundBuffer_Play(game_sound_flyby4,0,0,NULL); if (s == 8 || s == 9) IDirectSoundBuffer_Play(game_sound_flyby5,0,0,NULL); } if (r < 2) // if 0-1 pick random bomb sound { s = (rand()%5); if (s == 0) IDirectSoundBuffer_Play(game_sound_bomb1,0,0,NULL); if (s == 1) IDirectSoundBuffer_Play(game_sound_bomb2,0,0,NULL); } } // END OF PlayRandomSound ////////////////////////////////////////// // Play Random Explosion noise ////////////////////////////////////////// void PlayRandomExplosion(int v) { int s; if (!sound_ok || game_state != GAME_STATE_GAME_RUN) return; // volume v = 0 for box, v = 1 for loud enemy explosion, v = 2 for tank hits if (v == 0) { do { s = (rand()%4); } while (last_explode_box == s); if (s == 0) IDirectSoundBuffer_Play(game_sound_explode1,0,0,NULL); if (s == 1) IDirectSoundBuffer_Play(game_sound_explode2,0,0,NULL); if (s == 2) IDirectSoundBuffer_Play(game_sound_explode3,0,0,NULL); if (s == 3) IDirectSoundBuffer_Play(game_sound_explode4,0,0,NULL); last_explode_box = s; } if (v == 1) { do { s = (rand()%5); } while (last_explode_tank == s); if (s == 0) IDirectSoundBuffer_Play(game_sound_explode3,0,0,NULL); if (s == 1) IDirectSoundBuffer_Play(game_sound_explode4,0,0,NULL); if (s == 2) IDirectSoundBuffer_Play(game_sound_explode5,0,0,NULL); if (s == 3) IDirectSoundBuffer_Play(game_sound_explode6,0,0,NULL); if (s == 4) IDirectSoundBuffer_Play(game_sound_explode7,0,0,NULL); last_explode_tank = s; } if (v == 2) { s = (rand()%3); if (s == 0) IDirectSoundBuffer_Play(game_sound_bullet7,0,0,NULL); if (s == 1) IDirectSoundBuffer_Play(game_sound_bullet8,0,0,NULL); if (s == 2) IDirectSoundBuffer_Play(game_sound_bullet9,0,0,NULL); } } // END OF PlayRandomExplosion ////////////////////////////////////////////////// // Fires a tank bullet ////////////////////////////////////////////////// void FireBullet() { int i; // for loop bullet_wait--; if (bullet_wait > 0) return; bullet_wait = BULLET_PAUSE; if (tank_ammo == 0) { if (sound_ok) IDirectSoundBuffer_Play(game_sound_menu2,0,0,NULL); return; } for (i = 0; i < MAX_BULLETS; i++) // find a free bullet to insert { if (!Bullets[i].alive) // found a free one { Bullets[i].alive = TRUE; Bullets[i].x = Player.x; Bullets[i].y = Player.y; Bullets[i].z = Player.z; Bullets[i].dx = BULLET_SPEED * (GLfloat)cos(Player.Angle); // x direction Bullets[i].dy = Player.Look / 16.5f; // y direction Bullets[i].dz = -BULLET_SPEED * (GLfloat)sin(Player.Angle); // z direction Bullets[i].x += (Bullets[i].dx * 1.5f); // move bullet forward a bit Bullets[i].y += (Bullets[i].dy * 1.5f); Bullets[i].z += (Bullets[i].dz * 1.5f); Bullets[i].count = BULLET_DURATION; tank_ammo--; if (sound_ok) { tank_fire++; if (tank_fire > 3) tank_fire = 1; // loop thru sound buffers if (tank_fire == 1) IDirectSoundBuffer_Play(game_sound_tankgun1,0,0,NULL); if (tank_fire == 2) IDirectSoundBuffer_Play(game_sound_tankgun2,0,0,NULL); if (tank_fire == 3) IDirectSoundBuffer_Play(game_sound_tankgun3,0,0,NULL); } return; } // end if not alive } // end for } // END OF FireBullet //////////////////////////////////////////////////////// // Displays 2D text at x,y //////////////////////////////////////////////////////// void DisplayText(int x, int y) { glRasterPos2i(x,y); glPushAttrib(GL_LIST_BIT); glListBase(font_base - 32); glCallLists(strlen(text), GL_UNSIGNED_BYTE, text); glPopAttrib(); } // END OF DisplayText //////////////////////////////////////////////////////// // Displays the score and other at bottom of screen //////////////////////////////////////////////////////// void DisplayScore() { int x = 63, y = 128; // gun turret size // set the text string and display text glMatrixMode(GL_PROJECTION); glPushMatrix(); // save proj matrix glLoadIdentity(); // reset glOrtho(0, width, height, 0, -1, 1); // set up 2D display glMatrixMode(GL_MODELVIEW); glPushMatrix(); // save model matrix glLoadIdentity(); // reset sprintf(text,"FUEL=%d", tank_fuel); DisplayText(30, 20); sprintf(text,"AMMO=%d", tank_ammo); DisplayText(135, 20); sprintf(text,"TANKS=%d", tanks_left); DisplayText(280, 20); sprintf(text,"LEVEL=%d", game_level); DisplayText(380, 20); sprintf(text,"SCORE=%d", game_score); DisplayText(475, 20); // display menu in demo only if (game_state == GAME_STATE_DEMO_RUN) DisplayMenu(); // display target cross-hair and tank gun in game run only if (game_state == GAME_STATE_GAME_RUN) { sprintf(text,"+"); DisplayText(width/2 - 2, height/2 + 2); glDisable(GL_DEPTH_TEST); glBlendFunc(GL_DST_COLOR, GL_ZERO); // set blending mask glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBindTexture(GL_TEXTURE_2D, texture[TEX_MASK]); // use gun mask texture // draw the gun turret mask glBegin(GL_QUADS); glTexCoord2i(0,1); glVertex2i(width/2 - x, height - y); glTexCoord2i(0,0); glVertex2i(width/2 - x, height); glTexCoord2i(1,0); glVertex2i(width/2 + x, height); glTexCoord2i(1,1); glVertex2i(width/2 + x, height - y); glEnd(); glBlendFunc(GL_ONE, GL_ONE); // set blending mask glBindTexture(GL_TEXTURE_2D, texture[TEX_GUN]); // use gun texture // draw the gun turret glBegin(GL_QUADS); glTexCoord2i(0,1); glVertex2i(width/2 - x, height - y); glTexCoord2i(0,0); glVertex2i(width/2 - x, height); glTexCoord2i(1,0); glVertex2i(width/2 + x, height); glTexCoord2i(1,1); glVertex2i(width/2 + x, height - y); glEnd(); glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); } DisplayRadar(); // both demo and game run radar CalculateFPS(); // restore proj and model matrices glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); } // END OF DisplayScore /////////////////////////////////////////////////////////// // Display radar bottom-left of screen /////////////////////////////////////////////////////////// void DisplayRadar() { int i, xi, yi; GLfloat x,z; if (tank_damage == 1) // radar is damaged { frames_damage++; if (frames_damage > 100) frames_damage = 0; if (frames_damage < 70) { sprintf(text, "Radar Damaged"); DisplayText(10, height - 50); return; } } if (tank_damage == 2) // radar is out { sprintf(text, "Radar Out"); DisplayText(10, height - 50); return; } // radar is normal glColor3f(0.0f, 0.0f, 1.0f); // blue player x = Player.x; z = Player.z; xi = (int)((x + 1000) / 20) + 5; // convert 2D points yi = (int)(((z + 1000) / 20) + height - 105); glBegin(GL_QUADS); // display player glVertex2i(xi-5, yi-5); glVertex2i(xi+5, yi-5); glVertex2i(xi+5, yi+5); glVertex2i(xi-5, yi+5); glEnd(); for (i = 0; i < MAX_BULLETS; i++) // loop thru player bullets { if (Bullets[i].alive) { x = Bullets[i].x; z = Bullets[i].z; xi = (int)((x + 1000) / 20) + 5; // convert 2D points yi = (int)(((z + 1000) / 20) + height - 105); glBegin(GL_QUADS); // display bullets as quads glVertex2i(xi-2, yi-2); glVertex2i(xi+2, yi-2); glVertex2i(xi+2, yi+2); glVertex2i(xi-2, yi+2); glEnd(); } // end if } // end for if (level_wait == 0) { glColor3f(1.0f, 0.0f, 0.0f); // red enemies for (i = 0; i < MAX_ENEMIES; i++) // loop thru enemies { if (Enemy[i].alive) { x = Enemy[i].x; z = Enemy[i].z; xi = (int)((x + 1000) / 20) + 5; // convert 2D points yi = (int)(((z + 1000) / 20) + height - 105); if (xi > 0 && xi < 110 && yi > (height - 110) && yi < height) { // make sure in radar range if (Enemy[i].type == ENEMY_TANK) { glBegin(GL_TRIANGLES); // display enemy tanks as tri glVertex2i(xi, yi-5); glVertex2i(xi+5, yi+5); glVertex2i(xi-5, yi+5); glEnd(); } if (Enemy[i].type == ENEMY_COPTER) { glBegin(GL_LINE_LOOP); // display enemy copters as tri loops glVertex2i(xi, yi-5); glVertex2i(xi+5, yi+5); glVertex2i(xi-5, yi+5); glEnd(); } } // end in radar range } // end if } // end for for (i = 0; i < MAX_EBULLETS; i++) // loop thru enemy bullets { if (EBullets[i].alive) { x = EBullets[i].x; z = EBullets[i].z; xi = (int)((x + 1000) / 20) + 5; // convert 2D points yi = (int)(((z + 1000) / 20) + height - 105); glBegin(GL_QUADS); // display ebullets as quads glVertex2i(xi-2, yi-2); glVertex2i(xi+2, yi-2); glVertex2i(xi+2, yi+2); glVertex2i(xi-2, yi+2); glEnd(); } // end if } // end for } // end if level_wait glColor3f(0.0f, 1.0f, 0.0f); // green objects for (i = 0; i < MAX_OBJECTS; i++) // loop thru objects { if (Objects[i].alive) { x = Objects[i].x; z = Objects[i].z; xi = (int)((x + 1000) / 20) + 5; // convert 2D points yi = (int)(((z + 1000) / 20) + height - 105); if (Objects[i].type == TYPE_FUEL) { glBegin(GL_LINE_LOOP); // display fuels as tri loops glVertex2i(xi, yi-4); glVertex2i(xi+4, yi+4); glVertex2i(xi-4, yi+4); glEnd(); } // end if fuel else { glBegin(GL_LINE_LOOP); // display camo/ammo as quad loops glVertex2i(xi-3, yi-3); glVertex2i(xi+3, yi-3); glVertex2i(xi+3, yi+3); glVertex2i(xi-3, yi+3); glEnd(); } // end else } // end if } // end for glColor3f(1.0f, 1.0f, 1.0f); // reset color to white } // END OF DisplayRadar /////////////////////////////////////////////////////////// // Display menu selections /////////////////////////////////////////////////////////// void DisplayMenu() { frames_menu++; if (frames_menu > LIMIT_MENU) { frames_menu = 0; color_menu++; if (color_menu > 4) color_menu = 1; } if (color_menu == 1) glColor3f(1.0f, 1.0f, 1.0f); if (color_menu == 2) glColor3f(1.0f, 0.0f, 0.0f); if (color_menu == 3) glColor3f(0.0f, 1.0f, 0.0f); if (color_menu == 4) glColor3f(0.0f, 0.0f, 1.0f); // HEADING sprintf(text,"VazTank a game by PhilVaz (c) 2004"); DisplayText(150,70); sprintf(text,"www.bringyou.to/games or VazGames.com"); DisplayText(150,95); // MAIN MENU if (menu_select == MENU_SELECT_PLAY) sprintf(text,">Play<"); else sprintf(text," Play "); DisplayText(250,150); if (menu_select == MENU_SELECT_CONTROLS) sprintf(text,">Controls<"); else sprintf(text," Controls "); DisplayText(250,190); if (menu_select == MENU_SELECT_DIFFICULTY) sprintf(text,">Difficulty<"); else sprintf(text," Difficulty "); DisplayText(250,230); if (menu_select == MENU_SELECT_AMBIANCE) sprintf(text,">Ambiance<"); else sprintf(text," Ambiance "); DisplayText(250,270); if (menu_select == MENU_SELECT_SCORING) sprintf(text,">Scoring<"); else sprintf(text," Scoring "); DisplayText(250,310); if (menu_select == MENU_SELECT_QUIT) sprintf(text,">Quit<"); else sprintf(text," Quit "); DisplayText(250,350); // CONTROL MENU if (game_control == CONTROL_KEYBOARD) { if (menu_select == MENU_SELECT_KEYBOARD) sprintf(text,">Keyboard<"); else sprintf(text," Keyboard "); DisplayText(365,190); } if (game_control == CONTROL_MOUSE) { if (menu_select == MENU_SELECT_MOUSE) sprintf(text,">Mouse<"); else sprintf(text," Mouse "); DisplayText(365,190); } if (game_control == CONTROL_JOY) { if (menu_select == MENU_SELECT_JOY) sprintf(text,">Joystick<"); else sprintf(text," Joystick "); DisplayText(365,190); } // DIFFICULTY MENU if (game_difficulty == DIFFICULTY_EASY) { if (menu_select == MENU_SELECT_DIFFEASY) sprintf(text,">Easy<"); else sprintf(text," Easy "); DisplayText(365,230); } if (game_difficulty == DIFFICULTY_NORMAL) { if (menu_select == MENU_SELECT_DIFFNORMAL) sprintf(text,">Normal<"); else sprintf(text," Normal "); DisplayText(365,230); } if (game_difficulty == DIFFICULTY_HARD) { if (menu_select == MENU_SELECT_DIFFHARD) sprintf(text,">Hard<"); else sprintf(text," Hard "); DisplayText(365,230); } // AMBIANCE VOLUME MENU if (game_volume == AMBIANCE_OFF) { if (menu_select == MENU_SELECT_AMBOFF) sprintf(text,">Off<"); else sprintf(text," Off "); DisplayText(365,270); } if (game_volume == AMBIANCE_ON) { if (menu_select == MENU_SELECT_AMBON) sprintf(text,">On<"); else sprintf(text," On "); DisplayText(365,270); } // DESCRIPTION LINE sprintf(text, menu_text[menu_select]); DisplayText(150,410); if (menu_select == 0) // add desc line for Play { sprintf(text, "Destroy all Enemy Tanks each new level"); DisplayText(150,435); } if (menu_select == 4) { sprintf(text, "Bonus for Fuel, Ammo, and Camo boxes"); DisplayText(150,435); } glColor3f(1.0f, 1.0f, 1.0f); // reset color to white } // END OF DisplayMenu /////////////////////////////////////////////////////////// // Process menu selections /////////////////////////////////////////////////////////// void ProcessMenu() { menu_wait--; if (menu_wait > 0) return; menu_wait = MENU_PAUSE; if (KEYDOWN(VK_UP) || joy_up) { if (menu_select < 6) { menu_select--; if (menu_select < 0) menu_select = 5; if (sound_ok) IDirectSoundBuffer_Play(game_sound_menu1,0,0,NULL); } else { switch(menu_select) { case MENU_SELECT_KEYBOARD: { game_control = CONTROL_JOY; menu_select = MENU_SELECT_JOY; } break; case MENU_SELECT_MOUSE: { game_control = CONTROL_KEYBOARD; menu_select = MENU_SELECT_KEYBOARD; } break; case MENU_SELECT_JOY: { game_control = CONTROL_MOUSE; menu_select = MENU_SELECT_MOUSE; } break; case MENU_SELECT_DIFFEASY: { game_difficulty = DIFFICULTY_HARD; menu_select = MENU_SELECT_DIFFHARD; } break; case MENU_SELECT_DIFFNORMAL: { game_difficulty = DIFFICULTY_EASY; menu_select = MENU_SELECT_DIFFEASY; } break; case MENU_SELECT_DIFFHARD: { game_difficulty = DIFFICULTY_NORMAL; menu_select = MENU_SELECT_DIFFNORMAL; } break; case MENU_SELECT_AMBOFF: { game_volume = AMBIANCE_ON; menu_select = MENU_SELECT_AMBON; if (sound_ok) IDirectSoundBuffer_Play(game_sound_gun4,0,0,NULL); } break; case MENU_SELECT_AMBON: { game_volume = AMBIANCE_OFF; menu_select = MENU_SELECT_AMBOFF; } break; } // end switch if (sound_ok && menu_select != AMBIANCE_ON) IDirectSoundBuffer_Play(game_sound_menu2,0,0,NULL); } // end else } // end up else if (KEYDOWN(VK_DOWN) || joy_down) { if (menu_select < 6) { menu_select++; if (menu_select > 5) menu_select = 0; if (sound_ok) IDirectSoundBuffer_Play(game_sound_menu1,0,0,NULL); } else { switch(menu_select) { case MENU_SELECT_KEYBOARD: { game_control = CONTROL_MOUSE; menu_select = MENU_SELECT_MOUSE; } break; case MENU_SELECT_MOUSE: { game_control = CONTROL_JOY; menu_select = MENU_SELECT_JOY; } break; case MENU_SELECT_JOY: { game_control = CONTROL_KEYBOARD; menu_select = MENU_SELECT_KEYBOARD; } break; case MENU_SELECT_DIFFEASY: { game_difficulty = DIFFICULTY_NORMAL; menu_select = MENU_SELECT_DIFFNORMAL; } break; case MENU_SELECT_DIFFNORMAL: { game_difficulty = DIFFICULTY_HARD; menu_select = MENU_SELECT_DIFFHARD; } break; case MENU_SELECT_DIFFHARD: { game_difficulty = DIFFICULTY_EASY; menu_select = MENU_SELECT_DIFFEASY; } break; case MENU_SELECT_AMBOFF: { game_volume = AMBIANCE_ON; menu_select = MENU_SELECT_AMBON; if (sound_ok) IDirectSoundBuffer_Play(game_sound_gun4,0,0,NULL); } break; case MENU_SELECT_AMBON: { game_volume = AMBIANCE_OFF; menu_select = MENU_SELECT_AMBOFF; } break; } // end switch if (sound_ok && menu_select != MENU_SELECT_AMBON) IDirectSoundBuffer_Play(game_sound_menu2,0,0,NULL); } // end else } if (KEYDOWN(VK_LEFT) || joy_left) { if (menu_select > 5 && menu_select < 9) menu_select = MENU_SELECT_CONTROLS; else if (menu_select > 8 && menu_select < 12) menu_select = MENU_SELECT_DIFFICULTY; else if (menu_select > 11) menu_select = MENU_SELECT_AMBIANCE; if (sound_ok) IDirectSoundBuffer_Play(game_sound_menu1,0,0,NULL); } else if (KEYDOWN(VK_RIGHT) || joy_right) { if (menu_select == MENU_SELECT_CONTROLS) menu_select = game_control; else if (menu_select == MENU_SELECT_DIFFICULTY) menu_select = game_difficulty; else if (menu_select == MENU_SELECT_AMBIANCE) menu_select = game_volume; if (sound_ok) IDirectSoundBuffer_Play(game_sound_menu2,0,0,NULL); } if ((KEYDOWN(VK_RETURN) || KEYDOWN(VK_SPACE)) && menu_select == MENU_SELECT_PLAY) game_state = GAME_STATE_GAME_INIT; else if ((KEYDOWN(VK_RETURN) || KEYDOWN(VK_SPACE)) && menu_select == MENU_SELECT_QUIT) SendMessage (game_window, WM_CLOSE, 0, 0); } // END OF ProcessMenu /////////////////////////////////////////////////////////// // Render one scene (one game frame) /////////////////////////////////////////////////////////// void RenderScene() { int i; // loops int c; // particle color GLfloat x, y, z; // temp x y z position for particles // set up camera, clear screen, and render one scene glPushMatrix(); // save current scene info gluLookAt(Player.x, Player.y, Player.z, Player.x + (50.0f*(GLfloat)cos(Player.Angle)), Player.y + Player.Look, Player.z - (50.0f*(GLfloat)sin(Player.Angle)), 0.0f, 1.0f, 0.0f); // clear screen and depth buff glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); // save matrix glRotatef(sky_degrees,0.0f,1.0f,0.0f); // rotate upper sky glCallList(Sky2List); // draw upper sky (rotated) glPopMatrix(); // restore matrix glCallList(Sky1List); // draw bottom blended sky (non-moving) glCallList(RoomList); // draw the room walls and floor // draw objects camo, fuel, ammo boxes for (i = 0; i < MAX_OBJECTS; i++) { if (Objects[i].alive) { glPushMatrix(); glTranslatef(Objects[i].x, Objects[i].y, Objects[i].z); if (Objects[i].type == TYPE_CAMO) glCallList(CamoList); if (Objects[i].type == TYPE_FUEL) glCallList(FuelList); if (Objects[i].type == TYPE_AMMO) glCallList(AmmoList); glPopMatrix(); } } // end for // draw player bullets glColor3f(0.25f, 0.25f, 0.25f); // dark grey bullets for (i = 0; i < MAX_BULLETS; i++) { if (Bullets[i].alive) { glPushMatrix(); glTranslatef(Bullets[i].x, Bullets[i].y, Bullets[i].z); gluSphere(game_sphere, 1.0f, 20, 20); // draw sphere rad=1 long=10 lat=10 glPopMatrix(); } } // end for glColor3f(1.0f, 1.0f, 1.0f); // reset color to white if (level_wait == 0) { // draw enemy tanks for (i = 0; i < MAX_ENEMIES; i++) { if (Enemy[i].alive) { glPushMatrix(); glTranslatef(Enemy[i].x, Enemy[i].y, Enemy[i].z); if (Enemy[i].type == ENEMY_TANK) { glRotatef(Enemy[i].Angle, 0.0f, 1.0f, 0.0f); glCallList(EnemyList); // draw enemy tank } if (Enemy[i].type == ENEMY_COPTER) { // draw enemy copter glColor3f(0.37f, 0.37f, 0.0f); // reddish color glRotatef(90.0f, 0.0f, 0.0f, 1.0f); glRotatef( 0.0f, 1.0f, 0.0f, 0.0f); glRotatef(Enemy[i].Angle, 0.0f, 1.0f, 0.0f); glCallList(CopterList); } glPopMatrix(); } } // end for // draw enemy bullets glColor3f(0.65f, 0.0f, 0.0f); // dark red bullets for (i = 0; i < MAX_EBULLETS; i++) { if (EBullets[i].alive) { glPushMatrix(); glTranslatef(EBullets[i].x, EBullets[i].y, EBullets[i].z); gluSphere(game_sphere, 1.0f, 20, 20); // draw sphere rad=1 long=10 lat=10 glPopMatrix(); } } // end for glColor3f(1.0f, 1.0f, 1.0f); // reset color to white } // end if level_wait // draw explosion particles for (i = 0; i < MAX_PARTICLES; i++) { if (Particles[i].alive) // if particle is alive { x = Particles[i].x; y = Particles[i].y; z = Particles[i].z; c = Particles[i].color; if (c == COLOR_BLK) glColor3f(0.25f, 0.25f, 0.25f); if (c == COLOR_WHT) glColor3f(0.90f, 0.90f, 0.90f); if (c == COLOR_RED) glColor3f(0.87f, 0.0f, 0.0f); if (c == COLOR_TAN) glColor3f(0.59f, 0.45f, 0.0f); if (c == COLOR_MAR) glColor3f(0.53f, 0.0f, 0.26f); if (c == COLOR_DGR) glColor3f(0.33f, 0.41f, 0.0f); if (c == COLOR_BRN) glColor3f(0.66f, 0.41f, 0.0f); if (c == COLOR_YEL) glColor3f(0.96f, 0.96f, 0.0f); glBegin(GL_QUADS); // draw as tiny quads glVertex3f(x-0.1f, y-0.1f, z); glVertex3f(x+0.1f, y-0.1f, z); glVertex3f(x+0.1f, y+0.1f, z); glVertex3f(x-0.1f, y+0.1f, z); glEnd(); } // end if } // end for glColor3f(1.0f, 1.0f, 1.0f); // reset color to white glPopMatrix(); // restore current scene info DisplayScore(); SwapBuffers(game_dc); sky_degrees += 0.03f; // slowly spin the sky ProcessInput(); PlayRandomSound(); } // END OF RenderScene ///////////////////////////////////////////// // Set player tank initial position ///////////////////////////////////////////// void SetTank() { int i; Player.x = 0.0f; // center of world Player.z = 0.0f; Player.Look = 0.0f; // center the view SetCursorPos(Xmouse, height/2 - ((int)Player.Look * 6)); // reset cursor tank_speed = 0.0f; // stop movement tank_damage = 0; // reset damage tank_ammo = AMMO_TOTAL; tank_fuel = FUEL_TOTAL; for (i = 0; i < MAX_BULLETS; i++) Bullets[i].alive = FALSE; for (i = 0; i < MAX_EBULLETS; i++) EBullets[i].alive = FALSE; fire_wait = EFIRE_PAUSE; // reset enemy fire wait time to give player chance to move } // END OF SetTank ///////////////////////////////////////////// // Set enemy tanks initial positions ///////////////////////////////////////////// void SetEnemy() { int i, total; GLfloat x, y, z; // temp x y z position for tank GLfloat a = 0; // temp angle for (i = 0; i < MAX_ENEMIES; i++) Enemy[i].alive = FALSE; for (i = 0; i < MAX_EBULLETS; i++) EBullets[i].alive = FALSE; total_copters = 0; // reset copter counter for level if (game_level < 3) total = 4; if (game_level > 2 && game_level < 6) total = 8; if (game_level > 5) total = 12; // difficulty normal and hard check if (game_difficulty == DIFFICULTY_NORMAL) total += (total / 4); if (game_difficulty == DIFFICULTY_HARD) total += (total / 2); enemy_left = total; // save enemy tanks to kill for level // loop through total enemies for level for (i = 0; i < total; i++) { Enemy[i].alive = TRUE; x = (GLfloat)(rand()%480) + 500.0f; if (rand()%2 == 1) x = -x; // put on either side of x wall z = (GLfloat)(rand()%480) + 500.0f; if (rand()%2 == 1) z = -z; // put on either side of z wall y = 0; // tank is on ground Enemy[i].x = x; Enemy[i].y = y; Enemy[i].z = z; // save x y z Enemy[i].Angle = a; Enemy[i].type = ENEMY_TANK; // save angle and type Enemy[i].move = EMOVE_PAUSE; // save move pause Enemy[i].dx = 0; Enemy[i].dy = 0; Enemy[i].dz = 0; // initially no movement } // end for level_wait = LEVEL_PAUSE; // reset pause time } // END OF SetEnemy ///////////////////////////////////////////// // Move enemy tanks A.I. ///////////////////////////////////////////// void MoveEnemy() { int i, j, r; // loop, temp game level, and random fire GLfloat x, y, z; // temp x y z position of enemy GLfloat m, dx, dz; // temp magnitude (distance), diff x and z if (level_wait > 0) { level_wait--; if (level_wait == 3 && sound_ok && game_state == GAME_STATE_GAME_RUN) IDirectSoundBuffer_Play(game_sound_level,0,0,NULL); return; } if (fire_wait > 0) fire_wait--; for (i = 0; i < MAX_ENEMIES; i++) { if (Enemy[i].alive) { x = Enemy[i].x; y = Enemy[i].y; z = Enemy[i].z; // save current position x += Enemy[i].dx; z += Enemy[i].dz; // move // check if against wall (or if copter fly out of world) if (Enemy[i].type == ENEMY_COPTER) { if (x > 1100.0f || x < -1100.0f || z > 1100.0f || z < -1100.0f) { Enemy[i].alive = FALSE; total_copters--; } } if (Enemy[i].type == ENEMY_TANK) { if (x > 985.0f) x = 985.0f; else if (x < -985.0f) x = -985.0f; if (z > 985.0f) z = 985.0f; else if (z < -985.0f) z = -985.0f; } Enemy[i].move--; if (Enemy[i].move == 0) // is it time to move or fire yet? { j = game_level; if (j > 9) j = 9; // determine new pause time Enemy[i].move = EMOVE_PAUSE * (10 - j) + (rand()%EMOVE_PAUSE); dx = Player.x - x; dz = Player.z - z; // get diff between enemy and player m = (GLfloat)sqrt(dx*dx + dz*dz); if (m == 0.0f) m = .0001f; // get magnitude (distance) if (Enemy[i].type == ENEMY_TANK) // only enemy tanks will change direction { Enemy[i].dx = (GLfloat)((rand()%40) - 20.0f) * .01f; // move random on x Enemy[i].dz = (GLfloat)((rand()%40) - 20.0f) * .01f; // move random on z if (game_state == GAME_STATE_GAME_RUN) // if GAME_RUN move toward player also { Enemy[i].dx += (dx/m) * .3f; Enemy[i].dz += (dz/m) * .3f; } } // end if enemy tank if (m < 800.0f && fire_wait == 0) // within firing range? { r = (rand()%3); // chance for fire 1 in 3 if (r == 1) FireEBullet(x,y,z); // fire enemy bullet } // end if within range } // end if enemy time to move Enemy[i].x = x; Enemy[i].z = z; // save new position } // end if enemy alive } // end for // check for chance of adding enemy copter j = game_level; if (j > 9) j = 9; r = rand()%(500 * (10 - j)); if (r < 3 && total_copters < MAX_COPTERS) { for (i = 0; i < MAX_ENEMIES; i++) { if (!Enemy[i].alive) // found a free enemy { Enemy[i].alive = TRUE; Enemy[i].type = ENEMY_COPTER; total_copters++; x = (GLfloat)(rand()%500) + 500.0f; if (rand()%2 == 1) x = -x; // put inside x wall z = (GLfloat)(rand()%500) + 500.0f; if (rand()%2 == 1) z = -z; // put inside z wall y = (GLfloat)(rand()%25) + 8.0f; // copter is in the air Enemy[i].x = x; Enemy[i].y = y; Enemy[i].z = z; // random move on x dx = (GLfloat)((rand()%7) - 3.0f) * .15f; if ((x > 0 && dx > 0) || (x < 0 && dx < 0)) dx = -dx; // make sure moving into world do // random move on z { dz = (GLfloat)((rand()%7) - 3.0f) * .15f; } while (dz == 0 && dx == 0); // copter must move either on x or z if ((z > 0 && dz > 0) || (z < 0 && dz < 0)) dz = -dz; // make sure moving into world Enemy[i].dx = dx; Enemy[i].dy = 0.0f; Enemy[i].dz = dz; Enemy[i].Angle = 90.0f; // calculate y facing angle Enemy[i].move = EMOVE_PAUSE * (10 - j) + (rand()%EMOVE_PAUSE); return; } // end if free enemy } // end for enemies } // end add enemy copter } // END OF MoveEnemy ///////////////////////////////////////////// // Move player bullets ///////////////////////////////////////////// void MoveBullets() { int i; GLfloat x, y, z; // temp x y z position of bullet for (i = 0; i < MAX_BULLETS; i++) { if (Bullets[i].alive) { x = Bullets[i].x; y = Bullets[i].y; z = Bullets[i].z; // save current position x += Bullets[i].dx; y += Bullets[i].dy; z += Bullets[i].dz; // move if (y < 6 && (x < -995 || x > 995 || z < -995 || z > 995 || y < 0)) // out of range? { Bullets[i].alive = FALSE; if (y > 0) InsertParticles(x,y,z,EXPLODE_WALL); // small explosion else InsertParticles(x,y,z,EXPLODE_GROUND); } if (y > 90) Bullets[i].alive = FALSE; // bullet thru sky, no explosion Bullets[i].x = x; Bullets[i].y = y; Bullets[i].z = z; // save new position Bullets[i].count--; if (Bullets[i].count == 0) Bullets[i].alive = FALSE; } // end if bullet alive } // end for } // END OF MoveBullets ///////////////////////////////////////////// // Move enemy bullets ///////////////////////////////////////////// void MoveEBullets() { int i; GLfloat x, y, z; for (i = 0; i < MAX_EBULLETS; i++) { if (EBullets[i].alive) { x = EBullets[i].x; y = EBullets[i].y; z = EBullets[i].z; // save current position x += EBullets[i].dx; y += EBullets[i].dy; z += EBullets[i].dz; // move // special check on y for copter bomb hitting ground if (y < 0) { EBullets[i].alive = FALSE; InsertParticles(x,y,z,EXPLODE_GROUND); } if (x < -995 || x > 995 || z < -995 || z > 995) // out of range? { EBullets[i].alive = FALSE; InsertParticles(x,y,z,EXPLODE_WALL); // small explosion } EBullets[i].x = x; EBullets[i].y = y; EBullets[i].z = z; // save new position EBullets[i].count--; if (EBullets[i].count == 0) EBullets[i].alive = FALSE; } // end if EBullet live } // end for } // END OF MoveEBullets ///////////////////////////////////////////// // Fire an enemy bullet ///////////////////////////////////////////// void FireEBullet(GLfloat x, GLfloat y, GLfloat z) { int i; GLfloat m, dx, dy, dz, ax, az; if (game_state != GAME_STATE_GAME_RUN) return; for (i = 0; i < MAX_EBULLETS; i++) { if (!EBullets[i].alive) { EBullets[i].alive = TRUE; EBullets[i].x = x; EBullets[i].y = y; EBullets[i].z = z; // aim bullet directly at player, if copter bullet then need y vector too dx = Player.x - x; dz = Player.z - z; dy = 0; if (y > 0) dy = Player.y - y; m = (GLfloat)sqrt(dx*dx + dy*dy + dz*dz); if (m == 0.0f) m = .0001f; // adjust aim to miss slightly based on difficulty level ax = (GLfloat)((rand()%40) - 20.0f) * .005f; az = (GLfloat)((rand()%40) - 20.0f) * .005f; if (game_difficulty == DIFFICULTY_EASY) { ax *= 2.0f; az *= 2.0f; } if (game_difficulty == DIFFICULTY_NORMAL) { ax *= 1.5f; az *= 1.5f; } EBullets[i].dx = (dx/m) * BULLET_SPEED + ax; // save bullet dx plus miss EBullets[i].dz = (dz/m) * BULLET_SPEED + az; // save bullet dz plus miss EBullets[i].dy = (dy/m) * BULLET_SPEED; // straight ahead if tank, downward if copter EBullets[i].count = BULLET_DURATION - 25; // shorter range than player if (sound_ok) IDirectSoundBuffer_Play(game_sound_bullet10,0,0,NULL); return; } // end if EBullet alive } // end for } // END OF FireEBullet ///////////////////////////////////////////// // Set objects (camos, fuels, ammos) ///////////////////////////////////////////// void SetObjects() { int i, total; GLfloat x, y, z; // temp x y z position of object for (i = 0; i < MAX_OBJECTS; i++) Objects[i].alive = FALSE; for (i = 0; i < MAX_BULLETS; i++) Bullets[i].alive = FALSE; if (game_level < 3) total = 4; if (game_level > 2 && game_level < 6) total = 7; if (game_level > 5) total = 10; y = 0.0f; for (i = 0; i < (total * 2); i++) { Objects[i].alive = TRUE; Objects[i].type = TYPE_CAMO; // camo box x = (GLfloat)((rand()%1960) - 980); z = (GLfloat)((rand()%1960) - 980); Objects[i].x = x; Objects[i].y = y; Objects[i].z = z; Objects[total * 2 + i].alive = TRUE; Objects[total * 2 + i].type = TYPE_FUEL; // fuel box x = (GLfloat)((rand()%1960) - 980); z = (GLfloat)((rand()%1960) - 980); Objects[total * 2 + i].x = x; Objects[total * 2 + i].y = y; Objects[total * 2 + i].z = z; Objects[total * 4 + i].alive = TRUE; Objects[total * 4 + i].type = TYPE_AMMO; // ammo box x = (GLfloat)((rand()%1960) - 980); z = (GLfloat)((rand()%1960) - 980); Objects[total * 4 + i].x = x; Objects[total * 4 + i].y = y; Objects[total * 4 + i].z = z; } // end for } // END OF SetObjects ///////////////////////////////////////////// // Insert particle explosion of type t ///////////////////////////////////////////// void InsertParticles(GLfloat x, GLfloat y, GLfloat z, int t) { int i, n, p, r; if (t == EXPLODE_WALL || EXPLODE_GROUND) p = 1; if (t == EXPLODE_CAMO || t == EXPLODE_AMMO || t == EXPLODE_FUEL) p = 2; if (t == EXPLODE_ENEMY) p = 3; n = p * 300 + (rand()%(p*300)); // number of particles to insert for (i = 0; i < MAX_PARTICLES; i++) { if (!Particles[i].alive) // found free particle { Particles[i].alive = TRUE; Particles[i].x = x; // position at center of collision Particles[i].y = y; Particles[i].z = z; Particles[i].dx = 0.5f * p * ((GLfloat)(rand()%10000) / 10000.0f) - .25f * p; // movement away from center Particles[i].dy = 0.5f * p * ((GLfloat)(rand()%10000) / 10000.0f) - .25f * p; Particles[i].dz = 0.5f * p * ((GLfloat)(rand()%10000) / 10000.0f) - .25f * p; if (t == EXPLODE_WALL) { r = (rand()%2); if (r == 0) Particles[i].color = COLOR_BLK; if (r == 1) Particles[i].color = COLOR_WHT; } if (t == EXPLODE_GROUND) { r = (rand()%2); if (r == 0) Particles[i].color = COLOR_TAN; if (r == 1) Particles[i].color = COLOR_BRN; } if (t == EXPLODE_CAMO || t == EXPLODE_ENEMY) { r = (rand()%4); if (r == 0) Particles[i].color = COLOR_BLK; if (r == 1) Particles[i].color = COLOR_WHT; if (r == 2) Particles[i].color = COLOR_MAR; if (r == 3) Particles[i].color = COLOR_DGR; } if (t == EXPLODE_AMMO) { r = (rand()%4); if (r == 0) Particles[i].color = COLOR_BLK; if (r == 1) Particles[i].color = COLOR_WHT; if (r == 2) Particles[i].color = COLOR_RED; if (r == 3) Particles[i].color = COLOR_TAN; } if (t == EXPLODE_FUEL) { r = (rand()%4); if (r == 0) Particles[i].color = COLOR_BLK; if (r == 1) Particles[i].color = COLOR_WHT; if (r == 2) Particles[i].color = COLOR_BRN; if (r == 3) Particles[i].color = COLOR_YEL; } Particles[i].count = p * 50 + (rand()%(p*50)); // set duration of particle n--; if (n == 0) return; // finished inserting all particles } // end if } // end for } // END OF InsertParticles ///////////////////////////////////////////// // Move all particles ///////////////////////////////////////////// void MoveParticles() { int i; GLfloat x, y, z; // temp x y z position of particle for (i = 0; i < MAX_PARTICLES; i++) { if (Particles[i].alive) { // save current position x = Particles[i].x; y = Particles[i].y; z = Particles[i].z; // move to new position x += Particles[i].dx; y += Particles[i].dy; z += Particles[i].dz; // gravity effect on y movement if (Particles[i].dy < 0) Particles[i].dy += GRAVITY_SPEED; else Particles[i].dy -= GRAVITY_SPEED; if (x < -998 || x > 998) { Particles[i].dx *= (-1); x += Particles[i].dx; } // bounce on x if (z < -998 || z > 998) { Particles[i].dz *= (-1); z += Particles[i].dz; } // bounce on z if (y < 0) { Particles[i].dy *= (-1); y += Particles[i].dy; } // bounce on y (ground) // save new position Particles[i].x = x; Particles[i].y = y; Particles[i].z = z; // check if particle finished Particles[i].count--; if (Particles[i].count == 0) Particles[i].alive = FALSE; } // end if } // end for } // END OF MoveParticles ///////////////////////////////////////////// // Check all potential collisions ///////////////////////////////////////////// void CheckCollisions() { int i, j; // loop counters GLfloat xb, yb, zb, x, y, z; // bullet and temp positions int m; // approx magnitude (distance) for scoring // check player bullets with objects and enemies for (i = 0; i < MAX_BULLETS; i++) { if (Bullets[i].alive) { xb = Bullets[i].x; yb = Bullets[i].y; zb = Bullets[i].z; // check player bullet with each object for (j = 0; j < MAX_OBJECTS; j++) { if (Objects[j].alive) { x = Objects[j].x; y = Objects[j].y; z = Objects[j].z; // check collision bullet with object if (yb < 7 && xb < x+7 && xb > x-7 && zb < z+7 && zb > z-7) { // we have collision Objects[j].alive = FALSE; Bullets[i].alive = FALSE; game_score += 5; // determine type of object for particle colors if (Objects[j].type == TYPE_CAMO) InsertParticles(x,y,z,EXPLODE_CAMO); if (Objects[j].type == TYPE_AMMO) InsertParticles(x,y,z,EXPLODE_AMMO); if (Objects[j].type == TYPE_FUEL) InsertParticles(x,y,z,EXPLODE_FUEL); PlayRandomExplosion(0); } // end if collision } // end if object alive } // end for objects // check player bullet with each enemy tank or copter for (j = 0; j < MAX_ENEMIES; j++) { if (Enemy[j].alive) { x = Enemy[j].x; y = Enemy[j].y; z = Enemy[j].z; // check collision player bullet with enemy if (Enemy[j].type == ENEMY_TANK && yb < 6 && xb < x+6 && xb > x-6 && zb < z+6 && zb > z-6) { // we have hit an enemy tank Enemy[j].alive = FALSE; Bullets[i].alive = FALSE; m = (int)sqrt((Player.x - x)*(Player.x - x) + (Player.z - z)*(Player.z - z)); game_score += m; InsertParticles(x,y,z,EXPLODE_ENEMY); PlayRandomExplosion(1); enemy_left--; if (enemy_left == 0) game_state = GAME_STATE_GAME_RESET; } // end if collision if (Enemy[j].type == ENEMY_COPTER && yb < y+4 && yb > y-4 && xb < x+4 && xb > x-4 && zb < z+4 && zb > z-4) { // we have hit a copter Enemy[j].alive = FALSE; Bullets[i].alive = FALSE; game_score += 2000; InsertParticles(x,y,z,EXPLODE_ENEMY); PlayRandomExplosion(1); } // end if collision } // end if enemy alive } // end for enemies } // end if bullet alive } // end for bullets // check player tank with objects for (i = 0; i < MAX_OBJECTS; i++) { if (Objects[i].alive) { x = Objects[i].x; y = Objects[i].y; z = Objects[i].z; // check collision player tank with object if (Player.x < x+9 && Player.x > x-9 && Player.z < z+9 && Player.z > z-9) { Objects[i].alive = FALSE; // determine type of object for bonus if (Objects[i].type == TYPE_FUEL) { tank_fuel += 2500; if (tank_fuel > FUEL_TOTAL) tank_fuel = FUEL_TOTAL; game_score += 50; } if (Objects[i].type == TYPE_AMMO) { tank_ammo += 10; if (tank_ammo > AMMO_TOTAL) tank_ammo = AMMO_TOTAL; game_score += 25; } if (sound_ok) { if (Objects[i].type == TYPE_CAMO) IDirectSoundBuffer_Play(game_sound_explode0,0,0,NULL); else IDirectSoundBuffer_Play(game_sound_bonus,0,0,NULL); } } // end if collision } // end if object alive } // end for objects // check player tank with enemies (tanks only) for (i = 0; i < MAX_ENEMIES; i++) { if (Enemy[i].alive) { x = Enemy[i].x; y = Enemy[i].y; z = Enemy[i].z; // check collision enemy tank with player if (Enemy[i].type == ENEMY_TANK && Player.x < x+7 && Player.x > x-7 && Player.z < z+7 && Player.z > z-7) { Enemy[i].alive = FALSE; PlayRandomExplosion(1); // big boom enemy_left--; if (enemy_left == 0) game_state = GAME_STATE_GAME_RESET; tanks_left--; if (tanks_left == 0) game_state = GAME_STATE_DEMO_INIT; SetTank(); InsertParticles(Player.x, Player.y, Player.z, EXPLODE_ENEMY); } // end if collision } // end if enemy alive } // end for enemies // check enemy bullets with objects (bullets or bombs) for (i = 0; i < MAX_EBULLETS; i++) { if (EBullets[i].alive) { xb = EBullets[i].x; yb = EBullets[i].y; zb = EBullets[i].z; // check enemy bullet with each object for (j = 0; j < MAX_OBJECTS; j++) { if (Objects[j].alive) { x = Objects[j].x; y = Objects[j].y; z = Objects[j].z; // check collision ebullet with object if (yb < 7 && xb < x+7 && xb > x-7 && zb < z+7 && zb > z-7) { // we have collision EBullets[i].alive = FALSE; // determine type of object for particle colors if (Objects[j].type == TYPE_CAMO) InsertParticles(x,y,z,EXPLODE_WALL); // small if (Objects[j].type == TYPE_AMMO) // larger explosion { Objects[j].alive = FALSE; InsertParticles(x,y,z,EXPLODE_AMMO); } if (Objects[j].type == TYPE_FUEL) // larger explosion { Objects[j].alive = FALSE; InsertParticles(x,y,z,EXPLODE_FUEL); } PlayRandomExplosion(0); } // end if collision } // end if object alive } // end for objects // check enemy bullet with player tank if (yb < 7 && xb < Player.x + 5 && xb > Player.x - 5 && zb < Player.z + 5 && zb > Player.z - 5) { // we have collision EBullets[i].alive = FALSE; tank_damage++; PlayRandomExplosion(2); // enemy bullet hit sound if (tank_damage > 2) // on third hit player is dead { PlayRandomExplosion(1); // big boom tanks_left--; if (tanks_left == 0) game_state = GAME_STATE_DEMO_INIT; SetTank(); InsertParticles(Player.x, Player.y, Player.z, EXPLODE_ENEMY); } // end if tank damage } // end if collision } // end if enemy bullet alive } // end for enemy bullets } // END OF CheckCollisions ///////////////////////////////////////////// // Load ASC 3D Model file ///////////////////////////////////////////// bool LoadASCModel() { int i = 0, j = 0; // loop counters int v = 0, f = 0; // vertices and faces float x = 0.0f, y = 0.0f, z = 0.0f; // temp X Y Z point int a = 0, b = 0, c = 0; // temp vertices A B C model_file = fopen("Copter.asc","r"); if (model_file == NULL) return FALSE; ReadALine(); // read Ambient light line ReadALine(); // read blank line ReadALine(); // read Named object line ReadALine(); // read # vertices and faces line sscanf(line, "Tri-mesh, Vertices: %d Faces: %d", &v, &f); if (v == 0 || f == 0 || v > MAX_VERT || f > MAX_VERT) { MessageBox(game_window, "Sorry, model verts/faces = 0 or more than 5000 polygons","LoadASCModel Error",MB_OK); fclose(model_file); return FALSE; } ReadALine(); // read Vertex list line for (i = 0; i < v; i++) { ReadALine(); // read Vertex X Y Z sscanf(line, "Vertex %d: X:%f Y:%f Z:%f", &j, &x, &y, &z); CopterVerts[i][0] = x; CopterVerts[i][1] = y; CopterVerts[i][2] = z; } ReadALine(); // read Face list line // determine total number of triangles (polygons) total_copter_tris += f; if (total_copter_tris > MAX_VERT) // do not allow over 5000 polygons { MessageBox(game_window, "Sorry, models must be under 5000 polygons","LoadASCModel Error",MB_OK); fclose(model_file); return FALSE; // error returned } for (i = 0; i < f; i++) { ReadALine(); // read Faces A B C (triangles) sscanf(line, "Face %d: A:%d B:%d C:%d", &j, &a, &b, &c); CopterTris[i][0] = a; CopterTris[i][1] = b; CopterTris[i][2] = c; ReadALine(); // read Material line ReadALine(); // read Smoothing line } fclose(model_file); return TRUE; // success reading the entire model } // END OF LoadASCModel ///////////////////////////////////////////// // Reads a single line from file ///////////////////////////////////////////// void ReadALine() { char temp[MAX_BUF]; fgets(line, MAX_BUF, model_file); page_lines++; if (page_lines == MAX_LINES) // we hit end of page { page_lines = 0; fgets(temp, MAX_BUF, model_file); // read a line fgets(temp, MAX_BUF, model_file); // read a line fgets(temp, MAX_BUF, model_file); // read a line fgets(temp, MAX_BUF, model_file); // read a line fgets(temp, MAX_BUF, model_file); // read a line } } // END OF ReadALine ///////////////////////////////////////////// // Calculate frames per second (roughly) ///////////////////////////////////////////// void CalculateFPS() { current_time = GetTickCount() * 0.001f; FPS++; // keep track of running FPS if ((current_time - last_time) > 1.0f) // has one second elapsed? { last_time = current_time; // yes save new last time current_FPS = FPS; // save current FPS FPS = 0; } sprintf(text,"FPS=%d", current_FPS); // display current FPS DisplayText(550, height - 30); } // END GAME CODE //////////////////////////////////////////