/* ------------------------------------------------------------------ Black Nebula File : main3d.c Programmer: Colin Adams Date: 29/3/91 Last Modified : 10/6/91 Description: Controls the main loop of the player's ship. ------------------------------------------------------------------ */ #define AMIGA_INCLUDES #include "3d.h" #define FIRE 1 #define RIGHT 2 #define LEFT 4 #define DOWN 8 #define UP 16 #define DELAY_TIME 2 /* ------------------------------------------------------------------ External Variables ------------------------------------------------------------------ */ extern short swapflag; extern object myship; extern int player_score, player_scale; extern struct RastPort my_rast_port, back_rast_port; extern struct View my_view, back_view; extern struct ViewPort my_view_port, back_view_port; extern struct BitMap my_bit_map; extern struct BitMap back_bit_map; extern void AddNewObject(short, short, short, short); extern void SpinObjCentres(void); extern int GetPointAngle(int *, short, short); /* ------------------------------------------------------------------ Variables ------------------------------------------------------------------ */ static int last_draw = 0; int player_missiles; short U_rot = 270, W_rot = 180; short View2_Angle; int cos_angle[360], sin_angle[360]; char rotate[4]; /* pointers to bitmaps for console update */ PLANEPTR front_alt[4], back_alt[4]; PLANEPTR front_sp[4], back_sp[4]; PLANEPTR front_radar[66][4]; PLANEPTR back_radar[66][4]; char colour_mask[16][4]; UBYTE on_pat[8], off_pat[8]; /* ------------------------------------------------------------------ Routines ------------------------------------------------------------------ */ void SetUpConsole(void) /* precalculation of bitmap pointers for fast drawing */ { unsigned char i, j; for(i=0; i<16; i++) { colour_mask[i][0] = i & 1; colour_mask[i][1] = i & 2; colour_mask[i][2] = i & 4; colour_mask[i][3] = i & 8; } for(i=0; i<DEPTH; i++) { front_alt[i] = my_bit_map.Planes[i] + (G/8) * 225; front_sp[i] = my_bit_map.Planes[i] + (G/8) * 245; back_alt[i] = back_bit_map.Planes[i] + (G/8) * 225; back_sp[i] = back_bit_map.Planes[i] + (G/8) * 245; for(j=0; j<66; j++) { front_radar[j][i] = my_bit_map.Planes[i] + (G/8) * (182+j) + 16; back_radar[j][i] = back_bit_map.Planes[i] + (G/8) * (182+j) + 16; } } on_pat[0] = 1; off_pat[0] = ~1; on_pat[1] = 2; off_pat[1] = ~2; on_pat[2] = 4; off_pat[2] = ~4; on_pat[3] = 8; off_pat[3] = ~8; on_pat[4] = 16; off_pat[4] = ~16; on_pat[5] = 32; off_pat[5] = ~32; on_pat[6] = 64; off_pat[6] = ~64; on_pat[7] = 128; off_pat[7] = ~128; } void UpdateAlt(void) /* updates the altmeter on the user display */ { int byte = ey / (MAX_WORLD_Y/9); unsigned int i; for(i=0; i<DEPTH; i++) { register PLANEPTR temp, temp2; register unsigned int j; temp = front_alt[i] + byte; temp2 = back_alt[i] + byte; for(j=1; j<=9; j++) { register unsigned int colour; if(byte) { if(j>5) colour = j + 4; else colour = 13 - j; if(colour_mask[colour][i]) { *temp2 = 255; /* fill byte with colour */ *temp = 255; } else { *temp2 = 0; /* set to background colour */ *temp = 0; } } if(byte<9) { *(temp + 1) = 0; /* clear to right side */ *(temp2 + 1) = 0; } temp += G/8; temp2 += G/8; } } } void UpdateSpeed(void) /* Updates the speed meter on the user display. For some strange reason this code doesn't work on an Amiga 3000 the way it does on a 2000. ie. bugs */ { unsigned int i; for(i=0; i<DEPTH; i++) { register PLANEPTR temp, temp2; register unsigned int j; temp = front_sp[i] + velocity; temp2 = back_sp[i] + velocity; for(j=1; j<=9; j++) { register unsigned int colour; if(velocity) { if(j>5) colour = j + 4; else colour = 13 - j; if(colour_mask[colour][i]) { *temp2 = 255; /* fill byte with colour */ *temp = 255; } else { *temp2 = 0; /* set to background colour */ *temp = 0; } } if(velocity<9) { *(temp + 1) = 0; /* clear to right side */ *(temp2 + 1) = 0; } temp += G/8; temp2 += G/8; } } } void UpdateRadar(void) /* updates the radar on the user display */ { static PLANEPTR last_bytes_front[TOTAL_MAX_OBJECTS][DEPTH]; static PLANEPTR last_bytes_back[TOTAL_MAX_OBJECTS][DEPTH]; unsigned int i, loop; object *obj; for(loop=0; loop<last_draw; loop++) { for(i=0; i<DEPTH; i++) { *last_bytes_front[loop][i] = 0; *last_bytes_back[loop][i] = 0; } } last_draw = no_objects + 1; for(loop=0; loop<=no_objects; loop++) { register int y, byte, offset, colour, x; if(loop!=no_objects) { obj = active_list[loop]; x = obj->trans_x; y = obj->trans_z / (MAX_WORLD_Z/65); switch(obj->type) { case FIXED: colour = 9; break; case ENEMYMISSILE: colour = 10; break; case MYMISSILE: colour = 1; break; case SHUTTLE: colour = 14; break; case KRAIT: colour = 12; break; case GECKO: colour = 15; break; case THARGOID: colour = 11; break; case MAMBA: colour = 8; break; case GUIDEDMISSILE: colour = 3; break; case EXPLOSION: colour = 0; break; } } else { x = ex; y = ez / (MAX_WORLD_Z/65); colour = 8; } byte = x/(MAX_WORLD_X/7); /* get byte number */ offset = x- (byte*(MAX_WORLD_X/7)); /* get bit offset */ offset = 7 - (offset/((MAX_WORLD_X/7)/7)); for(i=0; i<DEPTH; i++) { register PLANEPTR temp, temp2; register UBYTE s_read; temp = front_radar[y][i] + byte; temp2 = back_radar[y][i] + byte; last_bytes_front[loop][i] = temp; last_bytes_back[loop][i] = temp2; if(colour_mask[colour][i]) /* set bit in this plane */ { s_read = *temp2; s_read |= on_pat[offset]; *temp2 = s_read; s_read = *temp; s_read |= on_pat[offset]; *temp = s_read; } else /* have to clear the bit in this plane */ { s_read = *temp2; s_read &= off_pat[offset]; *temp2 = s_read; s_read = *temp; s_read &= off_pat[offset]; *temp = s_read; } } } } void Setupangles(void) { FILE *fp; int i, j; double d; if(!(fp=fopen("moretrig.pre","r"))) { printf("Calculating fixed point sin/cos\n"); for(i=0; i<=359; i++) { d = sin(deg(i))*1000; j = d; sin_angle[i] = -j; d = cos(deg(i))*1000; j = d; cos_angle[i] = j; } for(i=0; i<=4096; i++) { double t = i; anti_sin[i] = asin(t/4096) * 57.29578; } if(!(fp=fopen("moretrig.pre","w"))) { printf("Failed to save sin/cos data!\n"); return; } fwrite((void *) &sin_angle[0], sizeof(int), 360, fp); fwrite((void *) &cos_angle[0], sizeof(int), 360, fp); fwrite((void *) &anti_sin[0], sizeof(int), 4097, fp); fclose(fp); } else { fread((void *) &sin_angle[0], sizeof(int), 360, fp); fread((void *) &cos_angle[0], sizeof(int), 360, fp); fread((void *) &anti_sin[0], sizeof(int), 4097, fp); fclose(fp); } } void PrintShip(char *name) { struct rastport *temp = rastport; rastport = &my_rast_port; SetAPen(rastport, 0); RectFill(rastport, 129, 183, 189, 243); SetAPen(rastport, 11); Move(rastport, 132, 215); Text(rastport, name, strlen(name)); rastport = &back_rast_port; SetAPen(rastport, 0); RectFill(rastport, 129, 183, 189, 243); SetAPen(rastport, 11); Move(rastport, 132, 215); Text(rastport, name, strlen(name)); rastport = temp; } void Print2Ship(char *name) { struct rastport *temp = rastport; rastport = &my_rast_port; SetAPen(rastport, 11); Move(rastport, 132, 230); Text(rastport, name, strlen(name)); rastport = &back_rast_port; SetAPen(rastport, 11); Move(rastport, 132, 230); Text(rastport, name, strlen(name)); rastport = temp; } void IntroShips(void) /* Spins each ship in the game around for a while. Note the hidden surface bug in the shuttle! */ { int counter = 10, timeout = 0, timeoutlim = 300; UBYTE value = 0; AddNewObject(counter, 4000, 360, 2000); PrintShip("Player"); Print2Ship("Ship"); while(value!=FIRE) { register int i; value = Joystick(); timeout++; if(timeout==timeoutlim) /* make it blow up! */ { active_list[0]->i_am_dying = 1; active_list[0]->explode = 1; StartSound(2); } if(no_objects==0) /* create a new object */ { Delay(100); counter--; if(counter<0) counter = 10; if(counter==9) counter = 7; switch(counter) { case 0: PrintShip("Enemy"); Print2Ship("Missile"); break; case 1: PrintShip("Space"); Print2Ship("Station"); break; case 2: PrintShip("Shuttle"); break; case 3: PrintShip("Krait"); break; case 4: PrintShip("Gecko"); break; case 5: PrintShip("Thargoid"); break; case 6: PrintShip("Cruiser"); break; case 7: PrintShip("Player"); Print2Ship("Missile"); break; case 10: PrintShip("Player"); Print2Ship("Ship"); break; } KillAllObjects(); SetUp3d(); AddNewObject(counter, 4000, 360, 2000); timeoutlim = (active_list[0]->numpoints > 10) ? 150 : 300; timeout = 0; } for(i=0; i<no_objects; i++) { register object *obj = active_list[i]; if(!obj->i_am_dying) obj->drawme = 1; else continue; if(obj->type==EXPLOSION) { int rad; if(--obj->timeinflight==0) { obj->i_am_dying = 1; break; } rad = getrandom(0,10); if(obj->up_or_down) { if(obj->trans_y<MAX_WORLD_Y-100) obj->trans_y += rad; } else { if(obj->trans_y>100) obj->trans_y -= rad; } if(rad>5) rad = getrandom(1, 6); else rad = -getrandom(1, 6); Rotate_Obj_Rel(obj, rad+2, rad-1, rad+1); obj->trans_x += FastCos(obj->velocity, obj->heading); obj->trans_z -= FastSin(obj->velocity, obj->heading); if(obj->trans_x<0) obj->trans_x = MAX_WORLD_X; else if(obj->trans_x > MAX_WORLD_X) obj->trans_x = 0; if(obj->trans_z<0) obj->trans_z = MAX_WORLD_Z; else if(obj->trans_z>MAX_WORLD_Z) obj->trans_z = 0; } else Rotate_Obj_Rel(obj, 5, 5, 5); } /* still interactive */ if(value & LEFT) { U_rot+=2; W_rot+=2; } else if(value & RIGHT) { U_rot-=2; W_rot-=2; } if(value & UP) { if(ey <= MAX_WORLD_Y-Y_OFFSET) { UpdateAlt(); ey += Y_OFFSET; } } else if(value & DOWN) { if(ey>0) { ey -= Y_OFFSET; UpdateAlt(); } } if(U_rot>359) U_rot-=360; else if(U_rot<0) U_rot+= 360; if(W_rot>359) W_rot-=360; else if(W_rot<0) W_rot+= 360; ux.numer = cos_angle[U_rot]; uz.numer = sin_angle[U_rot]; wx.numer = cos_angle[W_rot]; wz.numer = sin_angle[W_rot]; View_Angle = U_rot; SpinObjCentres(); CalculateObjects(); ClearObjects(); DisplayObjects(); if(swapflag) { rastport = &my_rast_port; swapflag = 0; WaitBOVP(&my_view_port); LoadView(&back_view); } else { rastport = &back_rast_port; swapflag = 1; WaitBOVP(&back_view_port); LoadView(&my_view); } } } void PrintView(char *name) { register struct rastport *temp = rastport; rastport = &my_rast_port; SetAPen(rastport, 0); RectFill(rastport, 245, 196, 280,210); SetAPen(rastport, 11); Move(rastport, 245, 206); Text(rastport, name, strlen(name)); rastport = &back_rast_port; SetAPen(rastport, 0); RectFill(rastport, 245, 196, 280 , 210); SetAPen(rastport, 11); Move(rastport, 245, 206); Text(rastport, name, strlen(name)); rastport = temp; } void PrintRot(void) { register struct rastport *temp = rastport; register short t = U_rot; rotate[0] = t / 100; t = t - (rotate[0]*100); rotate[1] = t / 10; t = t - (rotate[1]*10); rotate[2] = t + '0'; rotate[0] += '0'; rotate[1] += '0'; rastport = &my_rast_port; SetAPen(rastport, 0); RectFill(rastport, 245, 210, 280, 221); SetAPen(rastport, 11); Move(rastport, 245, 220); Text(rastport, rotate, 3); rastport = &back_rast_port; SetAPen(rastport, 0); RectFill(rastport, 245, 210, 280 , 221); SetAPen(rastport, 11); Move(rastport, 245, 220); Text(rastport, rotate, 3); rastport = temp; } void Do3d(void) { SetUp3d(); KillAllObjects(); AddObject("cube.object", FIXED); AddObject("ops_missile.object", ENEMYMISSILE); AddObject("missile.object", MYMISSILE); AddObject("krait.object", KRAIT); AddObject("shuttle.object",SHUTTLE); AddObject("gecko.object", GECKO); AddObject("thargoid.object", THARGOID); AddObject("cruiser.object", MAMBA); AddObject("guided.object",GUIDEDMISSILE); AddObject("player.object",PLAYER); SetUpViews(); SetUpConsole(); player_score = player_scale = last_draw = 0; Delay(25); IntroShips(); SetUp3d(); KillAllObjects(); loadpic("screen.iff"); /* clear radar area and screen display */ { struct rastport *temp = rastport; rastport = &my_rast_port; SetAPen(rastport, 0); RectFill(rastport, 129, 183, 189, 243); RectFill(rastport, X_MIN, Y_MIN, X_MAX, Y_MAX); rastport = &back_rast_port; SetAPen(rastport, 0); RectFill(rastport, 129, 183, 189, 243); RectFill(rastport, X_MIN, Y_MIN, X_MAX, Y_MAX); rastport = temp; } ez = 4000; player_missiles = 25; PrintView("Front"); PrintRot(); while(!am_i_dead) { register UBYTE value = Joystick(); register int speed; static int fireframe = 21; fireframe++; if(value & FIRE) { if(fireframe>5) { int tx = ex + FastCos(150, View_Angle); int tz = ez - FastSin(150, View_Angle); AddNewObject(MYMISSILE, tx, ey-100, tz); StartSound(0); fireframe = 0; } } if(value & LEFT) { U_rot+=2; W_rot+=2; PrintRot(); } else if(value & RIGHT) { U_rot-=2; W_rot-=2; PrintRot(); } if(value & UP) { if(ey <= MAX_WORLD_Y-Y_OFFSET) { UpdateAlt(); ey += Y_OFFSET; } } else if(value & DOWN) { if(ey>0) { ey -= Y_OFFSET; UpdateAlt(); } } value = Keyboard(); /* read keyboard */ if(value==0x40) /* space, increase speed */ { if(velocity<9) { velocity++; UpdateSpeed(); } } else if(value==0x41) /* backspace, decrease speed */ { if(velocity) { velocity--; UpdateSpeed(); /* only updates if changed */ } } else if(value==0x45) /* escape */ break; else if(value==0x19) /* P key, pause */ while(Keyboard()!=0x16); /* wait for a u key press */ else if(value==0x50) /* front view */ { PrintView("Front"); view_mode = 0; } else if(value==0x51) /* left view */ { PrintView("Left"); view_mode = 1; } else if(value==0x52) /* right view */ { PrintView("Right"); view_mode = 3; } else if(value==0x53) /* back view */ { PrintView("Rear"); view_mode = 2; } else if(value==0x37) { if(fireframe>5 && player_missiles) { int tx = ex + FastCos(150, View_Angle); int tz = ez - FastSin(150, View_Angle); AddNewObject(GUIDEDMISSILE, tx, ey-100, tz); StartSound(0); fireframe = 0; player_missiles--; } } /* correct rotations in case it went over 360 or under 0 */ if(U_rot>359) U_rot-=360; else if(U_rot<0) U_rot+= 360; if(W_rot>359) W_rot-=360; else if(W_rot<0) W_rot+= 360; switch(view_mode) { case 0: View_Angle = U_rot; break; case 1: View_Angle = U_rot + 90; break; case 2: View_Angle = U_rot + 180; break; case 3: View_Angle = U_rot + 270; break; } if(View_Angle>359) View_Angle -= 360; View2_Angle = View_Angle - 90; if(View2_Angle<0) View2_Angle += 360; ux.numer = cos_angle[View_Angle]; uz.numer = sin_angle[View_Angle]; wx.numer = cos_angle[View2_Angle]; wz.numer = sin_angle[View2_Angle]; /* Very simple code to move the ship according to velocity */ speed = velocity * 2; ex += FastCos(speed, U_rot); ez -= FastSin(speed, U_rot); /* Keep ship in the world */ if(ex<0) ex = MAX_WORLD_X; else if(ex>MAX_WORLD_X) ex = 0; if(ez<0) ez = MAX_WORLD_Z; else if(ez>MAX_WORLD_Z) ez = 0; SpinObjCentres(); MoveObjects(); UpdateObjects(); CalculateObjects(); ClearObjects(); UpdateRadar(); DisplayObjects(); /* swap views */ if(swapflag) { rastport = &my_rast_port; swapflag = 0; WaitBOVP(&my_view_port); LoadView(&back_view); } else { rastport = &back_rast_port; swapflag = 1; WaitBOVP(&back_view_port); LoadView(&my_view); } } if(am_i_dead) /* A really cool extern death sequence! Soon the wonder of external views won't be confined to the death sequence */ { int i; StartSound(1); PrintView("Death"); AddNewObject(PLAYER, ex-100, ey-50, ez-50); /* add a ship then blow it up! */ active_list[no_objects-1]->i_am_dying = 1; active_list[no_objects-1]->explode = 1; for(i=0; i<30; i++) { register int px, py, pz, speed; speed = velocity * 2; ex += FastCos(speed, U_rot); ez -= FastSin(speed, U_rot); /* Keep ship in the world */ if(ex<0) ex = MAX_WORLD_X; else if(ex>MAX_WORLD_X) ex = 0; if(ez<0) ez = MAX_WORLD_Z; else if(ez>MAX_WORLD_Z) ez = 0; if(ex>(MAX_WORLD_X/2)) px = ex - 1000; else px = ex + 1000; py = ey+150; if(ez>(MAX_WORLD_Y/2)) pz = ez - 1000; else pz = ez + 1000; SpinObjCentres(); { int dummy; View_Angle = GetPointAngle(&dummy, px, pz); } View2_Angle = View_Angle - 90; if(View2_Angle<0) View2_Angle += 360; ux.numer = cos_angle[View_Angle]; uz.numer = sin_angle[View_Angle]; wx.numer = cos_angle[View2_Angle]; wz.numer = sin_angle[View2_Angle]; { register int tempx, tempy, tempz; tempx = ex; tempy = ey; tempz = ez; ex = px; ey = py; ez = pz; MoveObjects(); CalculateObjects(); ClearObjects(); UpdateRadar(); DisplayObjects(); ex = tempx; ey = tempy; ez = tempz; } /* swap views */ if(swapflag) { rastport = &my_rast_port; swapflag = 0; WaitBOVP(&my_view_port); LoadView(&back_view); } else { rastport = &back_rast_port; swapflag = 1; WaitBOVP(&back_view_port); LoadView(&my_view); } } } KillAllObjects(); FreeView(); } /* end of module */