www.pudn.com > raiders3d_2.rar > raiders3d_2.cpp
// RAIDERS3D_2.CPP - 3D star raiders game version 2.0, 16-bit // READ THIS! // To compile make sure to include DDRAW.LIB, DSOUND.LIB, // DINPUT.LIB, DINPUT8.LIB, WINMM.LIB in the project link list, and of course // the C++ source modules T3DLIB1-7.CPP and the headers T3DLIB1-7.H // be in the working directory of the compiler // PRESSto change the nebula // INCLUDES /////////////////////////////////////////////// #define DEBUG_OFF #define INITGUID // make sure al the COM interfaces are available // instead of this you can include the .LIB file // DXGUID.LIB #define WIN32_LEAN_AND_MEAN #include // include important windows stuff #include #include #include // include important C/C++ stuff #include #include #include #include #include #include #include #include #include #include #include // directX includes #include #include #include #include #include #include #include "T3DLIB1.h" // game library includes #include "T3DLIB2.h" #include "T3DLIB3.h" #include "T3DLIB4.h" #include "T3DLIB5.h" #include "T3DLIB6.h" #include "T3DLIB7.h" // DEFINES //////////////////////////////////////////////// // defines for windows interface #define WINDOW_CLASS_NAME "WIN3DCLASS" // class name #define WINDOW_TITLE "T3D Graphics Console Ver 2.0" #define WINDOW_WIDTH 800 // size of window #define WINDOW_HEIGHT 600 #define WINDOW_BPP 16 // bitdepth of window (8,16,24 etc.) // note: if windowed and not // fullscreen then bitdepth must // be same as system bitdepth // also if 8-bit the a pallete // is created and attached #define WINDOWED_APP 0 // 0 not windowed, 1 windowed // 3D engine constants for overlay star field #define NEAR_Z 10 // the near clipping plane #define FAR_Z 2000 // the far clipping plane #define VIEW_DISTANCE 400 // viewing distance from viewpoint // this gives a field of view of 90 degrees // when projected on a window of 800 wide // create some constants for ease of access #define AMBIENT_LIGHT_INDEX 0 // ambient light index #define INFINITE_LIGHT_INDEX 1 // infinite light index #define POINT_LIGHT_INDEX 2 // point light index #define POINT_LIGHT2_INDEX 3 // point light index #define SPOT_LIGHT2_INDEX 4 // spot light index // alien defines #define NUM_ALIENS 16 // total number of ALIEN fighters // states for aliens #define ALIEN_STATE_DEAD 0 // alien is dead #define ALIEN_STATE_ALIVE 1 // alien is alive #define ALIEN_STATE_DYING 2 // alien is in process of dying // star defines #define NUM_STARS 256 // number of stars in sim // game state defines #define GAME_STATE_INIT 0 // game initializing for first time #define GAME_STATE_RESTART 1 // game restarting after game over #define GAME_STATE_RUN 2 // game running normally #define GAME_STATE_DEMO 3 // game in demo mode #define GAME_STATE_OVER 4 // game over #define GAME_STATE_EXIT 5 // game state exit #define MAX_MISSES_GAME_OVER 25 // miss this many and it's game over man // interface text positions #define TPOS_SCORE_X 346 // players score #define TPOS_SCORE_Y 4 #define TPOS_HITS_X 250 // total kills #define TPOS_HITS_Y 36 #define TPOS_ESCAPED_X 224 // aliens that have escaped #define TPOS_ESCAPED_Y 58 #define TPOS_GLEVEL_X 430 // diff level of game #define TPOS_GLEVEL_Y 58 #define TPOS_SPEED_X 404 // diff level of game #define TPOS_SPEED_Y 36 #define TPOS_GINFO_X 400 // general information #define TPOS_GINFO_Y 240 #define TPOS_ENERGY_X 158 // weapon energy level #define TPOS_ENERGY_Y 6 // player defines #define CROSS_START_X 0 #define CROSS_START_Y 0 #define CROSS_WIDTH 56 #define CROSS_HEIGHT 56 // font display stuff #define FONT_HPITCH 12 // space between chars #define FONT_VPITCH 14 // space between lines // difficulty stuff #define DIFF_RATE (float)(.001f) // rate to advance difficulty per frame #define DIFF_PMAX 75 // explosion stuff #define NUM_EXPLOSIONS 16 // PROTOTYPES ///////////////////////////////////////////// // game console funtions int Game_Init(void *parms=NULL); int Game_Shutdown(void *parms=NULL); int Game_Main(void *parms=NULL); // starfield functions void Draw_Starfield(UCHAR *video_buffer, int lpitch); void Move_Starfield(void); void Init_Starfield(void); // alien functions void Process_Aliens(void); void Draw_Aliens(void); void Init_Aliens(void); int Start_Alien(void); // explosions int Start_Mesh_Explosion(OBJECT4DV2_PTR obj, // object to destroy MATRIX4X4_PTR mrot, // initial orientation int detail, // the detail level,1 highest detail float rate, // velocity of explosion shrapnel int lifetime); // total lifetime of explosion void Draw_Mesh_Explosions(void); // font functions int Draw_Bitmap_Font_String(BOB_PTR font, int x, int y, char *string, int hpitch, int vpitch, LPDIRECTDRAWSURFACE7 dest); int Load_Bitmap_Font(char *fontfile, BOB_PTR font); // TYPES /////////////////////////////////////////////////// // this a 3D star typedef struct STAR3D_TYP { USHORT color; // color of point 16-bit float x,y,z; // coordinates of point in 3D } STAR3D, *STAR3D_PTR; // ALIEN fighter object typedef struct ALIEN_TYP { int state; // state of this ALIEN fighter int count; // generic counter int aux; // generic auxialiary var POINT3D pos; // position of ALIEN fighter VECTOR3D vel; // velocity of ALIEN fighter VECTOR3D rot; // rotational velocities VECTOR3D ang; // current orientation } ALIEN, *ALIEN_PTR; // GLOBALS ///////////////////////////////////////////////// HWND main_window_handle = NULL; // save the window handle HINSTANCE main_instance = NULL; // save the instance char buffer[2048]; // used to print text // initialize camera position and direction POINT4D cam_pos = {0,0,0,1}; POINT4D cam_target = {0,0,0,1}; VECTOR4D cam_dir = {0,0,0,1}; // all your initialization code goes here... VECTOR4D vscale={1.0,1.0,1.0,1}, vpos = {0,0,0,1}, vrot = {0,0,0,1}; CAM4DV1 cam; // the single camera RENDERLIST4DV2 rend_list; // the render list // color constants for line drawing USHORT rgb_green, rgb_white, rgb_red, rgb_blue; // colors for lights RGBAV1 white, light_gray, gray, black, red, green, blue; // music and sound stuff int main_track_id = -1, // main music track id laser_id = -1, // sound of laser pulse explosion_id = -1, // sound of explosion flyby_id = -1, // sound of ALIEN fighter flying by game_over_id = -1, // game over ambient_systems_id = -1; // ambient sounds // star globals STAR3D stars[NUM_STARS]; // the starfield // player and game globals int player_z_vel = 4; // virtual speed of viewpoint/ship float cross_x = 0, // cross hairs cross_y = 0; int cross_x_screen = WINDOW_WIDTH/2, // used for cross hair cross_y_screen = WINDOW_HEIGHT/2, target_x_screen = WINDOW_WIDTH/2, // used for targeter target_y_screen = WINDOW_HEIGHT/2; int escaped = 0; // tracks number of missed ships int hits = 0; // tracks number of hits int score = 0; // take a guess :) int weapon_energy = 100; // weapon energy int weapon_active = 0; // tracks if weapons are energized int game_state = GAME_STATE_INIT; // state of game int game_state_count1 = 0; // general counters int game_state_count2 = 0; int restart_state = 0; // state when game is restarting int restart_state_count1 = 0; // general counter // diffiuculty float diff_level = 1; // difficulty level used in velocity and probability calcs // alien globals ALIEN aliens[NUM_ALIENS]; // the ALIEN fighters OBJECT4DV2 obj_alien; // the master object // explosions OBJECT4DV2 explobj[NUM_EXPLOSIONS]; // the explosions // game imagery assets BOB cockpit; // the cockpit image BOB starscape; // the background starscape BOB tech_font; // the bitmap font for the engine BOB crosshair; // the player's cross hair // these are the positions of the energy binding on the main lower control panel POINT2D energy_bindings[6] = { {342, 527}, {459, 527}, {343, 534}, {458, 534}, {343, 540}, {458, 540} }; // these hold the positions of the weapon burst which use lighting too // the starting points are known, but the end points are computed on the fly // based on the cross hair POINT2D weapon_bursts[4] = { {78, 500}, {0,0}, // left energy weapon {720, 500}, {0,0} }; // right energy weapon int px = 0, py = 0, pz = 500 ; // debug stuff for object tracking // FUNCTIONS ////////////////////////////////////////////// LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { // this is the main message handler of the system PAINTSTRUCT ps; // used in WM_PAINT HDC hdc; // handle to a device context // what is the message switch(msg) { case WM_CREATE: { // do initialization stuff here return(0); } break; case WM_PAINT: { // start painting hdc = BeginPaint(hwnd,&ps); // end painting EndPaint(hwnd,&ps); return(0); } break; case WM_DESTROY: { // kill the application PostQuitMessage(0); return(0); } 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) { // this is the winmain function WNDCLASS winclass; // this will hold the class we create HWND hwnd; // generic window handle MSG msg; // generic message HDC hdc; // generic dc PAINTSTRUCT ps; // generic paintstruct // first fill in the window class stucture winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW; winclass.lpfnWndProc = WindowProc; winclass.cbClsExtra = 0; winclass.cbWndExtra = 0; winclass.hInstance = hinstance; winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); winclass.hCursor = LoadCursor(NULL, IDC_ARROW); winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); winclass.lpszMenuName = NULL; winclass.lpszClassName = WINDOW_CLASS_NAME; // register the window class if (!RegisterClass(&winclass)) return(0); // create the window, note the test to see if WINDOWED_APP is // true to select the appropriate window flags if (!(hwnd = CreateWindow(WINDOW_CLASS_NAME, // class WINDOW_TITLE, // title (WINDOWED_APP ? (WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION) : (WS_POPUP | WS_VISIBLE)), 0,0, // x,y WINDOW_WIDTH, // width WINDOW_HEIGHT, // height NULL, // handle to parent NULL, // handle to menu hinstance,// instance NULL))) // creation parms return(0); // save the window handle and instance in a global main_window_handle = hwnd; main_instance = hinstance; // resize the window so that client is really width x height if (WINDOWED_APP) { // now resize the window, so the client area is the actual size requested // since there may be borders and controls if this is going to be a windowed app // if the app is not windowed then it won't matter RECT window_rect = {0,0,WINDOW_WIDTH-1,WINDOW_HEIGHT-1}; // make the call to adjust window_rect AdjustWindowRectEx(&window_rect, GetWindowStyle(main_window_handle), GetMenu(main_window_handle) != NULL, GetWindowExStyle(main_window_handle)); // save the global client offsets, they are needed in DDraw_Flip() window_client_x0 = -window_rect.left; window_client_y0 = -window_rect.top; // now resize the window with a call to MoveWindow() MoveWindow(main_window_handle, 0, // x position 0, // y position window_rect.right - window_rect.left, // width window_rect.bottom - window_rect.top, // height FALSE); // show the window, so there's no garbage on first render ShowWindow(main_window_handle, SW_SHOW); } // end if windowed // perform all game console specific initialization Game_Init(); // disable CTRL-ALT_DEL, ALT_TAB, comment this line out // if it causes your system to crash SystemParametersInfo(SPI_SCREENSAVERRUNNING, TRUE, NULL, 0); // enter main event loop while(1) { 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 the window proc DispatchMessage(&msg); } // end if // main game processing goes here Game_Main(); } // end while // shutdown game and release all resources Game_Shutdown(); // enable CTRL-ALT_DEL, ALT_TAB, comment this line out // if it causes your system to crash SystemParametersInfo(SPI_SCREENSAVERRUNNING, FALSE, NULL, 0); // return to Windows like this return(msg.wParam); } // end WinMain //////////////////////////////////////////////////////////////////// void Render_Energy_Bindings(POINT2D_PTR bindings, // array containing binding positions in the form // start point, end point, start point, end point... int num_bindings, // number of energy bindings to render 1-3 int num_segments, // number of segments to randomize bindings into int amplitude, // amplitude of energy binding int color, // color of bindings UCHAR *video_buffer, // video buffer to render int lpitch) // memory pitch of buffer { // this functions renders the energy bindings across the main exterior energy // transmission emitters :) Basically, the function takes two points in 2d then // it anchors a line at the two ends and randomly walks from end point to the // other by breaking the line into segments and then randomly modulating the y position // and amount amplitude +-, maximum number of segments 16 POINT2D segments[17]; // to hold constructed segments after construction // render each binding for (int index = 0; index < num_bindings; index++) { // store starting and ending positions // starting position segments[0] = bindings[index*2]; // ending position segments[num_segments] = bindings[index*2+1]; // compute vertical gradient, so if y positions of endpoints are // greatly different bindings will be modulated using the straight line // as a basis float dyds = (segments[num_segments].y - segments[0].y) / (float)num_segments; float dxds = (segments[num_segments].x - segments[0].x) / (float)num_segments; // now build up segments for (int sindex = 1; sindex < num_segments; sindex++) { segments[sindex].x = segments[sindex-1].x + dxds; segments[sindex].y = segments[0].y + sindex*dyds + RAND_RANGE(-amplitude, amplitude); } // end for segment // draw binding for (sindex = 0; sindex < num_segments; sindex++) Draw_Line16(segments[sindex].x, segments[sindex].y, segments[sindex+1].x, segments[sindex+1].y, color, video_buffer, lpitch); } // end for index } // end Render_Energy_Binding //////////////////////////////////////////////////////////////////// void Render_Weapon_Bursts(POINT2D_PTR burstpoints, // array containing energy burst positions in the form // start point, end point, start point, end point... int num_bursts, int num_segments, // number of segments to randomize bindings into int amplitude, // amplitude of energy binding int color, // color of bindings UCHAR *video_buffer, // video buffer to render int lpitch) // memory pitch of buffer { // this functions renders the weapon energy bursts from the weapon emitter or wherever // function derived from Render_Energy_Binding, but generalized // Basically, the function takes two points in 2d then // it anchors a line at the two ends and randomly walks from end point to the // other by breaking the line into segments and then randomly modulating the x,y position // and amount amplitude +-, maximum number of segments 16 POINT2D segments[17]; // to hold constructed segments after construction // render each energy burst for (int index = 0; index < num_bursts; index++) { // store starting and ending positions // starting position segments[0] = burstpoints[index*2]; // ending position segments[num_segments] = burstpoints[index*2+1]; // compute horizontal/vertical gradients, so we can modulate the lines // on the proper trajectory float dyds = (segments[num_segments].y - segments[0].y) / (float)num_segments; float dxds = (segments[num_segments].x - segments[0].x) / (float)num_segments; // now build up segments for (int sindex = 1; sindex < num_segments; sindex++) { segments[sindex].x = segments[0].x + sindex*dxds + RAND_RANGE(-amplitude, amplitude); segments[sindex].y = segments[0].y + sindex*dyds + RAND_RANGE(-amplitude, amplitude); } // end for segment // draw binding for (sindex = 0; sindex < num_segments; sindex++) Draw_Line16(segments[sindex].x, segments[sindex].y, segments[sindex+1].x, segments[sindex+1].y, color, video_buffer, lpitch); } // end for index } // end Render_Weapons_Bursts /////////////////////////////////////////////////////////////// int Load_Bitmap_Font(char *fontfile, BOB_PTR font) { // this is a semi generic font loader... // expects the file name of a font in a template that is // 4 rows of 16 cells, each character is 16x14, and cell 0 is the space character // characters from 32 to 95, " " to "-", suffice for 90% of text work // load the font bitmap template if (!Load_Bitmap_File(&bitmap16bit, fontfile)) return(0); // create the bob that will hold the font, use a bob for speed, we can use the // hardware blitter Create_BOB(font, 0,0,16,14,64, BOB_ATTR_VISIBLE | BOB_ATTR_SINGLE_FRAME, DDSCAPS_SYSTEMMEMORY, 0, 16); // load all the frames for (int index=0; index < 64; index++) Load_Frame_BOB16(font, &bitmap16bit,index,index%16,index/16,BITMAP_EXTRACT_MODE_CELL); // unload the bitmap file Unload_Bitmap_File(&bitmap16bit); // return success return(1); } // end Load_Bitmap_Font /////////////////////////////////////////////////////////////// int Draw_Bitmap_Font_String(BOB_PTR font, // pointer to bob containing font int x, int y, // screen position to render char *string, // string to render int hpitch, int vpitch, // horizontal and vertical pitch LPDIRECTDRAWSURFACE7 dest) // destination surface { // this function draws a string based on a 64 character font sent in as a bob // the string will be drawn at the given x,y position with intercharacter spacing // if hpitch and a interline spacing of vpitch // are things semi valid? if (!string || !dest) return(0); // loop and render for (int index = 0; index < strlen(string); index++) { // set the position and character font->x = x; font->y = y; font->curr_frame = string[index] - 32; // test for overflow set to space if (font->curr_frame > 63 || font->curr_frame < 0) font->curr_frame = 0; // render character (i hate making a function call!) Draw_BOB16(font, dest); // move position x+=hpitch; } // end for index // return success return(1); } // end Draw_Bitmap_Font_String ///////////////////////////////////////////////////////// int Start_Alien(void) { // this function hunts in the alien list, finds an available alien and // starts it up for (int index = 0; index < NUM_ALIENS; index++) { // is this alien available? if (aliens[index].state == ALIEN_STATE_DEAD) { // clear any residual data memset((void *)&aliens[index], 0, sizeof (ALIEN) ); // start alien up aliens[index].state = ALIEN_STATE_ALIVE; // select random position in bounding volume aliens[index].pos.x = RAND_RANGE(-1000,1000); aliens[index].pos.y = RAND_RANGE(-1000,1000); aliens[index].pos.z = 20000; // select velocity based on difficulty level aliens[index].vel.x = RAND_RANGE(-10,10); aliens[index].vel.y = RAND_RANGE(-10,10); aliens[index].vel.z = -(10*diff_level+rand()%200); // set rotation rate for z axis only aliens[index].rot.z = RAND_RANGE(-5,5); // return the index return(index); } // end if } // end for index // failure return(-1); } // end Start_Alien ///////////////////////////////////////////////////////// void Init_Aliens(void) { // initializes all the ALIEN fighters to a known state for (int index = 0; index < NUM_ALIENS; index++) { // zero ALIEN fighter out memset((void *)&aliens[index], 0, sizeof (ALIEN) ); // set any other specific info now... aliens[index].state = ALIEN_STATE_DEAD; } // end for } // end Init_Aliens ///////////////////////////////////////////////////////// void Draw_Aliens(void) { // this function draws all the active ALIEN fighters MATRIX4X4 mrot; // used to transform objects for (int index = 0; index < NUM_ALIENS; index++) { // which state is alien in switch(aliens[index].state) { // is the alien dead? if so move on case ALIEN_STATE_DEAD: break; // is the alien alive? case ALIEN_STATE_ALIVE: case ALIEN_STATE_DYING: { // reset the object (this only matters for backface and object removal) Reset_OBJECT4DV2(&obj_alien); // generate rotation matrix around y axis Build_XYZ_Rotation_MATRIX4X4(aliens[index].ang.x, aliens[index].ang.y, aliens[index].ang.z ,&mrot); // rotate the local coords of the object Transform_OBJECT4DV2(&obj_alien, &mrot, TRANSFORM_LOCAL_TO_TRANS,1); // set position of constant shaded cube obj_alien.world_pos.x = aliens[index].pos.x; obj_alien.world_pos.y = aliens[index].pos.y; obj_alien.world_pos.z = aliens[index].pos.z; // attempt to cull object if (!Cull_OBJECT4DV2(&obj_alien, &cam, CULL_OBJECT_XYZ_PLANES)) { // perform world transform Model_To_World_OBJECT4DV2(&obj_alien, TRANSFORM_TRANS_ONLY); // insert the object into render list Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_alien,0); } // end if // ok, now test for collision with energy bursts, strategy is as follows // we will project the bounding box of the object into screen space to coincide with // the energy burst on the screen, if there is an overlap then the target is hit // simply need to do a few transforms, this kinda sucks that we are going to do this work // 2x, but the problem is that the graphics pipeline knows nothing about collision etc., // so we can't "get to" the information me want since we are ripping the objects apart // and passing them down the pipeline for a later world to camera transform, thus you // may want to think about this problem when making your own game, how tight to couple // collision and the engine, HOWEVER, the only reason we are having a problem at all is that // we want to use the screen coords of the energy burst this is fine, but a more effective method // would be to compute the 3D world coords of where the energy burst is firing and then project // that parallelpiped in 3D space to see where the player is trying to fire, this is a classic // problem with screen picking, hence, in the engine, when an object is rendered sometimes its // a good idea to track somekind of collision boundary in screen coords that can be used later // for "object picking" and collision, anyway, let's do it the easy way, but the long way.... // first is the player firing weapon? if (weapon_active) { // we need 4 transforms total, first we need all our points in world coords, // then we need camera coords, then perspective coords, then screen coords // we need to transform 2 points: the center and a point lying on the surface of the // bounding sphere, as long as the POINT4D pbox[4], // bounding box coordinates, center points, surrounding object // denoted by X's, we need to project these to screen coords // ....X.... // . | | . // X |-O-| X // . | | . // ....X.... // we will use the average radius as the distance to each X from the center presult; // used to hold temp results // world to camera transform // transform center point only Mat_Mul_VECTOR4D_4X4(&obj_alien.world_pos, &cam.mcam, &presult); // result holds center of object in camera coords now // now we are in camera coords, aligned to z-axis, compute radial point axis aligned // bounding box points // x+r, y, z pbox[0].x = presult.x + obj_alien.avg_radius[obj_alien.curr_frame]; pbox[0].y = presult.y; pbox[0].z = presult.z; pbox[0].w = 1; // x-r, y, z pbox[1].x = presult.x - obj_alien.avg_radius[obj_alien.curr_frame]; pbox[1].y = presult.y; pbox[1].z = presult.z; pbox[1].w = 1; // x, y+r, z pbox[2].x = presult.x; pbox[2].y = presult.y + obj_alien.avg_radius[obj_alien.curr_frame]; pbox[2].z = presult.z; pbox[2].w = 1; // x, y-r, z pbox[3].x = presult.x; pbox[3].y = presult.y - obj_alien.avg_radius[obj_alien.curr_frame]; pbox[3].z = presult.z; pbox[3].w = 1; // now we are ready to project the points to screen space float alpha = (0.5*cam.viewport_width-0.5); float beta = (0.5*cam.viewport_height-0.5); // loop and process each point for (int bindex=0; bindex < 4; bindex++) { float z = pbox[bindex].z; // perspective transform first pbox[bindex].x = cam.view_dist*pbox[bindex].x/z; pbox[bindex].y = cam.view_dist*pbox[bindex].y*cam.aspect_ratio/z; // z = z, so no change // screen projection pbox[bindex].x = alpha*pbox[bindex].x + alpha; pbox[bindex].y = -beta*pbox[bindex].y + beta; } // end for bindex #ifdef DEBUG_ON // now we have the 4 points is screen coords and we can test them!!! ya!!!! Draw_Clip_Line16(pbox[0].x, pbox[2].y, pbox[1].x, pbox[2].y, rgb_red, back_buffer, back_lpitch); Draw_Clip_Line16(pbox[0].x, pbox[3].y, pbox[1].x, pbox[3].y, rgb_red, back_buffer, back_lpitch); Draw_Clip_Line16(pbox[0].x, pbox[2].y, pbox[0].x, pbox[3].y, rgb_red, back_buffer, back_lpitch); Draw_Clip_Line16(pbox[1].x, pbox[2].y, pbox[1].x, pbox[3].y, rgb_red, back_buffer, back_lpitch); #endif // test for collision if ((cross_x_screen > pbox[1].x) && (cross_x_screen < pbox[0].x) && (cross_y_screen > pbox[2].y) && (cross_y_screen < pbox[3].y) ) { Start_Mesh_Explosion(&obj_alien, // object to blow &mrot, // initial orientation 3, // the detail level,1 highest detail .5, // velocity of explosion shrapnel 100); // total lifetime of explosion // remove from simulation aliens[index].state = ALIEN_STATE_DEAD; // make some sound DSound_Play(explosion_id); // increment hits hits++; // take into consideration the z, the speed, the level, blah blah score += (int)((diff_level*10 + obj_alien.world_pos.z/10)); } // end if } // end if weapon active } break; default: break; } // end switch } // end for index // debug code #ifdef DEBUG_ON // reset the object (this only matters for backface and object removal) Reset_OBJECT4DV2(&obj_alien); static int ang_y = 0; // generate rotation matrix around y axis Build_XYZ_Rotation_MATRIX4X4(0, ang_y++, 0 ,&mrot); // rotate the local coords of the object Transform_OBJECT4DV2(&obj_alien, &mrot, TRANSFORM_LOCAL_TO_TRANS,1); // set position of constant shaded cube obj_alien.world_pos.x = px; obj_alien.world_pos.y = py; obj_alien.world_pos.z = pz; // perform world transform Model_To_World_OBJECT4DV2(&obj_alien, TRANSFORM_TRANS_ONLY); // insert the object into render list Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &obj_alien,0); #endif } // end Draw_Aliens ////////////////////////////////////////////////////////// void Draw_Mesh_Explosions(void) { // this function draws the mesh explosions MATRIX4X4 mrot; // used to transform objects // draw the explosions, note we do NOT cull them for (int eindex = 0; eindex < NUM_EXPLOSIONS; eindex++) { // is the mesh explosions active if ((explobj[eindex].state & OBJECT4DV2_STATE_ACTIVE)) { // reset the object Reset_OBJECT4DV2(&explobj[eindex]); // generate rotation matrix Build_XYZ_Rotation_MATRIX4X4(0, 0, 0 ,&mrot); // rotate the local coords of the object Transform_OBJECT4DV2(&explobj[eindex], &mrot, TRANSFORM_LOCAL_TO_TRANS,1); // perform world transform Model_To_World_OBJECT4DV2(&explobj[eindex], TRANSFORM_TRANS_ONLY); // insert the object into render list Insert_OBJECT4DV2_RENDERLIST4DV2(&rend_list, &explobj[eindex],0); // now animate the mesh VECTOR4D_PTR trajectory = (VECTOR4D_PTR)explobj[eindex].ivar1; for (int pindex = 0; pindex < explobj[eindex].num_polys; pindex++) { // vertex 0 VECTOR4D_Add(&explobj[eindex].vlist_local[pindex*3 + 0].v, &trajectory[pindex*2+0], &explobj[eindex].vlist_local[pindex*3 + 0].v); // vertex 1 VECTOR4D_Add(&explobj[eindex].vlist_local[pindex*3 + 1].v, &trajectory[pindex*2 + 0], &explobj[eindex].vlist_local[pindex*3 + 1].v); // vertex 2 VECTOR4D_Add(&explobj[eindex].vlist_local[pindex*3 + 2].v, &trajectory[pindex*2 + 0], &explobj[eindex].vlist_local[pindex*3 + 2].v); // modulate color //explobj[eindex].plist[pindex].color = RGB16Bit(rand()%255,0,0); } // end for pindex // update counter, test for terminate if (--explobj[eindex].ivar2 < 0) { // reset this object explobj[eindex].state = OBJECT4DV2_STATE_NULL; // release memory of object, but only data that isn't linked to master object // local vertex list if (explobj[eindex].head_vlist_local) free(explobj[eindex].head_vlist_local); // transformed vertex list if (explobj[eindex].head_vlist_trans) free(explobj[eindex].head_vlist_trans); // polygon list if (explobj[eindex].plist) free(explobj[eindex].plist); // trajectory list if ((VECTOR4D_PTR)(explobj[eindex].ivar1)) free((VECTOR4D_PTR)explobj[eindex].ivar1); // now clear out object completely memset((void *)&explobj[eindex], 0, sizeof(OBJECT4DV2)); } // end if } // end if } // end for eindex } // end Draw_Mesh_Explosions ////////////////////////////////////////////////////////// int Start_Mesh_Explosion(OBJECT4DV2_PTR obj, // object to destroy MATRIX4X4_PTR mrot, // initial orientation of object int detail, // the detail level,1 highest detail float rate, // velocity of explosion shrapnel int lifetime) // total lifetime of explosion { // this function "blows up" an object, it takes a pointer to the object to // be destroyed, the detail level of the polyogonal explosion, 1 is high detail, // numbers greater than 1 are lower detail, the detail selects the polygon extraction // stepping, hence a detail of 5 would mean every 5th polygon in the original mesh // should be part of the explosion, on average no more than 10-50% of the polygons in // the original mesh should be used for the explosion; some would be vaporized and // in a more practical sense, when an object is far, there's no need to have detail // the next parameter is the rate which is used as a general scale for the explosion // velocity, and finally lifetime which is the number of cycles to display the explosion // the function works by taking the original object then copying the core information // except for the polygon and vertex lists, the function operates by selecting polygon // by polygon to be destroyed and then makes a copy of the polygon AND all of it's vertices, // thus the vertex coherence is completely lost, this is a necessity since each polygon must // be animated separately by the engine, thus they can't share vertices, additionally at the // end if the vertex list will be the velocity and rotation information for each polygon // this is hidden to the rest of the engine, now for the explosion, the center of the object // is used as the point of origin then a ray is drawn thru each polygon, then each polygon // is thrown at some velocity with a small rotation rate // finally the system can only have so many explosions at one time // step: 1 find an available explosion for (int eindex = 0; eindex < NUM_EXPLOSIONS; eindex++) { // is this explosion available? if (explobj[eindex].state == OBJECT4DV2_STATE_NULL) { // copy the object, including the pointers which we will unlink shortly... explobj[eindex] = *obj; explobj[eindex].state = OBJECT4DV2_STATE_ACTIVE | OBJECT4DV2_STATE_VISIBLE; // recompute a few things explobj[eindex].num_polys = obj->num_polys/detail; explobj[eindex].num_vertices = 3*obj->num_polys; explobj[eindex].total_vertices = 3*obj->num_polys; // one frame only // unlink/re-allocate all the pointers except the texture coordinates, we can use those, they don't // depend on vertex coherence // allocate memory for vertex lists if (!(explobj[eindex].vlist_local = (VERTEX4DTV1_PTR)malloc(sizeof(VERTEX4DTV1)*explobj[eindex].num_vertices))) return(0); // clear data memset((void *)explobj[eindex].vlist_local,0,sizeof(VERTEX4DTV1)*explobj[eindex].num_vertices); if (!(explobj[eindex].vlist_trans = (VERTEX4DTV1_PTR)malloc(sizeof(VERTEX4DTV1)*explobj[eindex].num_vertices))) return(0); // clear data memset((void *)explobj[eindex].vlist_trans,0,sizeof(VERTEX4DTV1)*explobj[eindex].num_vertices); // allocate memory for polygon list if (!(explobj[eindex].plist = (POLY4DV2_PTR)malloc(sizeof(POLY4DV2)*explobj[eindex].num_polys))) return(0); // clear data memset((void *)explobj[eindex].plist,0,sizeof(POLY4DV2)*explobj[eindex].num_polys); // now, we need somewhere to store the vector trajectories of the polygons and their rotational rates // so let's allocate an array of VECTOR4D elements to hold this information and then store it // in ivar1, therby using ivar as a pointer, this is perfectly fine :) // each record will consist of a velocity and a rotational rate in x,y,z, so V0,R0, V1,R1,...Vn-1, Rn-1 // allocate memory for polygon list if (!(explobj[eindex].ivar1 = (int)malloc(sizeof(VECTOR4D)*2*explobj[eindex].num_polys))) return(0); // clear data memset((void *)explobj[eindex].ivar1,0,sizeof(VECTOR4D)*2*explobj[eindex].num_polys); // alias working pointer VECTOR4D_PTR trajectory = (VECTOR4D_PTR)explobj[eindex].ivar1; // these are needed to track the "head" of the vertex list for multi-frame objects explobj[eindex].head_vlist_local = explobj[eindex].vlist_local; explobj[eindex].head_vlist_trans = explobj[eindex].vlist_trans; // set the lifetime in ivar2 explobj[eindex].ivar2 = lifetime; // now comes the tricky part, loop and copy each polygon, but as we copy the polygons, we have to copy // the vertices, and insert them, and fix up the vertice indices etc. for (int pindex = 0; pindex < explobj[eindex].num_polys; pindex++) { // alright, we want to copy the (pindex*detail)th poly from the master to the (pindex)th // polygon in explosion explobj[eindex].plist[pindex] = obj->plist[pindex*detail]; // we need to modify, the vertex list pointer and the vertex indices themselves explobj[eindex].plist[pindex].vlist = explobj[eindex].vlist_local; // now comes the hard part, we need to first copy the vertices from the original mesh // into the new vertex storage, but at the same time keep the same vertex ordering // vertex 0 explobj[eindex].plist[pindex].vert[0] = pindex*3 + 0; explobj[eindex].vlist_local[pindex*3 + 0] = obj->vlist_local[ obj->plist[pindex*detail].vert[0] ]; // vertex 1 explobj[eindex].plist[pindex].vert[1] = pindex*3 + 1; explobj[eindex].vlist_local[pindex*3 + 1] = obj->vlist_local[ obj->plist[pindex*detail].vert[1] ]; // vertex 2 explobj[eindex].plist[pindex].vert[2] = pindex*3 + 2; explobj[eindex].vlist_local[pindex*3 + 2] = obj->vlist_local[ obj->plist[pindex*detail].vert[2] ]; // reset shading flag to constant for some of the shrapnel since they are giving off light if ((rand()%5) == 1) { SET_BIT(explobj[eindex].plist[pindex].attr, POLY4DV2_ATTR_SHADE_MODE_PURE); RESET_BIT(explobj[eindex].plist[pindex].attr, POLY4DV2_ATTR_SHADE_MODE_GOURAUD); RESET_BIT(explobj[eindex].plist[pindex].attr, POLY4DV2_ATTR_SHADE_MODE_FLAT); // set color explobj[eindex].plist[pindex].color = RGB16Bit(128+rand()%128,0,0); } // end if // add some random noise to trajectory static VECTOR4D mv; // first compute trajectory as vector from center to vertex piercing polygon VECTOR4D_INIT(&trajectory[pindex*2+0], &obj->vlist_local[ obj->plist[pindex*detail].vert[0] ].v); VECTOR4D_Scale(rate, &trajectory[pindex*2+0], &trajectory[pindex*2+0]); mv.x = RAND_RANGE(-10,10); mv.y = RAND_RANGE(-10,10); mv.z = RAND_RANGE(-10,10); mv.w = 1; VECTOR4D_Add(&mv, &trajectory[pindex*2+0], &trajectory[pindex*2+0]); // now rotation rate, this is difficult to do since we don't have the center of the polygon // maybe later... // trajectory[pindex*2+1] = } // end for pindex // now transform final mesh into position destructively Transform_OBJECT4DV2(&explobj[eindex], mrot, TRANSFORM_LOCAL_ONLY,1); return(1); } // end if found a free explosion } // end for eindex // return failure return(0); } // end Start_Mesh_Explosion /////////////////////////////////////////////////////////// void Process_Aliens(void) { // this function moves the aliens and performs AI for (int index = 0; index < NUM_ALIENS; index++) { // which state is alien in switch(aliens[index].state) { // is the alien dead? if so move on case ALIEN_STATE_DEAD: break; // is the alien alive? case ALIEN_STATE_ALIVE: case ALIEN_STATE_DYING: { // move the alien aliens[index].pos.x+=aliens[index].vel.y; aliens[index].pos.y+=aliens[index].vel.y; aliens[index].pos.z+=aliens[index].vel.z; // rotate the alien if ((aliens[index].ang.x+=aliens[index].rot.x) > 360) aliens[index].ang.x = 0; if ((aliens[index].ang.y+=aliens[index].rot.y) > 360) aliens[index].ang.y = 0; if ((aliens[index].ang.z+=aliens[index].rot.z) > 360) aliens[index].ang.z = 0; // test if alien has past players near clipping plane // if so remove from simulation if (aliens[index].pos.z < cam.near_clip_z) { // remove from simulation aliens[index].state = ALIEN_STATE_DEAD; // another one gets away! escaped++; } // end if } break; default: break; } // end switch } // end for index } // end Process_Aliens ////////////////////////////////////////////////////////// void Init_Starfield(void) { // create the starfield for (int index=0; index < NUM_STARS; index++) { // randomly position stars in an elongated cylinder stretching from // the viewpoint 0,0,-d to the yon clipping plane 0,0,far_z stars[index].x = -WINDOW_WIDTH/2 + rand()%WINDOW_WIDTH; stars[index].y = -WINDOW_HEIGHT/2 + rand()%WINDOW_HEIGHT; stars[index].z = NEAR_Z + rand()%(FAR_Z - NEAR_Z); // set color of stars stars[index].color = rgb_white; } // end for index } // end Init_Starfield ////////////////////////////////////////////////////////// void Move_Starfield(void) { // move the stars int index; // looping var // the stars are technically stationary,but we are going // to move them to simulate motion of the viewpoint for (index=0; index >=1; // draw each star for (index=0; index < NUM_STARS; index++) { // draw the next star // step 1: perspective transform float x_per = VIEW_DISTANCE*stars[index].x/stars[index].z; float y_per = VIEW_DISTANCE*stars[index].y/stars[index].z; // step 2: compute screen coords int x_screen = WINDOW_WIDTH/2 + x_per; int y_screen = WINDOW_HEIGHT/2 - y_per; // clip to screen coords if (x_screen>=WINDOW_WIDTH || x_screen < 0 || y_screen >= WINDOW_HEIGHT || y_screen < 0) { // continue to next star continue; } // end if else { // else render to buffer int i = ((float)(10000*NEAR_Z) / stars[index].z); if (i > 255) i = 255; ((USHORT *)video_buffer)[x_screen + y_screen*lpitch] = RGB16Bit(i,i,i); } // end else } // end for index } // Draw_Starfield ///////////////////////////////////////////////////////////// // T3D II GAME PROGRAMMING CONSOLE FUNCTIONS //////////////// int Game_Init(void *parms) { // this function is where you do all the initialization // for your game int index; // looping var // start up DirectDraw (replace the parms as you desire) DDraw_Init(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_BPP, WINDOWED_APP); // initialize directinput DInput_Init(); // acquire the keyboard and mouse DInput_Init_Keyboard(); DInput_Init_Mouse(); // add calls to acquire other directinput devices here... // initialize directsound and directmusic DSound_Init(); DMusic_Init(); // load in sound fx explosion_id = DSound_Load_WAV("exp1.wav"); laser_id = DSound_Load_WAV("shocker.wav"); game_over_id = DSound_Load_WAV("gameover.wav"); ambient_systems_id = DSound_Load_WAV("stationthrob2.wav"); // initialize directmusic DMusic_Init(); // load main music track main_track_id = DMusic_Load_MIDI("midifile2.mid"); // hide the mouse //if (!WINDOWED_APP) ShowCursor(FALSE); // seed random number generator srand(Start_Clock()); Open_Error_File("ERROR.TXT"); // initialize math engine Build_Sin_Cos_Tables(); // initialize the camera with 90 FOV, normalized coordinates Init_CAM4DV1(&cam, // the camera object CAM_MODEL_EULER, // the euler model &cam_pos, // initial camera position &cam_dir, // initial camera angles &cam_target, // no target 150.0, // near and far clipping planes 20000.0, 120.0, // field of view in degrees WINDOW_WIDTH, // size of final screen viewport WINDOW_HEIGHT); // load flat shaded cube VECTOR4D_INITXYZ(&vscale,18.00,18.00,18.00); Load_OBJECT4DV2_COB(&obj_alien,"tie04.cob", &vscale, &vpos, &vrot, VERTEX_FLAGS_SWAP_YZ | VERTEX_FLAGS_SWAP_XZ | VERTEX_FLAGS_INVERT_WINDING_ORDER | VERTEX_FLAGS_TRANSFORM_LOCAL | VERTEX_FLAGS_TRANSFORM_LOCAL_WORLD ); // create system colors rgb_green = RGB16Bit(0,255,0); rgb_white = RGB16Bit(255,255,255); rgb_blue = RGB16Bit(0,0,255); rgb_red = RGB16Bit(255,0,0); // set up lights Reset_Lights_LIGHTV1(); // create some working colors white.rgba = _RGBA32BIT(255,255,255,0); light_gray.rgba = _RGBA32BIT(100,100,100,0); gray.rgba = _RGBA32BIT(50,50,50,0); black.rgba = _RGBA32BIT(0,0,0,0); red.rgba = _RGBA32BIT(255,0,0,0); green.rgba = _RGBA32BIT(0,255,0,0); blue.rgba = _RGBA32BIT(0,0,255,0); // ambient light Init_Light_LIGHTV1(AMBIENT_LIGHT_INDEX, LIGHTV1_STATE_ON, // turn the light on LIGHTV1_ATTR_AMBIENT, // ambient light type gray, black, black, // color for ambient term only NULL, NULL, // no need for pos or dir 0,0,0, // no need for attenuation 0,0,0); // spotlight info NA VECTOR4D dlight_dir = {-1,-1,1,0}; // directional sun light Init_Light_LIGHTV1(INFINITE_LIGHT_INDEX, LIGHTV1_STATE_ON, // turn the light on LIGHTV1_ATTR_INFINITE, // infinite light type black, light_gray, black, // color for diffuse term only NULL, &dlight_dir, // need direction only 0,0,0, // no need for attenuation 0,0,0); // spotlight info NA // the red giant VECTOR4D plight_pos = {-2700,-1600,8000,0}; // point light Init_Light_LIGHTV1(POINT_LIGHT_INDEX, LIGHTV1_STATE_ON, // turn the light on LIGHTV1_ATTR_POINT, // pointlight type black, red, black, // color for diffuse term only &plight_pos, NULL, // need pos only 0,.001,0, // linear attenuation only 0,0,1); // spotlight info NA // the weapons blast green glow point light from front of ship VECTOR4D plight2_pos = {0,0,200,0}; // point light2 Init_Light_LIGHTV1(POINT_LIGHT2_INDEX, LIGHTV1_STATE_OFF, // turn the light on LIGHTV1_ATTR_POINT, // pointlight black, green, black, // color for diffuse term only &plight2_pos, NULL, // need pos only 0,.0005,0, // linear attenuation only 0,0,1); // create lookup for lighting engine RGB_16_8_IndexedRGB_Table_Builder(DD_PIXEL_FORMAT565, // format we want to build table for palette, // source palette rgblookup); // lookup table // load in the cockpit image Load_Bitmap_File(&bitmap16bit, "cockpit03.BMP"); Create_BOB(&cockpit, 0,0,800,600,2, BOB_ATTR_VISIBLE | BOB_ATTR_SINGLE_FRAME, DDSCAPS_SYSTEMMEMORY, 0, 16); Load_Frame_BOB16(&cockpit, &bitmap16bit,0,0,0,BITMAP_EXTRACT_MODE_ABS); Unload_Bitmap_File(&bitmap16bit); Load_Bitmap_File(&bitmap16bit, "cockpit03b.BMP"); Load_Frame_BOB16(&cockpit, &bitmap16bit,1,0,0,BITMAP_EXTRACT_MODE_ABS); Unload_Bitmap_File(&bitmap16bit); // load in the background starscape(s) Create_BOB(&starscape, 0,0,800,600,3, BOB_ATTR_VISIBLE | BOB_ATTR_SINGLE_FRAME, DDSCAPS_SYSTEMMEMORY, 0, 16); Load_Bitmap_File(&bitmap16bit, "nebblue01.bmp"); Load_Frame_BOB16(&starscape, &bitmap16bit,0,0,0,BITMAP_EXTRACT_MODE_ABS); Load_Bitmap_File(&bitmap16bit, "nebred01.bmp"); Load_Frame_BOB16(&starscape, &bitmap16bit,1,0,0,BITMAP_EXTRACT_MODE_ABS); Load_Bitmap_File(&bitmap16bit, "nebgreen03.bmp"); Load_Frame_BOB16(&starscape, &bitmap16bit,2,0,0,BITMAP_EXTRACT_MODE_ABS); Unload_Bitmap_File(&bitmap16bit); // load the crosshair image Load_Bitmap_File(&bitmap16bit, "crosshair01.bmp"); Create_BOB(&crosshair, 0,0,CROSS_WIDTH, CROSS_HEIGHT,1, BOB_ATTR_VISIBLE | BOB_ATTR_SINGLE_FRAME, DDSCAPS_SYSTEMMEMORY, 0, 16); Load_Frame_BOB16(&crosshair, &bitmap16bit,0,0,0,BITMAP_EXTRACT_MODE_ABS); Unload_Bitmap_File(&bitmap16bit); // set single precission //_control87( _PC_24, MCW_PC ); return(1); } // end Game_Init /////////////////////////////////////////////////////////// int Game_Shutdown(void *parms) { // this function is where you shutdown your game and // release all resources that you allocated // shut everything down // release all your resources created for the game here.... // now directsound DSound_Stop_All_Sounds(); DSound_Delete_All_Sounds(); DSound_Shutdown(); // directmusic DMusic_Delete_All_MIDI(); DMusic_Shutdown(); // shut down directinput DInput_Release_Keyboard(); // shutdown directinput DInput_Shutdown(); // shutdown directdraw last DDraw_Shutdown(); Close_Error_File(); // return success return(1); } // end Game_Shutdown ////////////////////////////////////////////////////////// int Game_Main(void *parms) { // this is the workhorse of your game it will be called // continuously in real-time this is like main() in C // all the calls for you game go here! static MATRIX4X4 mrot; // general rotation matrix // these are used to create a circling camera static float view_angle = 0; static float camera_distance = 6000; static VECTOR4D pos = {0,0,0,0}; static float tank_speed; static float turning = 0; // state variables for different rendering modes and help static int wireframe_mode = 0; static int backface_mode = 1; static int lighting_mode = 1; static int zsort_mode = 1; char work_string[256]; // temp string int index; // looping var // what state is game in? switch(game_state) { case GAME_STATE_INIT: { // load the font Load_Bitmap_Font("tech_char_set_01.bmp", &tech_font); // transition to un state game_state = GAME_STATE_RESTART; } break; case GAME_STATE_RESTART: { // start music DMusic_Play(main_track_id); DSound_Play(ambient_systems_id, DSBPLAY_LOOPING); // initialize the stars Init_Starfield(); // initialize all the aliens Init_Aliens(); // reset all vars player_z_vel = 4; // virtual speed of viewpoint/ship cross_x = CROSS_START_X; // cross hairs cross_y = CROSS_START_Y; cross_x_screen = CROSS_START_X; // used for cross hair cross_y_screen = CROSS_START_Y; target_x_screen = CROSS_START_X; // used for targeter target_y_screen = CROSS_START_Y; escaped = 0; // tracks number of missed ships hits = 0; // tracks number of hits score = 0; // take a guess :) weapon_energy = 100; // weapon energy weapon_active = 0; // weapons are off game_state_count1 = 0; // general counters game_state_count2 = 0; restart_state = 0; // state when game is restarting restart_state_count1 = 0; // general counter // difficulty diff_level = 1; // transition to run state game_state = GAME_STATE_RUN; } break; case GAME_STATE_RUN: case GAME_STATE_OVER: // keep running sim, but kill diff_level, and player input { // start the clock Start_Clock(); // clear the drawing surface //DDraw_Fill_Surface(lpddsback, 0); // blt to destination surface lpddsback->Blt(NULL, starscape.images[starscape.curr_frame], NULL, DDBLT_WAIT, NULL); // reset the render list Reset_RENDERLIST4DV2(&rend_list); // read keyboard and other devices here DInput_Read_Keyboard(); DInput_Read_Mouse(); // game logic begins here... #ifdef DEBUG_ON if (keyboard_state[DIK_UP]) { py+=10; } // end if if (keyboard_state[DIK_DOWN]) { py-=10; } // end if if (keyboard_state[DIK_RIGHT]) { px+=10; } // end if if (keyboard_state[DIK_LEFT]) { px-=10; } // end if if (keyboard_state[DIK_1]) { pz+=20; } // end if if (keyboard_state[DIK_2]) { pz-=20; } // end if #endif // modes and lights // wireframe mode if (keyboard_state[DIK_W]) { // toggle wireframe mode if (++wireframe_mode > 1) wireframe_mode = 0; Wait_Clock(100); // wait, so keyboard doesn't bounce } // end if // change nebula if (keyboard_state[DIK_N]) { if (++starscape.curr_frame >= starscape.num_frames) starscape.curr_frame = 0; Wait_Clock(100); // wait, so keyboard doesn't bounce } // end if // backface removal if (keyboard_state[DIK_B]) { // toggle backface removal backface_mode = -backface_mode; Wait_Clock(100); // wait, so keyboard doesn't bounce } // end if // lighting if (keyboard_state[DIK_L]) { // toggle lighting engine completely lighting_mode = -lighting_mode; Wait_Clock(100); // wait, so keyboard doesn't bounce } // end if // toggle ambient light if (keyboard_state[DIK_A]) { // toggle ambient light if (lights[AMBIENT_LIGHT_INDEX].state == LIGHTV1_STATE_ON) lights[AMBIENT_LIGHT_INDEX].state = LIGHTV1_STATE_OFF; else lights[AMBIENT_LIGHT_INDEX].state = LIGHTV1_STATE_ON; Wait_Clock(100); // wait, so keyboard doesn't bounce } // end if // toggle infinite light if (keyboard_state[DIK_I]) { // toggle ambient light if (lights[INFINITE_LIGHT_INDEX].state == LIGHTV1_STATE_ON) lights[INFINITE_LIGHT_INDEX].state = LIGHTV1_STATE_OFF; else lights[INFINITE_LIGHT_INDEX].state = LIGHTV1_STATE_ON; Wait_Clock(100); // wait, so keyboard doesn't bounce } // end if // toggle point light if (keyboard_state[DIK_P]) { // toggle point light if (lights[POINT_LIGHT_INDEX].state == LIGHTV1_STATE_ON) lights[POINT_LIGHT_INDEX].state = LIGHTV1_STATE_OFF; else lights[POINT_LIGHT_INDEX].state = LIGHTV1_STATE_ON; Wait_Clock(100); // wait, so keyboard doesn't bounce } // end if // toggle spot light if (keyboard_state[DIK_S]) { // toggle spot light if (lights[SPOT_LIGHT2_INDEX].state == LIGHTV1_STATE_ON) lights[SPOT_LIGHT2_INDEX].state = LIGHTV1_STATE_OFF; else lights[SPOT_LIGHT2_INDEX].state = LIGHTV1_STATE_ON; Wait_Clock(100); // wait, so keyboard doesn't bounce } // end if // z-sorting if (keyboard_state[DIK_Z]) { // toggle z sorting zsort_mode = -zsort_mode; Wait_Clock(100); // wait, so keyboard doesn't bounce } // end if // track cross hair cross_x+=(mouse_state.lX); cross_y-=(mouse_state.lY); // check bounds (x,y) are in 2D space coords, not screen coords if (cross_x >= (WINDOW_WIDTH/2-CROSS_WIDTH/2)) cross_x = (WINDOW_WIDTH/2-CROSS_WIDTH/2) - 1; else if (cross_x <= -(WINDOW_WIDTH/2-CROSS_WIDTH/2)) cross_x = -(WINDOW_WIDTH/2-CROSS_WIDTH/2) + 1; if (cross_y >= (WINDOW_HEIGHT/2-CROSS_HEIGHT/2)) cross_y = (WINDOW_HEIGHT/2-CROSS_HEIGHT/2) - 1; else if (cross_y <= -(WINDOW_HEIGHT/2-CROSS_HEIGHT/2)) cross_y = -(WINDOW_HEIGHT/2-CROSS_HEIGHT/2) + 1; // player is done moving create camera matrix ////////////////////////// Build_CAM4DV1_Matrix_Euler(&cam, CAM_ROT_SEQ_ZYX); // perform all non-player game AI and motion here ///////////////////// // move starfield Move_Starfield(); // move aliens Process_Aliens(); // update difficulty of game if ((diff_level+=DIFF_RATE) > DIFF_PMAX) diff_level = DIFF_PMAX; // start a random alien as a function of game difficulty if ( (rand()%(DIFF_PMAX - (int)diff_level+2)) == 1) Start_Alien(); // perform animation and transforms on lights ////////////////////////// // lock the back buffer DDraw_Lock_Back_Surface(); // draw all 3D entities Draw_Aliens(); // draw mesh explosions Draw_Mesh_Explosions(); // entire into final 3D pipeline /////////////////////////////////////// // remove backfaces if (backface_mode==1) Remove_Backfaces_RENDERLIST4DV2(&rend_list, &cam); // light scene all at once if (lighting_mode==1) Light_RENDERLIST4DV2_World16(&rend_list, &cam, lights, 4); // apply world to camera transform World_To_Camera_RENDERLIST4DV2(&rend_list, &cam); // sort the polygon list (hurry up!) if (zsort_mode == 1) Sort_RENDERLIST4DV2(&rend_list, SORT_POLYLIST_AVGZ); // apply camera to perspective transformation Camera_To_Perspective_RENDERLIST4DV2(&rend_list, &cam); // apply screen transform Perspective_To_Screen_RENDERLIST4DV2(&rend_list, &cam); // draw the starfield now Draw_Starfield(back_buffer, back_lpitch); // reset number of polys rendered debug_polys_rendered_per_frame = 0; // render the list if (wireframe_mode == 0) { Draw_RENDERLIST4DV2_Solid16(&rend_list, back_buffer, back_lpitch); } else if (wireframe_mode == 1) { Draw_RENDERLIST4DV2_Wire16(&rend_list, back_buffer, back_lpitch); } // end if // draw energy bindings Render_Energy_Bindings(energy_bindings, 1, 8, 16, RGB16Bit(0,255,0), back_buffer, back_lpitch); // fire the weapon if (game_state == GAME_STATE_RUN && mouse_state.rgbButtons[0] && weapon_energy > 20) { // set endpoints of energy bolts weapon_bursts[1].x = cross_x_screen + RAND_RANGE(-4,4); weapon_bursts[1].y = cross_y_screen + RAND_RANGE(-4,4); weapon_bursts[3].x = cross_x_screen + RAND_RANGE(-4,4); weapon_bursts[3].y = cross_y_screen + RAND_RANGE(-4,4); // draw the weapons firing Render_Weapon_Bursts(weapon_bursts, 2, 8, 16, RGB16Bit(0,255,0), back_buffer, back_lpitch); // decrease weapon energy if ((weapon_energy-=5) < 0) weapon_energy = 0; // make sound DSound_Play(laser_id); // energize weapons weapon_active = 1; // turn the lights on! lights[POINT_LIGHT2_INDEX].state = LIGHTV1_STATE_ON; cockpit.curr_frame = 1; } // end if else { weapon_active = 0; // turn the lights off! lights[POINT_LIGHT2_INDEX].state = LIGHTV1_STATE_OFF; cockpit.curr_frame = 0; } // end else if ((weapon_energy+=1) > 100) weapon_energy = 100; // draw any overlays /////////////////////////////////////////////////// // unlock the back buffer DDraw_Unlock_Back_Surface(); // draw cockpit Draw_BOB16(&cockpit, lpddsback); // draw information sprintf(work_string, "SCORE:%d", score); Draw_Bitmap_Font_String(&tech_font, TPOS_SCORE_X,TPOS_SCORE_Y, work_string, FONT_HPITCH, FONT_VPITCH, lpddsback); sprintf(work_string, "HITS:%d", hits); Draw_Bitmap_Font_String(&tech_font, TPOS_HITS_X,TPOS_HITS_Y, work_string, FONT_HPITCH, FONT_VPITCH, lpddsback); sprintf(work_string, "ESCAPED:%d", escaped); Draw_Bitmap_Font_String(&tech_font, TPOS_ESCAPED_X,TPOS_ESCAPED_Y, work_string, FONT_HPITCH, FONT_VPITCH, lpddsback); sprintf(work_string, "LEVEL:%2.4f", diff_level); Draw_Bitmap_Font_String(&tech_font, TPOS_GLEVEL_X,TPOS_GLEVEL_Y, work_string, FONT_HPITCH, FONT_VPITCH, lpddsback); sprintf(work_string, "VEL:%3.2fK/S", player_z_vel+diff_level); Draw_Bitmap_Font_String(&tech_font, TPOS_SPEED_X,TPOS_SPEED_Y, work_string, FONT_HPITCH, FONT_VPITCH, lpddsback); sprintf(work_string, "NRG:%d", weapon_energy); Draw_Bitmap_Font_String(&tech_font, TPOS_ENERGY_X,TPOS_ENERGY_Y, work_string, FONT_HPITCH, FONT_VPITCH, lpddsback); // process game over stuff if (game_state == GAME_STATE_OVER) { // do rendering sprintf(work_string, "GAME OVER"); Draw_Bitmap_Font_String(&tech_font, TPOS_GINFO_X-(FONT_HPITCH/2)*strlen(work_string),TPOS_GINFO_Y, work_string, FONT_HPITCH, FONT_VPITCH, lpddsback); sprintf(work_string, "PRESS ENTER TO PLAY AGAIN"); Draw_Bitmap_Font_String(&tech_font, TPOS_GINFO_X-(FONT_HPITCH/2)*strlen(work_string),TPOS_GINFO_Y + 2*FONT_VPITCH, work_string, FONT_HPITCH, FONT_VPITCH, lpddsback); // logic... if (keyboard_state[DIK_RETURN]) { game_state = GAME_STATE_RESTART; } // end if } // end if #ifdef DEBUG_ON // display diagnostics sprintf(work_string,"LE[%s]:AMB=%d,INFL=%d,PNTL=%d,SPTL=%d,ZS[%s],BFRM[%s], WF=%s", ((lighting_mode == 1) ? "ON" : "OFF"), lights[AMBIENT_LIGHT_INDEX].state, lights[INFINITE_LIGHT_INDEX].state, lights[POINT_LIGHT_INDEX].state, lights[SPOT_LIGHT2_INDEX].state, ((zsort_mode == 1) ? "ON" : "OFF"), ((backface_mode == 1) ? "ON" : "OFF"), ((wireframe_mode == 1) ? "ON" : "OFF")); Draw_Bitmap_Font_String(&tech_font, 4,WINDOW_HEIGHT-16, work_string, FONT_HPITCH, FONT_VPITCH, lpddsback); #endif // draw startup information for first few seconds of restart if (restart_state == 0) { sprintf(work_string, "GET READY!"); Draw_Bitmap_Font_String(&tech_font, TPOS_GINFO_X-(FONT_HPITCH/2)*strlen(work_string),TPOS_GINFO_Y, work_string, FONT_HPITCH, FONT_VPITCH, lpddsback); // update counter if (++restart_state_count1 > 100) restart_state = 1; } // end if #ifdef DEBUG_ON sprintf(work_string, "p=[%d, %d, %d]", px,py,pz); Draw_Bitmap_Font_String(&tech_font, TPOS_GINFO_X-(FONT_HPITCH/2)*strlen(work_string),TPOS_GINFO_Y+24, work_string, FONT_HPITCH, FONT_VPITCH, lpddsback); #endif if (game_state == GAME_STATE_RUN) { // draw cross on top of everything, it's holographic :) cross_x_screen = WINDOW_WIDTH/2 + cross_x; cross_y_screen = WINDOW_HEIGHT/2 - cross_y; crosshair.x = cross_x_screen - CROSS_WIDTH/2+1; crosshair.y = cross_y_screen - CROSS_HEIGHT/2; Draw_BOB16(&crosshair, lpddsback); } // end if // flip the surfaces DDraw_Flip(); // sync to 30ish fps Wait_Clock(30); // check if player has lost? if (escaped == MAX_MISSES_GAME_OVER) { game_state = GAME_STATE_OVER; DSound_Play(game_over_id); escaped++; } // end if // check of user is trying to exit if (KEY_DOWN(VK_ESCAPE) || keyboard_state[DIK_ESCAPE]) { PostMessage(main_window_handle, WM_DESTROY,0,0); game_state = GAME_STATE_EXIT; } // end if } break; case GAME_STATE_DEMO: { } break; case GAME_STATE_EXIT: { } break; default: break; } // end switch game state // return success return(1); } // end Game_Main //////////////////////////////////////////////////////////