← Back to contents
C source, ASCII text
— 18K bytes
— 1994-01-01
/*
------------------------------------------------------------------
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 */