// Version with memory card support removed (failed sony QA) /* Car game */ #include #include #include #include "pad.h" #include "inv.c" // inventory numbers #include "carpak.c" // offsets into pak file //#include "anim.c" // defines for animations #define MUSIC 1 // 1 = do music, 0 = not installed #define ANIM_PLAYER1 0 #define ANIM_LAPRECORD 1 #define ANIM_SHOTS 2 #define ANIM_SMOKE 3 #define ANIM_EXPLO1 4 #define ANIM_OVERLAY 5 #define ANIM_DUST 6 #define ANIM_DUSTCLOUD 7 #define SCREENWIDTH 320 #define SCREENHEIGHT 256 #define FPS 50 // frames per second (50 = PAL) #define MAXOBJ 1024 // Maximum number of objects #define MAXSPRITES MAXOBJ // Maximum number of sprites #define MAX3DOBJECTS 32 // Maximum number of 3D objects (buildings etc) #define MAXPRIMITIVES 4224 // Maximum number of primitives (sprites,lines,polygons etc) #define MAXDRIVERS 6 // Maximum number of drivers (including CPU's) #define OT_LENGTH 9 // # bits resolution for Z sorting #define TOPOT_LENGTH 6 #define BOTOT_LENGTH 7 #define OVERLAYOT_LENGTH 3 #define BACKOT_LENGTH 3 #define OTPRI_GROUND 28 // priority for the ground (track) #define OTPRI_OVERLAY 8 // overlay map (trees, tunnels etc) #define OTPRI_CARS 24 // the cars #define OTPRI_MISSILES 22 // flying objects under smoke, but over cars #define OTPRI_SMOKE 20 // air based objects (over cars i.e. smoke screen) #define OTPRI_MINES 26 // ground base (underneath cars i.e. mines) #define OTPRI_BACK (1<(h)?(h):(x))) typedef struct { short x,y,dx,dy; u_char col,active; } dustparticle; typedef struct { // for unPAK routine(s) u_long offset,length; u_char method,x1,x2,x3; // method: 1 = standard RLE } entryheader; typedef struct { u_long offset; u_long length; } movementheader; typedef struct { short dx,dy; char time,control; } simplemove; typedef struct { u_long x,y; } ghostdatapos; typedef struct { u_short active; // 0 = don't update/1=update short current,previous,best; // number of lap array used for current lap,previous lap etc.. ghostdatapos stcurrent,stprevious,stbest; // starting positions for ghost data ghostdatapos cucurrent; // current positions for input short index; // index into the lap array (time) u_long ghostobj; // object index for ghost car u_long obj,x,y; // object index to pull data from, output x and y short angle; // output angle short out; // number of lap array to use for output short outdisp; // 0 = no display of out, 1 = display u_long laptime; // lap time of output short lap[3][MAXGHOSTENTRIES]; // lap arrays } ghostdatadef; typedef struct { u_long offset; u_short uv0,uv1,uv2,xx; } headlightprecalc; typedef struct { u_short w,h; u_long offset; } maskheader; typedef struct { u_long attribute; u_short w,h,tpage; u_char u,v; short cx,cy; } singleframe; typedef struct { u_long offset; // offset into singleframe array u_short nframes; } animation; typedef struct { u_long index; } entity; typedef struct { u_char active,ot,xxx,xxxx; u_long *tmdpt; // pointer to top of tmd data u_long aoff,hox,hoy; GsDOBJ2 handler; GsCOORDINATE2 coord; VECTOR origin; SVECTOR rotate; u_char tmd[2048]; // tmd data } headlight3D; typedef struct { short active,playerindex; u_char controller,controller_type; } extrasprite; typedef struct { u_char active,ot,xxx,xxxx; u_long *tmdpt; // pointer to top of tmd data GsDOBJ2 handler; GsCOORDINATE2 coord; VECTOR origin; SVECTOR rotate; } object3d; typedef struct { long x,y,angle; // object position long momdx,momdy; short speed,maxspeed; u_short cx,cy; // sprite clut animation *base_sprite; // pointer to animation u_short frame; // frame of animation u_short oaindex; // index in object array that this resides long dx,dy,dangle; // velocity long prevangle; // previous angle (for oil slicks etc) u_short hx,hy; // sprite handle offsets long minx,miny,maxx,maxy; long health,driverindex; long followx,followy; u_char active; // 0 = not active u_char shottime; u_char chkpoint,lap; // current checkpointt number and lap number u_char lastchkpoint,dead_flag; // last checkpoint visited. u_char spritetype,effects; // spritetype 0=normal, 1=fast. effects b0=on oil b1=turbo pressed u_char bright,steer,r,g,b,followactive; // brightness u_char spin,missilecount,minecount; // spin - is player spinning (from explosion) u_short index; u_long time; // lifetime (continuously increasing) void (*control)(u_long obj); // pointer to objects control function extrasprite *extra; entity other; u_short class; // 0 = null (explosion), 1 = player, 2 = cpu player, 3 = ghost u_short pri; // OT priority (0=maximum i.e displayed on top) u_long owner; short voice[4]; } standardsprite; typedef struct { u_char active; u_char carcolour; u_short carclut; // base clut of car u_long object; long score; long cash; u_long laptime,lastlap,bestlap; // in 1/100ths u_char trigger[4]; // which weapon on which button short inventory[MAXINVENTORY]; // count of each inventory item long fuel; short maxspeed; char name[16]; u_char pad,controllertype; // which pad to use (0 or 1) and what it is u_short paddata[4]; // actual pad data long health; } playerdata; typedef struct { u_long time; u_long x,y; u_char type,flag; // type is 1 for WAVE u_short alien; u_short movement; u_short pad; } event; typedef struct { long dx,dy; } velocity; typedef struct { u_char lv,rv,sv,xx; } volume; typedef struct { long x,y,angle; } startpositioninfo; typedef struct { u_long laprecord[4],trackrecord[4]; // records for novice/intermediate/pro/race char bestlapname[4][12],bestname[4][12]; // names } recordsentry; typedef struct { int celmblock,mpimblock,colmblock; // memory blocks used for each part of track data int celtopmblock,mpitopmblock; u_short *maskaddress; // pointer to track mask (.TBM) u_long celindex,mpiindex,colindex,tim1index,tim2index; // offsets into PAK file u_long celtopindex,mpitopindex; recordsentry records; char name[32]; // name of track u_char numcheckpoints,numlaps,type,valid,night,xx; // valid 0=not, 1=yes (available to use this track) startpositioninfo grid[MAXDRIVERS]; // starting positions for each driver short ghost[MAXGHOSTENTRIES/16]; // ghost data for best lap } trackinfoheader; // information about track (to be used for all tracks) typedef struct { // .NFO file contents startpositioninfo grid[4]; } tracknfofile; typedef struct { char name[16]; // location name (england, japan etc..) int index[MAXTRACKSPERLOC]; // index into tracks array for 8 tracks. -1 = not available } locationinfoheader; typedef struct { u_short active; void *address; u_long size; } memoryblockentry; typedef struct { u_long object; u_char class,active; } cardriver; typedef struct { u_short sx,sy; // coordinates on screen u_char grid[16*22]; } lcdgrid; typedef struct { u_char x,y; } charcoord; typedef struct { u_char current[3]; u_char next[3]; u_char x,y; u_long speed; lcdgrid *lcdpt; u_short pad,pause; u_char padcount,removecount,status,blocktype; charcoord remove[16]; } columsdef; typedef struct { charcoord current[4]; charcoord next[4]; u_char x,y; u_long speed; lcdgrid *lcdpt; u_short pad,pause; u_char padcount,status; } tetrisdef; #include "invtext.c" int titlescreenn; int cdebug = 0; u_short *cdebuglpt,*cdebugrpt; short soundvab,musicvab,musicseq,funkseq; // VAB id for sound int soundvhmblock; short musicvolume,mainvolume; // in font table (look up for font TIM), ~ represents " char fontcolour; char font8table[]="0123456789!~#$%&'()*+.-,/:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_£"; GsSPRITE char8sprite,char8spritefast,sprite16bit,blocksprite,tdsprite; char timechar[8]; lcdgrid lcd[2]; // simple screens for mini games (1 per player) columsdef game1[2]; tetrisdef game2[2]; u_short blockcolours[16*16],fontcolours[16*16]; // 4bit CLUTS for lcd blocks and font8 u_char nighttime; // nighttime = 1 when driving at night u_char timetrial; // 0 = race, 1 = time trial standardsprite object[MAXSTANDARDSPRITES]; object3d obj3d[MAX3DOBJECTS]; extrasprite extrasprites[MAXEXTRASPRITES]; headlight3D headlight[MAXHEADLIGHTS]; object3d hlobj; object3d backlobj,backrobj; // background screen object GsSPRITE mysprite; GsSPRITE laprecordsprite; long laprecordx,laprecordy,laprecorddx,laprecorddy,laprecordactive; playerdata *laprecordholder; GsBG background[2]; GsMAP backmap[2]; trackinfoheader trackinfo; trackinfoheader tracks[MAXTRACKS]; // data for all the tracks in the game locationinfoheader location[MAXLOCATIONS]; u_long backgroundworkarea[(((SCREENWIDTH/16+1)*(SCREENHEIGHT/16+1+1)*6+4)*2+2)]; u_long backgroundtopworkarea[(((SCREENWIDTH/16+1)*(SCREENHEIGHT/16+1+1)*6+4)*2+2)]; long mapwidth,mapheight,mapx,mapy; memoryblockentry memoryblock[MAXMEMORYBLOCKS]; // used to keep track of allocated memory cardriver driver[MAXDRIVERS]; // keeps track of drivers on the race playerdata players[6]; // 2+ are bodges ghostdatadef ghostdata; dustparticle dust[MAXDUSTPARTICLES]; char qrand[8] = {2,5,-4,3,-6,1,-5,4}; int selectedclass,selectedlocation,selectedtrack; int optionsreturn; int playeri; // player entering in options screen (for single player) int carpositions[MAXDRIVERS]; // list of car drivers in position order struct DIRENTRY de1[15],de2[15],*dept; char cardfname[32]; u_char cardbuff[8200]; char savegamefilename[20] = "BESLES23981rmshover"; u_short carcolours[6] = {0x001f,0x17e0,0x7c01,0x03fe,0x7c1b,0x7f80}; u_short screenline[320]; short maxspeeds[4] = {180,220,280,350}; int racetracks[8] = {0,2,1,8,10,4,5,6}; u_char racestatus; // 0 = starting grid, 1 = running u_char game_paused; u_char QUIT_GAME_FLAG; // 0 = no, 1 = exit program u_char postextx[6] = {0,22,60,94,126,157}; // positions of position text textures (1st 2nd 3rd etc..) u_char postextw[6] = {22,38,36,32,31,31}; GsSPRITE postextspr; GsSPRITE ammospr; u_short projection; GsRVIEW2 cameraview; GsF_LIGHT light[3]; DISPENV display; singleframe *framespt; // pointer to frame data animation *animspt; // pointer to animation data maskheader *maskheaderpt; // pointer to frame masks headers u_short *maskspt; // and data movementheader *movespt; // pointer to movementheaders u_char *simplemovespt; velocity *veltabpt; // pointer to velocity table volume *volumetabpt; // pointer to volume table u_char *colmappt; // pointer to collision map u_char *chkmappt; // pointer to cpu check map u_char *cpudirmappt; // pointer to cpu dir map u_long *headlighttmdpt; // pointer to headlight tmd data headlightprecalc *headlpcpt; // pointer to headlight pre-calc table u_long headlnprims; // number of prims in headlight tmd void *vhptr,*musicvhptr; // pointer to VH sound header GsOT WorldOT[2]; GsOT_TAG OTTags[2][1<offset)), &laprecordsprite); laprecordsprite.attribute = 0x40000000; laprecordsprite.mx = laprecordsprite.w/2; laprecordsprite.my = laprecordsprite.h/2; laprecordsprite.r = laprecordsprite.g = laprecordsprite.b = 128; laprecordsprite.scalex = laprecordsprite.scaley = 4096; laprecordsprite.rotate = 0; laprecordx = 0; laprecordy = -200; laprecordactive = 0; nighttime = 0; timetrial = 0; opt = -1; optionsreturn = 0; while (opt != 0) { SsSeqPlay(funkseq,SSPLAY_PLAY,SSPLAY_INFINITY); opt = optionentry(); if (opt == 1) // arcade { int racequit; racequit = 0; for (f=0;f<2;f++) ResetPlayer(f); // initialise all (both) players selectedclass = 4; racenumber = 0; while ( racequit == 0) { arcadeentry(racenumber); // soundtest(); nighttime = 0; timetrial = 0; SsSeqStop(funkseq); if (players[0].active != 0 || players[1].active != 0) { Race(racetracks[racenumber%8]); backlobj.origin.vx = -2870; backlobj.origin.vy = -1974; backrobj.origin.vx = 2894; backrobj.origin.vy = -1974; backlobj.rotate.vy = 2048; backrobj.rotate.vy = 2048; backlobj.rotate.vz = 0; backrobj.rotate.vz = 0; if (quitwithpause == 0) showrankings(); } if (quitwithpause == 1) { players[0].fuel = 0; players[1].fuel = 0; } for (f=0;f<2;f++) if (players[f].fuel == 0) players[f].active = 0; racenumber++; if (players[0].active == 0 && players[1].active == 0) // if both players inactive racequit = 1; // quit & return to main screen } } if (opt == 2) // time trial { nighttime = 0; timetrial = 1; SsSeqStop(funkseq); Race(location[selectedlocation].index[selectedtrack]); } } // testcollision(); freeallmemory(); closesound(); printf("OUT\n"); return(0); } void SetupTrack(int trackn) { // Initializes track trackn ready for use Initializebackground(&tracks[trackn]); InitializeTrack(&tracks[trackn]); trackinfo = tracks[trackn]; } void DeSetupTrack(int trackn) { // copies back some data (best times etc.. to track info) int f; for (f=0;f<4;f++) { tracks[trackn].records.laprecord[f] = trackinfo.records.laprecord[f]; tracks[trackn].records.trackrecord[f] = trackinfo.records.trackrecord[f]; strcpy(&tracks[trackn].records.bestlapname[f][0],&trackinfo.records.bestlapname[f][0]); strcpy(&tracks[trackn].records.bestname[f][0],&trackinfo.records.bestname[f][0]); } } void Race(int trackn) { // Does the race on track trackn if (timetrial == 1) nighttime = tracks[trackn].night & 1; SetupTrack(trackn); // printf("calling racegamemain\n"); racegamemain(); DeSetupTrack(trackn); FreeBackground(&tracks[trackn]); } int optionentry() { // main options screens // returns: 1=arcade entry, 0 = exit int f,g,QUIT_FLAG,optstatus,status,num,hcnt,butwait,duston,dustang,qrandi,fpb; char c,cpos,name[16]; u_short pc; short dustx,dusty,dustm,dustdx,dustdx2; u_long obj,cubeobj; long card1,card2,save1avail,save2avail; //card1 and card2 are id's of card. save1avail & save2avail hold whether valid savegame found (0=no, 1=yes) GsLINE dot; GsSPRITE lspr; // sprite for option selector // printf("Option Entry\n"); card1 = 0; card2 = 0; dot.attribute = 0x70000000; dot.r = dot.b = dot.g = 0; lspr.attribute = 0x41000000;//(1<<24) | (1<<27) | (1<<30) | (3<<28); lspr.w = 14; lspr.h = 14; lspr.u = 103; lspr.v = 91+48; lspr.tpage = GetTPage(0,0,640,256); lspr.r = 128; lspr.g = 96; lspr.b = 0; lspr.cx = 640; lspr.cy = 496; for (f=0;f<2;f++) { WorldOT[f].length = OT_LENGTH; WorldOT[f].org = OTTags[f]; TopOT[f].length = TOPOT_LENGTH; TopOT[f].org = TopOTTags[f]; BotOT[f].length = BOTOT_LENGTH; BotOT[f].org = BotOTTags[f]; OverlayOT[f].length = OVERLAYOT_LENGTH; OverlayOT[f].org = OverlayOTTags[f]; BackOT[f].length = BACKOT_LENGTH; BackOT[f].org = BackOTTags[f]; } // Set up text area FntLoad(960,256); FntOpen(16-SCREENWIDTH/2,16-SCREENHEIGHT/2,256,200,0,512); for (f=0;f<2;f++) ResetPlayer(f); // initialise all (both) players for (f=0;f0) { obj3d[cubeobj].rotate.vy+=64; status--; } if (obj3d[cubeobj].rotate.vy < 0) obj3d[cubeobj].rotate.vy+=4096; if (obj3d[cubeobj].rotate.vy >= 4096) obj3d[cubeobj].rotate.vy-=4096; FntPrint("status %d, rot %d\n",status,obj3d[cubeobj].rotate.vy/1024); switch(obj3d[cubeobj].rotate.vy/1024) { case 0: { print8(72,200,"RACE AGAINST THE CLOCK"); break; } case 1: { print8(72,200,"GENERAL OPTIONS SCREEN"); break; } case 2: { print8(124,200,"EXIT GAME"); break; } case 3: { print8(64,200,"ENTER ARCADE CHAMPIONSHIPS"); break; } } for (f=0;f<2;f++) { if (butwait == 0) { if ( players[f].paddata[1] & PADLleft ) { if (status < 0) obj3d[cubeobj].rotate.vy = (obj3d[cubeobj].rotate.vy/1024)*1024; else if (status > 0) obj3d[cubeobj].rotate.vy = (((obj3d[cubeobj].rotate.vy/1024)+1)%4)*1024; status=-16; thud(-45); butwait = 8; } if ( players[f].paddata[1] & PADLright ) { thud(45); if (status < 0) obj3d[cubeobj].rotate.vy = (obj3d[cubeobj].rotate.vy/1024)*1024; else if (status > 0) obj3d[cubeobj].rotate.vy = (((obj3d[cubeobj].rotate.vy/1024)+1)%4)*1024; status=16; butwait = 8; } } else butwait--; if ( players[f].paddata[1] & PADRdown ) { switch(obj3d[cubeobj].rotate.vy/1024) { case 0: { kill3d(cubeobj); cubeobj = MAX3DOBJECTS+1; // so that it gets spawned again optstatus = 3; // time trial init playeri = f; players[playeri].active = 1; break; } case 1: { playeri = f; optstatus = 11; kill3d(cubeobj); cubeobj = MAX3DOBJECTS+1; // so that it gets spawned again break; } case 2: { kill3d(cubeobj); cubeobj = MAX3DOBJECTS+1; // so that it gets spawned again return(0); break; } case 3: { kill3d(cubeobj); cubeobj = MAX3DOBJECTS+1; // so that it gets spawned again optionsreturn = 0; for (fpb=2;fpb<6;fpb++) // bodge players[fpb].score = 0; return(1); // arcade break; } } } } } else if (optstatus == 3) // init time trial { if (pc == 0) optstatus = 4; duston = 1; dustang = 0; limitrange(selectedclass,0,2); status = selectedclass; dusty = -7680+((status*20)<<7); for (f=0;fdx; dustdx2 = ((float)(dustdx-(veltabpt+((dustang+31)%32))->dx)/10)*(dustm>>1); dustdx*=dustm; lspr.x = ((dustx+dustdx)>>5)-8; lspr.y = (dusty>>7)-5; for (g=0;g<2+(rand()&7);g++) { obj = spawndust(); if (obj != -1) { dust[obj].col = 255; dust[obj].dx = dustdx2; dust[obj].dy = -6+(qrand[qrandi]>>4);//-128+rand()&128; dust[obj].x = dustx+dustdx+qrand[(qrandi+1)%8]; dust[obj].y = dusty+(qrand[qrandi]<<2); qrandi=(qrandi+1)%8; } } dustang=(dustang+1)%32; } else if (optstatus == 5) // choose loc init { duston = 1; dustang = 0; status = selectedlocation; dusty = -7680; for (f=0;fdx; dustdx2 = ((float)(dustdx-(veltabpt+((dustang+31)%32))->dx)/10)*(dustm>>1); dustdx*=dustm; lspr.x = ((dustx+dustdx)>>5)-8; lspr.y = (dusty>>7)-5; for (g=0;g<3+(rand()&3);g++) { obj = spawndust(); if (obj != -1) { dust[obj].col = 255; dust[obj].dx = dustdx2; dust[obj].dy = -6+(qrand[qrandi]>>4);//+rand()&15; dust[obj].x = dustx+dustdx+qrand[(qrandi+1)%8]; dust[obj].y = dusty+(qrand[qrandi]); qrandi=(qrandi+1)%8; } } dustang=(dustang+1)%32; } else if (optstatus == 7) // choose track init { duston = 1; dustang = 0; status = selectedtrack; dusty = -7680; for (f=0;f=num) status = 0; optionchoosetrackinfo(&tracks[location[selectedlocation].index[status]]); if (pc & PADRup) optstatus = 5; if (pc & PADRdown) { selectedtrack = status; optstatus = 9; } if (butwait == 0) { if (pc & PADLup) { status = (status+(num-1))%num; butwait = 8; } if (pc & PADLdown) { status = (status+1)%num; butwait = 8; } } else butwait--; dustx = (-140<<5)+(((strlen(tracks[location[selectedlocation].index[status]].name)/2)<<3)<<5); dusty = ((-54)<<7)+((status*10)<<7); dustm = strlen(tracks[location[selectedlocation].index[status]].name)/2; dustdx = (veltabpt+dustang)->dx; dustdx2 = ((float)(dustdx-(veltabpt+((dustang+31)%32))->dx)/10)*(dustm>>1); dustdx*=dustm; lspr.x = ((dustx+dustdx)>>5)-8; lspr.y = (dusty>>7)-5; for (g=0;g<3+(rand()&3);g++) { obj = spawndust(); if (obj != -1) { dust[obj].col = 255; dust[obj].dx = dustdx2; dust[obj].dy = -6+(qrand[qrandi]>>4); dust[obj].x = dustx+dustdx+qrand[(qrandi+1)%8]; dust[obj].y = dusty+(qrand[qrandi]); qrandi=(qrandi+1)%8; } } dustang=(dustang+1)%32; } else if (optstatus == 9) // final options init { status = 0; // if (pc == 0) optstatus = 10; } else if (optstatus == 10) // final options { print8(156,16,"GO"); if (pc & PADRup) optstatus = 7; if (status == 0) { optionsreturn = 40; players[playeri].active = 1; // make player active so that they can drive players[playeri].maxspeed = maxspeeds[selectedclass]; return(2); } } else if (optstatus == 11) // options init { status = 0; if (pc == 0) optstatus = 12; } else if (optstatus == 12) // options { optionoptions(status); if (pc & PADRup) optstatus = 1; if (butwait == 0) { if (pc & PADRdown && status == 0) optstatus = 13; if (pc & PADRdown && status == 4) optstatus = 1; // if (pc & PADRdown && status == 1) // { // optstatus = 15; // } if (pc & PADLright) { status+=1; butwait = 6; } if (pc & PADLleft) { status-=1; butwait = 6; } if (pc & PADLdown) { status+=2; butwait = 6; } if (pc & PADLup) { status-=2; butwait = 6; } } else butwait--; if (status == 1) status = 2; if (pc & PADL1) if (status == 3 && musicvolume>0) musicvolume--; else if (status == 2 && mainvolume>0) mainvolume--; if (pc & PADR1) if (status == 3 && musicvolume<127) musicvolume++; else if (status == 2 && mainvolume<127) mainvolume++; SsSeqSetVol(funkseq,musicvolume,musicvolume); SsSeqSetVol(musicseq,musicvolume,musicvolume); SsSetMVol(mainvolume,mainvolume); if (status == 0) print8(64,216,"SCREEN CENTRE ADJUSTMENT"); else if (status == 2) print8(88,216,"ADJUST MAIN VOLUME"); else if (status == 3) print8(88,216,"ADJUST MUSIC VOLUME"); else if (status == 4) print8(64,216,"EXIT FROM OPTIONS SCREEN"); // else if (status == 1) // print8(88,216,"MEMORY CARD OPTIONS"); limitrange(status,0,4); if (status<4) drawbox((status%2)*160-107,(status>>1)*50-82,53,37); else drawbox(-27,18,53,37); } else if (optstatus == 13) // centre screen init { if (pc == 0) optstatus = 14; } else if (optstatus == 14) // centre screen { optioncentrescreen(); if (pc & PADRdown || pc & PADRup) optstatus = 11; if (pc & PADLleft) GsDISPENV.screen.x-=1; if (pc & PADLright) GsDISPENV.screen.x+=1; if (pc & PADLup) GsDISPENV.screen.y-=1; if (pc & PADLdown) GsDISPENV.screen.y+=1; limitrange(GsDISPENV.screen.x,-8,64); limitrange(GsDISPENV.screen.y,-2,64); } else if (optstatus == 15) // memory card init init { status = 14; // optstatus = 16; optstatus = 11; } else if (optstatus>15 && optstatus<40) optstatus = 1; else if (optstatus == 40) // return from race here { if (laprecordholder == NULL) // if didn't get a lap record optstatus = 7; else { for (f=0;f<15;f++) name[f]=0; // clear name butwait = 0; status = 16; cpos = 0; // name text position optstatus = 41; // go to high score entry } } else if (optstatus == 41) // high score entry { optionentername(); if (pc & PADRup) optstatus = 7; print8(104,32,"NEW LAP RECORD"); c=drawtextentry(96,64,status); print8(120,120,name); if (butwait == 0) { if (pc & PADLleft) { status-=1; butwait=6; } if (pc & PADLright) { status+=1; butwait=6; } if (pc & PADLup) { status-=8; butwait=6; } if (pc & PADLdown) { status+=8; butwait=6; } limitrange(status,0,31); if (cpos==10) limitrange(status,30,31); // limit to DEL and END if name is 10 chars long if (pc & PADRdown) { if (status == 31) // if END { name[cpos]=0; strcpy(&tracks[location[selectedlocation].index[selectedtrack]].records.bestlapname[selectedclass][0],name); optstatus = 7; } else if (status == 30) // if DEL { name[cpos]=0; if (cpos>0) cpos--; name[cpos]=0; } else { name[cpos]=c; if (cpos<10) cpos++; } butwait=6; } } else butwait--; drawbox(92+((status%8)<<4)-SCREENWIDTH/2,68+((status>>3)<<4)-SCREENHEIGHT/2,16,16); } else if (optstatus == 99) { print8(16,16,"HOVER CAR RACING"); print8(16,32,"BY RICHARD SMITHIES"); print8(16,64,"WITH THANKS TO"); print8(16,72,"MICHAEL DE CRUZ"); print8(16,80,"FOR ADDITIONAL TRACK DESIGN"); if (titlescreenn>0) titlescreenn--; else optstatus = 0; if ( (players[0].paddata[1] & PADstart) || (players[1].paddata[1] & PADstart) ) titlescreenn = 1; } UpdateObjectCoordinates(&backlobj); UpdateObjectCoordinates(&backrobj); DisplayObject3D(&backlobj); DisplayObject3D(&backrobj); for (f=0;f>5; dot.y0 = dot.y1 = dust[f].y>>7; dot.g = dust[f].col; GsSortLine(&dot,&WorldOT[activebuff],0*OTPRI_GROUND); dust[f].x+=dust[f].dx; dust[f].y+=dust[f].dy; dust[f].dy+=1; dust[f].col-=2; if (dust[f].col < 16) dust[f].active = 0; } } } GsSortOt(&BackOT[activebuff],&WorldOT[activebuff]); GsSortOt(&BotOT[activebuff],&WorldOT[activebuff]); FntPrint("Hsync:%d\n",hcnt); // FntFlush(-1); DrawSync(0); GsDrawOt(&OverlayOT[activebuff]); DrawSync(0); hcnt = VSync(0); GsSwapDispBuff(); GsSortClear(0,0,0,&WorldOT[activebuff]); GsDrawOt(&WorldOT[activebuff]); } return(0); } int spawndust() { int f; for (f=0;f128) { g = 128; gd = -4; } if (g<32) { g = 32; gd = 4; } } void drawbox(int x,int y,int w, int h) { drawbox2(x,y,w,h); drawbox2(x+1,y+1,w-2,h-2); } void drawline(int x, int y, int w, int h) { GsLINE line; line.attribute = 0; line.r = 96; line.g = 128; line.b = 112; line.x0=x-SCREENWIDTH/2; line.y0 = y-SCREENHEIGHT/2; line.x1=line.x0+w; line.y1 = line.y0+h; GsSortLine(&line,&WorldOT[activebuff],0); } int memorycardload(long card) { // card is either 1 or 2. returns -1 if failure return(0); } int memorycardsave(long card,long avail) { // card is either 1 or 2. avail is savexavail (x is card) returns -1 if failure return(0); } void optionsmemorycard(long card1,long card2,long save1avail,long save2avail) { } void optioncentrescreen() { drawline(0,0,0,32); drawline(0,0,32,0); drawline(319,0,0,32); drawline(319,0,-32,0); drawline(0,239,0,-32); drawline(0,239,32,0); drawline(319,239,0,-32); drawline(319,239,-32,0); drawline(SCREENWIDTH/2,SCREENHEIGHT/2-16,0,32); drawline(SCREENWIDTH/2-16,SCREENHEIGHT/2,32,0); } void optionoptions(int status) { GsSPRITE spr; GsLINE line; int vol; spr.attribute = (1<<24) | (1<<27); spr.w = 76; spr.h = 13; spr.tpage = GetTPage(0,0,640,256); spr.cx = 640; spr.cy = 496; spr.r = spr.g = spr.b = 128; spr.u = 0; // OPTIONS spr.v = 95+48; spr.x = -38; spr.y = -120; GsSortFastSprite(&spr,&WorldOT[activebuff],4); spr.u = 0; // CENTRE SCREEN BOX spr.v = 124+48; spr.w = 50; spr.h = 34; spr.x = -105; spr.y = -80; GsSortFastSprite(&spr,&WorldOT[activebuff],4); // spr.u = 200; // Memory card options // spr.x = 55; spr.y = -80; // GsSortFastSprite(&spr,&WorldOT[activebuff],4); spr.u = 50; // Music Volume spr.x = 55; spr.y = -30; GsSortFastSprite(&spr,&WorldOT[activebuff],4); spr.u = 100; // Main Volume spr.x = -105; spr.y = -30; GsSortFastSprite(&spr,&WorldOT[activebuff],4); spr.u = 150; // Exit spr.x = -25; spr.y = 20; GsSortFastSprite(&spr,&WorldOT[activebuff],4); if (status == 3 || status == 2) // if on volume control box { vol = mainvolume; if (status == 3) vol = musicvolume; vol = vol/2.35185; spr.u = 0; spr.v = 159+48; // volume bar spr.w = 100; spr.h = 13; spr.x = -50; spr.y = 70; GsSortFastSprite(&spr,&WorldOT[activebuff],4); line.r = line.g = line.b = 180; line.attribute = 0; line.x0 = spr.x + 22; line.y0 = spr.y + 6; line.x1 = line.x0 + vol; line.y1 = line.y0; GsSortLine(&line,&WorldOT[activebuff],3); } } void optionchooseclass() { GsSPRITE spr; spr.attribute = (1<<24) | (1<<27); spr.w = 124; spr.h = 13; spr.tpage = GetTPage(0,0,640,256); spr.u = 0; spr.v = 48; spr.cx = 640; spr.cy = 496; spr.r = spr.g = spr.b = 128; spr.x = -62; spr.y = -120; GsSortFastSprite(&spr,&WorldOT[activebuff],4); spr.w = 64; // novice spr.h = 13; spr.u = 0; spr.v = 54+48; spr.x = -140; spr.y = -64; GsSortFastSprite(&spr,&WorldOT[activebuff],4); spr.w = 100; // competent spr.h = 13; spr.u = 0; spr.v = 68+48; spr.x = -140; spr.y = -44; GsSortFastSprite(&spr,&WorldOT[activebuff],4); spr.w = 34; // pro spr.h = 13; spr.u = 0; spr.v = 82+48; spr.x = -140; spr.y = -24; GsSortFastSprite(&spr,&WorldOT[activebuff],4); spr.w = 104; // X CHOOSE ^ BACK spr.h = 13; spr.u = 0; spr.v = 109+48; spr.x = -52; spr.y = 98; GsSortFastSprite(&spr,&WorldOT[activebuff],4); } int optionchooselocation() { int f; GsSPRITE spr; spr.attribute = (1<<24) | (1<<27); spr.w = 158; spr.h = 13; spr.tpage = GetTPage(0,0,640,256); spr.u = 0; spr.v = 15+48; spr.cx = 640; spr.cy = 496; spr.r = spr.g = spr.b = 128; spr.x = -78; spr.y = -120; GsSortFastSprite(&spr,&WorldOT[activebuff],4); spr.w = 104; // X CHOOSE ^ BACK spr.h = 13; spr.u = 0; spr.v = 109+48; spr.x = -52; spr.y = 98; GsSortFastSprite(&spr,&WorldOT[activebuff],4); for (f=0;f<2;f++) print8(16,64+f*10,location[f].name); return(2); // number of locations available } int optionchoosetrack(int loc) { // gimme index of location int f; GsSPRITE spr; spr.attribute = (1<<24) | (1<<27); spr.w = 140; spr.h = 13; spr.tpage = GetTPage(0,0,640,256); spr.u = 0; spr.v = 28+48; spr.cx = 640; spr.cy = 496; spr.r = spr.g = spr.b = 128; spr.x = -70; spr.y = -120; GsSortFastSprite(&spr,&WorldOT[activebuff],4); spr.w = 104; // X CHOOSE ^ BACK spr.h = 13; spr.u = 0; spr.v = 109+48; spr.x = -52; spr.y = 98; GsSortFastSprite(&spr,&WorldOT[activebuff],4); for (f=0;fnumlaps); print8(160,64,"TRACK RECORD"); time8(160,72,tpt->records.trackrecord[selectedclass]); print8(224,72,&tpt->records.bestname[selectedclass][0]); print8(160,88,"LAP RECORD"); time8(160,96,tpt->records.laprecord[selectedclass]); print8(224,96,&tpt->records.bestlapname[selectedclass][0]); } void optionentername() { GsSPRITE spr; spr.attribute = (1<<24) | (1<<27); spr.w = 156; spr.h = 13; spr.tpage = GetTPage(0,0,640,256); spr.u = 0; spr.v = 41+48; spr.cx = 640; spr.cy = 496; spr.r = spr.g = spr.b = 128; spr.x = -70; spr.y = -120; GsSortFastSprite(&spr,&WorldOT[activebuff],4); } void arcadeentry(int racenumber) { // Displays Arcade entry screens int f,g,QUIT_FLAG,numplayersfinished,numplayersactive,hcnt; u_long obj; short scrx; u_char flash; u_short pc; // player controller char c; u_char fade[2],blocktype[2],gamepaused[2]; RECT rct; GsLINE line; RECT huesatrect; GsSPRITE huesatsprite; u_long tmplong; GsSPRITE spr; huesatsprite.attribute = 2<<24; huesatsprite.w = huesatsprite.h = 64; huesatsprite.tpage = GetTPage(0,0,640,256); huesatsprite.u = huesatsprite.v = 0; huesatsprite.r = huesatsprite.g = huesatsprite.b = 128; spr.attribute = (1<<24) | (1<<27); spr.tpage = GetTPage(0,0,640,256); spr.cx = 640; spr.cy = 496; spr.r = spr.g = spr.b = 128; line.attribute = 0; line.r = 128; line.g = 128; line.b = 128; TransferTIM(_PAK_kelly_INDEX); // LCD background TransferTIM(_PAK_smallblocks_INDEX); // LCD blocks rct.x=816; rct.y = 257; rct.w = 16; rct.h = 11; LoadImage(&rct, (u_long *) blockcolours); // copy block CLUTS to frame buffer TransferTIM(_PAK_huesattim_INDEX); huesatrect.x = 640; huesatrect.y = 256; huesatrect.w = 64; huesatrect.h = 64; for (f=0;f<2;f++) { fade[f]=64; // brightness of background pic ( for games ) blocktype[f]=0; gamepaused[f]=0; } for (f=0;f<2;f++) { WorldOT[f].length = OT_LENGTH; WorldOT[f].org = OTTags[f]; TopOT[f].length = TOPOT_LENGTH; TopOT[f].org = TopOTTags[f]; BotOT[f].length = BOTOT_LENGTH; BotOT[f].org = BotOTTags[f]; OverlayOT[f].length = OVERLAYOT_LENGTH; OverlayOT[f].org = OverlayOTTags[f]; BackOT[f].length = BACKOT_LENGTH; BackOT[f].org = BackOTTags[f]; } // Set up text area FntLoad(960,256); FntOpen(16-SCREENWIDTH/2,16-SCREENHEIGHT/2,256,200,0,512); // Put sprite graphics in frame buffer Initializeframes(); // Clear all objects InitializeObjects(); for (f=0;f0) players[f].name[15]-=1; players[f].bestlap = 16; thud(-90); } else if (pc & PADLright) { if (players[f].name[15]<31) players[f].name[15]+=1; players[f].bestlap = 16; thud(90); } else if (pc & PADLup) { if (players[f].name[15]>7) players[f].name[15]-=8; players[f].bestlap = 16; thud(0); } else if (pc & PADLdown) { if (players[f].name[15]<24) players[f].name[15]+=8; players[f].bestlap = 16; thud(180); } if (pc & PADL1) if (fontcolour>0) { short vn; fontcolour--; updatefontcolour(); players[f].bestlap = 16; vn=SsUtKeyOn(soundvab,0,0,63,0,110,60); // printf("%d\n",vn); // printf("%d\n",SsUtKeyOn(soundvab,0,0,90,0,63,63)); // printf("%d\n",SsUtKeyOn(soundvab,0,1,60,1,63,63)); // printf("%d\n",SsUtKeyOn(soundvab,2,0,60,0,63,63)); // printf("Vol %d\n",SsUtSetVVol(vn,63,63)); } if (pc & PADR1) if (fontcolour<11) { fontcolour++; updatefontcolour(); players[f].bestlap = 16; SsUtKeyOn(soundvab,0,0,63,0,60,110); } } else if (pc == 0) // if no key pressed players[f].bestlap=0; // allow instant movement next time else players[f].bestlap-=1; // else wait a little for next move if (players[f].name[14] == 127) // if no selected character { if (pc & PADRdown) // select character { if (players[f].name[15]==31) // if END character selected { ResetPlayer(f); players[f].active = 1; players[f].laptime=4; // go to menu players[f].bestlap = 8; thud(0); } else if (players[f].name[15]==30) // else if DELETE character selected { if ( (players[f].name[13]>0) && (players[f].bestlap == 0) ) { players[f].name[13]-=1; players[f].name[players[f].name[13]]=0; players[f].bestlap = 8; } } else // whizz character { if (players[f].name[13]<10) // if not 10th character { players[f].name[14] = c; // selected character becomes ASCII of highlighted char players[f].inventory[0] = ((players[f].name[15]%8)<<4)+scrx+16; // whizzy character x players[f].inventory[1] = ((players[f].name[15]/8)<<4)+64; // whizzy character y players[f].inventory[2] = ((players[f].name[13])<<3)+scrx+32; // whizzy destination x thud(0); } } } } else // if whizzy character needs moving { if (players[f].inventory[0]players[f].inventory[2]) players[f].inventory[0]-=2; if (players[f].inventory[1]<200) players[f].inventory[1]+=4; if ( (players[f].inventory[0] == players[f].inventory[2]) && (players[f].inventory[1] >= 200) ) // if got to destination { players[f].name[players[f].name[13]] = players[f].name[14]; players[f].name[13]+=1; players[f].name[players[f].name[13]] = 0; players[f].name[14]=127; } else char8fast(players[f].inventory[0],players[f].inventory[1],players[f].name[14]); } } else if (players[f].laptime == 1) // MENU SCREEN { print8(scrx+80-(strlen(players[f].name)*4),0,players[f].name); // display player name char8fast(scrx+0,16,'$'); int8(scrx+8,16,5,players[f].cash); if (pc & PADstart) // if pressed START players[f].laptime=2; // goto finished screen print8(scrx+36,64,"RESPRAY"); print8(scrx+36,72,"TRADE"); print8(scrx+36,80,"GAMBLE"); print8(scrx+36,88,"RACE"); print8(scrx+36,96,"EXIT"); print8(scrx+28,64+players[f].name[15]*8,"*"); // selector if (players[f].bestlap>0) // bestlap used to slow down pad input { players[f].bestlap--; // wait a while if (pc == 0) players[f].bestlap = 0; // or until pad is released } if (players[f].bestlap == 0) // to stop fast key repeat { if ( (pc & PADLup) && (players[f].name[15]>0) ) { players[f].name[15]--; // menu selector up players[f].bestlap = 8; thud(0); } else if ( (pc & PADLdown) && (players[f].name[15]<4) ) { players[f].name[15]++; // menu selector down players[f].bestlap = 8; thud(0); } if (pc & PADRdown) { players[f].bestlap = 8; switch(players[f].name[15]) { case 0: // respray { players[f].laptime = 5; players[f].name[14] = 32; // Hue players[f].name[13] = 32; // Sat break; } case 1: // trade { players[f].laptime = 13; break; } case 2: // gamble { players[f].laptime = 6; break; } case 3: // race { players[f].laptime = 2; break; } case 4: { players[0].active = 0; players[1].active = 0; players[0].fuel = 0; players[1].fuel = 0; return; break; } } } } } else if (players[f].laptime == 2) // just finished { kill(players[f].object); players[f].object = 0; players[f].laptime++; numplayersfinished++; } else if (players[f].laptime == 3) // has finished, is waiting { print8(scrx+80-(strlen(players[f].name)*4),0,players[f].name); // display player name print8(scrx+36,80,"WAITING FOR"); print8(scrx+32,96,"OTHER PLAYER"); if (pc & PADRup) // if TRIANGLE pressed { players[f].laptime = 4; // return to menu players[f].bestlap = 8; numplayersfinished--; } } else if (players[f].laptime == 4) // menu screen entry point { players[f].name[15] = 0; // menu selector players[f].laptime = 1; // menu screen players[f].bestlap = 8; if (players[f].object == 0) // if car not already displayed { obj = spawn(); // set up a car object to represent the players car object[obj].x = (f*160+80)<0) // bestlap used to slow down pad input { players[f].bestlap--; // wait a while if (pc == 0) players[f].bestlap = 0; // or until pad is released } if (players[f].bestlap == 0) // to stop fast key repeat { if ( (pc & PADLleft)) { if (players[f].name[14]>0) players[f].name[14]--; players[f].bestlap = 1; } else if ( (pc & PADLright) ) { if (players[f].name[14]<63) players[f].name[14]++; players[f].bestlap = 1; } if (pc & PADLup) { if (players[f].name[13]>0) players[f].name[13]--; players[f].bestlap = 1; } else if (pc & PADLdown) { if (players[f].name[13]<63) players[f].name[13]++; players[f].bestlap = 1; } if (pc & PADRdown) players[f].laptime = 4; // back to menu } } else if (players[f].laptime == 6) // gamble { print8(scrx+80-(strlen(players[f].name)*4),0,players[f].name); // display player name char8fast(scrx+0,16,'$'); int8(scrx+8,16,5,players[f].cash); print8(scrx+36,64,"STACKS $20"); print8(scrx+36,72,"????"); print8(scrx+36,80,"????"); print8(scrx+36,88,"EXIT"); print8(scrx+28,64+players[f].name[15]*8,"*"); // selector limitrange(players[f].name[15],0,3); if (players[f].bestlap>0) // bestlap used to slow down pad input { players[f].bestlap--; // wait a while if (pc == 0) players[f].bestlap = 0; // or until pad is released } if (players[f].bestlap == 0) // to stop fast key repeat { if ( (pc & PADLup) && (players[f].name[15]>0) ) { players[f].name[15]--; // gamble selector up players[f].bestlap = 8; thud(0); } else if ( (pc & PADLdown) && (players[f].name[15]<3) ) { players[f].name[15]++; // gamble selector down players[f].bestlap = 8; thud(0); } if (pc & PADRdown) { players[f].bestlap = 8; switch(players[f].name[15]) { case 0: { if (players[f].cash>=20) { players[f].laptime = 7; // goto game1 players[f].cash-=20; } break; } // case 1: // { // if (players[f].cash>=20) // { // players[f].laptime = 11; // players[f].cash-=20; // } // break; // } case 3: { players[f].laptime = 4; // return to menu break; } } } } } else if (players[f].laptime == 7) // game1 entry { clearlcd(&lcd[f]); lcd[f].sx = f*160+16; lcd[f].sy = 48; game1[f].lcdpt = &lcd[f]; game1[f].speed = 25<<8; game1[f].x=8; game1[f].y=0; game1[f].pause = 25; // to slow block fall game1[f].padcount = 0; // to slow key repeat game1[f].status = 0; // 0 = falling blocks newgame1block(game1[f].current); newgame1block(game1[f].next); players[f].laptime = 8; for (g=0;g<3;g++) lcdblock(game1[f].lcdpt,0,g,game1[f].next[g]); // draw 'NEXT' block in corner } else if (players[f].laptime == 8) // columns running { spr.w = 46; // BUY spr.h = 11; spr.u = 108; spr.v = 159+48; spr.x = scrx+24-SCREENWIDTH/2-23; spr.y = 180-SCREENHEIGHT/2; if (gamepaused[f]==0) GsSortFastSprite(&spr,&WorldOT[activebuff],4); print8(scrx+80-(strlen(players[f].name)*4),0,players[f].name); // display player name char8fast(scrx+0,16,'$'); int8(scrx+8,16,5,players[f].cash); game1[f].pad = pc; // pass pad status to game if (gamepaused[f]==0) // if not paused { if ( rungame1(&game1[f],&players[f].cash) != 0) // run game (1 frame) and if game is over players[f].laptime = 10; // go to game over backfader(pc, &fade[f]); blocktypechange(pc, &blocktype[f]); lcddisplay(&lcd[f],fade[f],blocktype[f]); if (players[f].bestlap == 0) { if (pc & PADstart) // if START { gamepaused[f] = 1; players[f].bestlap = 8; } } else { players[f].bestlap--; } } else { print8(scrx+16,64," GAME PAUSED"); print8(scrx+16,80,"START CONTINUE"); print8(scrx+16,88,"SELECT QUIT"); if (players[f].bestlap == 0) { if (pc & PADstart) { gamepaused[f] = 0; players[f].bestlap = 8; } if (pc & PADselect) { players[f].laptime = 9; // wait to release, players[f].bestlap = 6; // then go back to gamble menu } } else players[f].bestlap--; } } else if (players[f].laptime == 9) // wait to release all buttons { if (pc == 0) players[f].laptime = players[f].bestlap; } else if (players[f].laptime == 10) { print8(scrx+80-(strlen(players[f].name)*4),0,players[f].name); // display player name char8fast(scrx+0,16,'$'); int8(scrx+8,16,5,players[f].cash); print8(scrx+72,88,"GAME"); print8(scrx+72,104,"OVER"); if (pc & PADstart) // if START { players[f].laptime = 9; // wait to release, players[f].bestlap = 6; // then go back to gamble menu } } else if (players[f].laptime == 11) // game 2 (tetris) init { clearlcd(&lcd[f]); lcd[f].sx = f*160+16; lcd[f].sy = 48; game2[f].lcdpt = &lcd[f]; game2[f].speed = 25<<8; game2[f].x=8; game2[f].y=0; game2[f].pause = 25; // to slow block fall game2[f].padcount = 0; // to slow key repeat game2[f].status = 0; // 0 = falling blocks players[f].laptime = 12; } else if (players[f].laptime == 12) // game 2 running { print8(scrx+80-(strlen(players[f].name)*4),0,players[f].name); // display player name char8fast(scrx+0,16,'$'); int8(scrx+8,16,5,players[f].cash); game2[f].pad = pc; // pass pad status to game if ( rungame2(&game2[f],&players[f].cash) != 0) // run game (1 frame) and if game is over players[f].laptime = 10; // go to game over backfader(pc, &fade[f]); lcddisplay(&lcd[f],fade[f],blocktype[f]); if (pc & PADstart) // if START { players[f].laptime = 9; // wait to release, players[f].bestlap = 6; // then go back to gamble menu } } else if (players[f].laptime == 13) // trade start { players[f].name[15] = 0; // selected inv number players[f].laptime = 14; } else if (players[f].laptime == 14) // trade running { spr.w = 42; // BUY spr.h = 14; spr.u = 156; spr.v = 108+48; spr.x = scrx+80-SCREENWIDTH/2-22; spr.y = 208-SCREENHEIGHT/2; GsSortFastSprite(&spr,&WorldOT[activebuff],4); spr.w = 48; // SELL spr.u = 198; spr.y = 222-SCREENHEIGHT/2; GsSortFastSprite(&spr,&WorldOT[activebuff],4); spr.w = 46; // BACK spr.u = 58; spr.y = 194-SCREENHEIGHT/2; GsSortFastSprite(&spr,&WorldOT[activebuff],4); print8(scrx+80-(strlen(players[f].name)*4),0,players[f].name); // display player name char8fast(scrx+0,16,'$'); int8(scrx+8,16,5,players[f].cash); char8spritefast.r = char8spritefast.g = char8spritefast.b = 80; players[f].inventory[INV_FUEL] = players[f].fuel>>MYBITSHIFT; if (players[f].name[15]>0) { print8(scrx+16,80-8,&invtext[players[f].name[15]-1][0]); // display previous inv text int8(scrx+120,80-8,3,players[f].inventory[players[f].name[15]-1]); // and number of it } if (players[f].name[15]<4) { print8(scrx+16,80+8,&invtext[players[f].name[15]+1][0]); // display next inv text int8(scrx+120,80+8,3,players[f].inventory[players[f].name[15]+1]); // and number of it } char8spritefast.r = char8spritefast.g = char8spritefast.b = 128; print8(scrx+16,80,&invtext[players[f].name[15]][0]); // display selected inv text int8(scrx+120,80,3,players[f].inventory[players[f].name[15]]); // and number of it print8(scrx+16,152,"BUY $"); int8(scrx+64,152,4,inventorylist[players[f].name[15]].buy); print8(scrx+16,160,"SELL $"); int8(scrx+64,160,4,inventorylist[players[f].name[15]].sell); print8(scrx+16,168,"UNIT SIZE"); int8(scrx+96,168,3,inventorylist[players[f].name[15]].amount); print8(scrx+16,176,"LIMIT"); int8(scrx+96,176,3,inventorylist[players[f].name[15]].max); if (players[f].bestlap == 0) { if (pc & PADLup) if (players[f].name[15] > 0) { players[f].name[15]-=1; players[f].bestlap = 10; } if (pc & PADLdown) if (players[f].name[15]<4) { players[f].name[15]+=1; players[f].bestlap = 10; } if (pc & PADRdown) // X - Buy item { if (players[f].inventory[players[f].name[15]] < inventorylist[players[f].name[15]].max) // if has space if (players[f].cash > inventorylist[players[f].name[15]].buy) // if can afford { if (players[f].name[15] == INV_FUEL) players[f].fuel += 100<= inventorylist[players[f].name[15]].amount) // if have enough { if (players[f].name[15] == INV_FUEL) players[f].fuel -= 100<0) && (numplayersactive == numplayersfinished) ) QUIT_FLAG = 1; ObjectHandler(); // display and handle objects obj3d[31].rotate.vy+=4; for (f=0;f>5)&31; cg = (clut[g]>>10)&31; brightmult = ( ((float)(g-1)/10.0) ); // set brightness multiplier cr = cr * brightmult; cg = cg * brightmult; cb = cb * brightmult; // printf("colour %d %d %d %d mult=%f\n",g,cr,cg,cb,brightmult); clut[g] = ((cg&31)<<10) | ((cb&31)<<5) | (cr&31); } for (g=10;g<=14;g++) { clut[g] = col; cr = clut[g]&31; cb = (clut[g]>>5)&31; cg = (clut[g]>>10)&31; brightmult = ( ((float)(g-4)/10.0) ); // set brightness multiplier cr = cr * brightmult; cg = cg * brightmult; cb = cb * brightmult; // printf("colour %d %d %d %d mult=%f\n",g,cr,cg,cb,brightmult); clut[g] = ((cg&31)<<10) | ((cb&31)<<5) | (cr&31); } rct.x = 576 + (car%4)*16; rct.y = 241+(car>>2); rct.w = 16; rct.h = 1; LoadImage(&rct,(u_long *)clut); // copy clut to carcolour area } void initstarfield(VECTOR *pt) { int f; for (f=0;fvx = (rand()-16384)/16; pt->vy = (rand()-16384)/16; pt->vz = (rand()-16384)/16; pt++; } } void thud(short angle) { // makes thud sound for menu movement etc. angle is 0 for forwards, -90 left 90 right volume *pt; if (angle<0) angle+=360; angle=angle%360; pt=volumetabpt+angle; SsUtKeyOn(soundvab,0,0,63,0,pt->lv,pt->rv); } void prang() { // car prang sound SsUtKeyOn(soundvab,6,0,62,0,80,80); } void boom() { // explosion sound SsUtKeyOn(soundvab,6,0,52,0,120,120); } void backfader(u_short p, u_char *fade) { // fades fade with L2 and R2 if ( p & PADL2 ) if ((*fade)>0) (*fade)-=1; if ( p & PADR2 ) if ((*fade)<255) (*fade)+=1; } void blocktypechange(u_short p, u_char *type) { // alters block type with L1 and R1 static u_char count = 0; if (count == 0) { if (p & PADL1) if ( (*type)>0 ) { (*type)-=1; count = 12; } if (p & PADR1) if ( (*type)<3 ) { (*type)+=1; count = 12; } } else count--; if (p == 0) count = 0; } void clearlcd(lcdgrid *pt) { // clears lcd grid int f; for (f=0;f<(16*22);f++) pt->grid[f]=0; } void lcddisplay(lcdgrid *lcdpt,u_char fade,u_char type) { // shows lcd on screen u_short x,y; u_char b; sprite16bit.x = (lcdpt->sx)-160+32; sprite16bit.y = (lcdpt->sy)-120; sprite16bit.u = 0; sprite16bit.v = 16; sprite16bit.r = sprite16bit.g = sprite16bit.b = fade; sprite16bit.w = 64; sprite16bit.h = 176; GsSortFastSprite(&sprite16bit,&WorldOT[activebuff],8); for (y=0;y<22;y++) for (x=0;x<16;x++) { b = lcdpt->grid[x+y*16]; if (b>0 && b<254) { blocksprite.x = (lcdpt->sx)-160+x*8; blocksprite.y = (lcdpt->sy)-120+y*8; blocksprite.u = (((type)>>1)<<5)+((b>>6)<<3); blocksprite.v = ((type)%2)<<3; blocksprite.cy = 256+(b&15); GsSortFastSprite(&blocksprite,&WorldOT[activebuff],0); } } } void lcdblock(lcdgrid *lcdpt,short x, short y,u_char block) { // puts a block onto the lcd u_long offset; offset = x+(y<<4); if ( (offset>=0) && (offset<(16*22)) ) lcdpt->grid[offset] = block; } u_char lcdread(lcdgrid *lcdpt, short x, short y) { // returns block on lcd at x,y u_long offset; offset = x+(y<<4); if ( (x>=0) && (x<16) && (y>=0) && (y<22) ) return(lcdpt->grid[offset]); else return(255); // if off screen, return a 255 } void newgame1block(u_char *pt) { // generates next block for colums game long r; r = rand(); pt[0]=((r%7)+1)+48; pt[1]=(((r>>4)%7)+1)+48; pt[2]=(((r>>8)%7)+1)+48; } u_char game1movevalid(short x,short y, columsdef *game) { // returns 0 if stack (3blocks) can move to these x,y if (lcdread(game->lcdpt,x,y)!=0) return(1); if (y>0) if (lcdread(game->lcdpt,x,y-1)!=0) return(2); if (y>1) if (lcdread(game->lcdpt,x,y-2)!=0) return(3); return(0); } void updategame1delete(columsdef *game) { // deletes matching blocks horizontally (marks them for deletion actually) short x,y; int count,f; u_char prev,curr; for (y=0;y<22;y++) { count = 0; prev = 255; for (x=4;x<13;x++) // check horizontal (reads 255 if over actual lcd) (if don't read 17, ignores rows that end on last line) { curr = lcdread(game->lcdpt,x,y); if ( (curr == prev) && (curr != 255) && (curr != 0) ) count++; else { if (count>1) for (f=0;f<=count;f++) { game->remove[game->removecount].x = x-f-1; // put into remove list game->remove[game->removecount].y = y; game->removecount++; } prev = curr; count = 0; } } } } void updategame1deletevert(columsdef *game) { // deletes matching blocks vertically short x,y; int count,f; u_char prev,curr; for (x=4;x<12;x++) { count = 0; prev = 255; for (y=0;y<23;y++) // check horizontal (reads 255 if over actual lcd) (if don't read 17, ignores rows that end on last line) { curr = lcdread(game->lcdpt,x,y); if ( (curr == prev) && (curr != 255) && (curr != 0) ) count++; else { if (count>1) for (f=0;f<=count;f++) { game->remove[game->removecount].x = x; // put into remove list game->remove[game->removecount].y = y-f-1; game->removecount++; } prev = curr; count = 0; } } } } void updategame1deletediagdr(columsdef *game) { // deletes diagonals (down right) short x,y,d; int count,f; u_char prev,curr; for (y=-12;y<21;y++) // start above screen { count = 0; prev = 255; for (x=0;x<12;x++) // do diagonals { curr = lcdread(game->lcdpt,x+4,y+x); if ( (curr == prev) & (curr != 255) & (curr != 0) ) count++; else { if (count>1) { for (f=0;f<=count;f++) { game->remove[game->removecount].x = x+4-f-1; game->remove[game->removecount].y = y+(x-f-1); game->removecount++; } } prev = curr; count = 0; } } } } void updategame1deletediagdl(columsdef *game) { // deletes diagonals (down left) short x,y,d; int count,f; u_char prev,curr; for (y=-12;y<21;y++) // start above screen { count = 0; prev = 255; for (x=12;x>=-1;x-=1) // do diagonals { curr = lcdread(game->lcdpt,x+4,y+12-x); if ( (curr == prev) & (curr != 255) & (curr != 0) ) count++; else { if (count>1) { for (f=0;f<=count;f++) { game->remove[game->removecount].x = x+4+f+1; game->remove[game->removecount].y = y+12-(x+f+1); game->removecount++; } } prev = curr; count = 0; } } } } void optimizeremovelist(columsdef *game) { // clears duplicates from list // otherwise, blocks do not get fully dissolved on overlapping horiz/vert matches int f,g; u_char x,y; if (game->removecount<2) return; for (f=0;fremovecount-1;f++) // step through all bar last { x = game->remove[f].x; y = game->remove[f].y; for (g=f+1;gremovecount;g++) // check rest against this { if ( (game->remove[g].x == x) && (game->remove[g].y == y) ) game->remove[g].x = 255; // if duplicate, mark as x=255 } } } int updategame1drop(columsdef *game) { // drops all blocks short x,y; int c,totalc; totalc = 0; // printf("Dropping\n"); for (y=20;y>=0;y--) { for (x=4;x<12;x++) { c = 0; if ( (lcdread(game->lcdpt,x,y) > 0) && (lcdread(game->lcdpt,x,y) < 254) ) { while (lcdread(game->lcdpt,x,y+1+c) == 0) c++; // count number of gaps below block if (c>0) { // printf("DROP %d,%d of %d c=%d\n",x,y,lcdread(game->lcdpt,x,y),c); lcdblock(game->lcdpt,x,y+c,lcdread(game->lcdpt,x,y)); // put old block at bottom lcdblock(game->lcdpt,x,y,0); // remove old block } totalc+=c; } } } return(totalc); } int rungame1(columsdef *game, long *cashpt) { // colums game int f; u_char tmp; for (f=0;f<3;f++) if ( (game->y-f) >= 0 ) lcdblock(game->lcdpt,game->x,game->y-f,0); // clear old blocks if (game->padcount == 0) { if (game->pad & PADLleft) // move left if (game->x>4) if (game1movevalid(game->x-1,game->y,game)==0) { game->x-=1; game->padcount = 8; } if (game->pad & PADLright) // move right if (game->x<11) if (game1movevalid(game->x+1,game->y,game)==0) { game->x+=1; game->padcount = 8; } if (game->pad & PADRdown) // rotate { tmp = game->current[0]; game->current[0]=game->current[1]; game->current[1]=game->current[2]; game->current[2]=tmp; game->padcount = 8; } } else game->padcount-=1; if (game->pad == 0) // if released keys, game->padcount = 0; // then can press immediately after if (game->pad == PADLdown) // if down game->pause = 0; // speed up block fall if (game->status == 0) // make next block fall { if (game->pause == 0) { game->pause = game->speed>>8; // reset pause to speed if (game->speed>(2<<8)) game->speed-=1; // speed up gradually if ( (game->y<21) && (game1movevalid(game->x,game->y+1,game)==0) ) // if not at bottom, and not blocked game->y+=1; // move down automatically else // or, if at bottom, do new block { SsUtKeyOn(soundvab,1,0,63,0,110,110); for (f=0;f<3;f++) // display old block at bottom if ( (game->y-f) >= 0 ) lcdblock(game->lcdpt,game->x,game->y-f,game->current[f]); game->status = 1; game->current[0] = game->next[0]; game->current[1] = game->next[1]; game->current[2] = game->next[2]; newgame1block(game->next); for (f=0;f<3;f++) lcdblock(game->lcdpt,0,2-f,game->next[f]); // draw 'NEXT' block in corner game->y=0; game->x=8; if ( lcdread(game->lcdpt,game->x,game->y+2) != 0) game->status = 16; // GAME OVER } } else game->pause-=1; for (f=0;f<3;f++) // display new block if ( (game->y-f) >= 0 ) lcdblock(game->lcdpt,game->x,game->y-f,game->current[f]); } else if (game->status == 1) // check remove blocks horiz { game->removecount = 0; updategame1delete(game); // check and remove lines game->status = 2; } else if (game->status == 2) // check vert { updategame1deletevert(game); game->status = 3; } else if (game->status == 3) // check diag { updategame1deletediagdr(game); game->status = 7; } else if (game->status == 7) // check diag (2) { updategame1deletediagdl(game); game->status = 4; } else if (game->status == 4) // make blocks disappear { optimizeremovelist(game); if (game->removecount>14) printf("OOOOOOOOOOOOOOOOPS, remove count %d\n",game->removecount); if (game->removecount>3) (*cashpt)+=game->removecount*2; // printf("CASH: %d",*cashpt); game->pause = 3; if (game->removecount>0) game->status = 6; else game->status = 5; } else if (game->status == 5) { if (updategame1drop(game) == 0) // drop blocks, and if total dropped is zero game->status = 0; else { SsUtKeyOn(soundvab,7,0,63,0,110,110); game->status = 1; } } else if (game->status == 6) // dissolve blocks { if (game->pause == 0) { game->pause = 3; for (f=0;fremovecount;f++) { if (game->remove[f].x != 255) // 255 means DO NOT DISSOLVE { tmp = lcdread(game->lcdpt,game->remove[f].x,game->remove[f].y); tmp+=64; if (tmp<64) { game->status = 5; tmp = 0; } lcdblock(game->lcdpt,game->remove[f].x,game->remove[f].y,tmp); } } } else game->pause--; } else if (game->status == 16) return(1); return(0); } int rungame2(tetrisdef *game, long *cashpt) { // tetris game return(1); } void playerspincontrol(u_long obj) { // spins car for arcade entry object[obj].angle=(object[obj].angle+(2<>MYBITSHIFT)/11.25); object[obj].frame=object[obj].frame<<8; } char drawtextentry(short sx,short sy,char highlight) { // draws text entry screen at coordinate sx,sy and highlights character number highlight // returns ASCII code for highlight char c; int f; short x,y; static short ang1 = 0; static short ang2 = 8; short scalex,scaley; long rotate = 4096*45; static char table[]=" ABCDEFGHIJKLMNOPQRSTUVWXYZ.!$<^"; long v1dx,v1dy,v2dx,v2dy; v1dx=((veltabpt+ang1/8)->dx); v1dy=((veltabpt+ang1/8)->dy); v2dx=((veltabpt+ang2/8)->dx); v2dy=((veltabpt+ang2/8)->dy); ang1=(ang1+3)%256; // update angles (scaled by 8) ang2=(ang2+1)%256; rotate = (v1dx*4096)/8; scalex = 6096+v2dx*6; scaley = 6096+v1dy*6; for (f=0;f<32;f++) { c = table[f]; x = sx+((f%8)<<4); y = sy+((f>>3)<<4); if (f!=highlight) char8fast(x,y,c); else { char8sprite.r = 128+v1dx/3; char8sprite.g = 128+v1dy/3; char8(x,y,scalex,scaley,rotate,c); char8sprite.r = 128; } } return(table[highlight]); } void soundtest() { int QUIT,f,hcnt; short prog,tone,note,voice,adj; char *atrpt,*pt,wt; short ar,dr,sr,rr,sl,vol,edit; GsLINE linear,linedr,linesr,linerr; QUIT = 0; prog =0; tone = 0; note = 63; voice= -1; adj = 16; wt = 0; ar = 127; dr = sr = rr = 0; sl = 15; vol = 120; edit = 0; linear.attribute = linedr.attribute = linesr.attribute = linerr.attribute = 0; atrpt = (char *)vhptr; atrpt += 2048+32; for (f=0;f<2;f++) { WorldOT[f].length = OT_LENGTH; // Very important this, don't forget it WorldOT[f].org = OTTags[f]; // it initialises the OT TopOT[f].length = TOPOT_LENGTH; TopOT[f].org = TopOTTags[f]; BotOT[f].length = BOTOT_LENGTH; BotOT[f].org = BotOTTags[f]; OverlayOT[f].length = OVERLAYOT_LENGTH; OverlayOT[f].org = OverlayOTTags[f]; } while (QUIT == 0) { // Clear up display data activebuff = GsGetActiveBuff(); //Get buffer we can use GsSetWorkBase((PACKET *)GpuPacketArea[activebuff]); // Tell the system where the packet buffer is GsClearOt(0, 0, &WorldOT[activebuff]); // Clear the Ordering Table GsClearOt(0, OTPRI_SMOKE, &TopOT[activebuff]); // 3D above ground GsClearOt(0, OTPRI_GROUND+1, &BotOT[activebuff]); // 3D below ground GsClearOt(0, 0, &OverlayOT[activebuff]); // overlay (status, scores etc..) // Read Controller for (f=0;f<2;f++) ReadController(players[f].pad, players[f].paddata); if (players[0].paddata[1] & PADselect) QUIT = 1; pt = atrpt+(tone*32+(prog*32*16)); /* atrpt = (char *)vhptr; atrpt += 2048+32; pt = atrpt+(tone*32+(prog*32*16)); *(pt+17) = 0-(127+1); *(pt+16) = (15&15) | ((15-(0&15))<<4); */ if (wt == 0) { if (players[0].paddata[1] & PADRdown) if (voice == voice) { SsUtAllKeyOff(0); voice = SsUtKeyOn(soundvab,prog,tone,note,0,120,120); } if (players[0].paddata[1] & PADRright) if (voice != -1) { SsUtKeyOff(voice,soundvab,prog,tone,note); SsUtAllKeyOff(0); voice = -1; } if (players[0].paddata[1] & PADLleft) if (prog>0) prog-=1; if (players[0].paddata[1] & PADLright) if (prog<126) prog+=1; if (players[0].paddata[1] & PADLdown) if (tone>0) tone-=1; if (players[0].paddata[1] & PADLup) if (tone<16) tone+=1; if (players[0].paddata[1] & PADL1) if (note>0) note-=1; if (players[0].paddata[1] & PADR1) if (note<99) note+=1; if (players[0].paddata[1] & PADL2) if (edit == 0) ar-=1; else if (edit == 1) dr-=1; else if (edit == 2) sr-=1; else if (edit == 3) rr-=1; else if (edit == 4) sl-=1; if (players[0].paddata[1] & PADR2) if (edit == 0) ar+=1; else if (edit == 1) dr+=1; else if (edit == 2) sr+=1; else if (edit == 3) rr+=1; else if (edit == 4) sl+=1; if (players[0].paddata[1] & PADRup) edit=(edit+1)%5; wt = 1; } else wt-=1; limitrange(edit,0,4); limitrange(ar,0,127); limitrange(dr,0,15); limitrange(sr,0,127); limitrange(rr,0,31); limitrange(sl,0,15); if (players[0].paddata[1] & PADRleft) { *(pt+17) = 0-(ar+1); *(pt+16) = (sl&15) | ((15-(dr&15))<<4); } linear.x0 = -150; linear.y0 = 110; linear.x1 = linear.x0+(127-ar)/2; linear.y1 = 110-vol; linear.r = linear.g = linear.b = 120; linedr.x0 = linear.x1; linedr.y0 = linear.y1; linedr.x1 = linedr.x0 + dr; linedr.y1 = linedr.y0 + sl*2; linedr.r = linedr.g = linedr.b = 120; linesr.x0 = linedr.x1; linesr.y0 = linedr.y1; linesr.x1 = linesr.x0 + sr/2; linesr.y1 = linesr.y0 + sr/2; linesr.r = linesr.g = linesr.b = 120; GsSortLine(&linear,&WorldOT[activebuff],0); GsSortLine(&linedr,&WorldOT[activebuff],0); GsSortLine(&linesr,&WorldOT[activebuff],0); FntPrint("prog %d tone %d note %d\n",prog,tone,note); FntPrint("Voice %d adj %d\n",voice,adj); if (edit == 0) FntPrint("AR\n"); else if (edit == 1) FntPrint("DR\n"); else if (edit == 2) FntPrint("SR\n"); else if (edit == 3) FntPrint("RR\n"); else if (edit == 4) FntPrint("SL\n"); for (f=0;f<32;f+=4) { FntPrint("%d\t%d\t%d\t%d\n",*pt,*(pt+1),*(pt+2),*(pt+3)); pt+=4; } // GsDrawOt(&OverlayOT[activebuff]); DrawSync(0); hcnt = VSync(0); // Wait for the V-Blank period GsSwapDispBuff(); // Swap screens GsSortClear(0,0,0,&WorldOT[activebuff]); GsDrawOt(&WorldOT[activebuff]); // Start drawing the OT FntFlush(-1); } } // ***************************** // ***************************** // ** ** // ** THE MAIN RACE GAME LOOP ** // ** ** // ***************************** // ***************************** void racegamemain () { int QUIT_FLAG,hcnt,fuelpause = 0; int showback,backx,backdx,backr,backdr,backstyle; // showback 0 = backdrop picture not shown int i,f,g,pause_time = 0; int numplayers,singleplayer,player2; int pl1,pl2,pltmp; int mblock; u_long playtime,cameraplayer; u_long ghostobj; // object for ghost car long xdiff,ydiff; // diffs for map camera calc long imapx,imapy,cameraspeed; u_long obj,objediting; u_long objindex[8]; u_char cameramaxchkpoint; GsSPRITE countdown; short countdownsize,countdownframe; u_long screen3dobj,screenpathpos; u_long studioobj[8]; u_long *testpt; u_long tt_attr; // trans test u_char tt_r,tt_g,tt_b; // trans test u_short c; u_long cang; // headlight test u_long hs; // headlight test scale GsFOGPARAM fog; tt_attr = 0x60000000; tt_r = 0xf0; tt_g = 0xf0; tt_b = 0xf0; hs = 19; showback = 50; backx = 4; backdx = 32; backr = 0; backdr = 16; backstyle = rand()%3; laprecordholder = NULL; // Initialize OT for (f=0;f<2;f++) { WorldOT[f].length = OT_LENGTH; // Very important this, don't forget it WorldOT[f].org = OTTags[f]; // it initialises the OT TopOT[f].length = TOPOT_LENGTH; TopOT[f].org = TopOTTags[f]; BotOT[f].length = BOTOT_LENGTH; BotOT[f].org = BotOTTags[f]; OverlayOT[f].length = OVERLAYOT_LENGTH; OverlayOT[f].org = OverlayOTTags[f]; BackOT[f].length = BACKOT_LENGTH; BackOT[f].org = BackOTTags[f]; } // Set up text area FntLoad(960,256); // Load standard 256x128 font pattern to the frame buffer at coords specified FntOpen(16-SCREENWIDTH/2,64-SCREENHEIGHT/2,256,160,0,512); // Setup a text window. x,y,w,h,isbg,n where x,y is coords in frame buffer // w,h are width and height. isbg is 0 for background clear/1 no clear, n = number of letters // Init car positions for (f=0;fplayerindex = f; object[obj].extra->controller = players[f].pad; makecarcolours(players[f].carclut,players[f].carcolour); pin++; } } } // Add CPU drivers if (timetrial == 0) // only if normal race for (f=0;f<(4-numplayers);f++) { obj = adddriver(trackinfo.grid[f+numplayers].x,trackinfo.grid[f+numplayers].y,trackinfo.grid[f+numplayers].angle,2,f+2); carpositions[f+numplayers]=object[obj].driverindex; object[obj].extra->playerindex = 2+f; makecarcolours(carcolours[f+2],f+2); } // Add Ghost driver if (timetrial == 1) { ghostobj = spawn(); newplayer(ghostobj); object[ghostobj].control = ghostcontrol; object[ghostobj].health = 0; // initially not displayed (health = 0) object[ghostobj].class = 3; object[ghostobj].cx = 624; object[ghostobj].cy = 240; ghostdata.ghostobj = ghostobj; initializeghostdata(); } // Update coordinates for all 3D objects (because static objects don't get this again) for (f=0;f 0) { lapsfinished++; print8(124,80,"RACE OVER"); } if (lapsfinished > 100) QUIT_FLAG = 1; if (numplayers == 2) if (players[singleplayer].fuel == 0) { singleplayer = player2; numplayers = 1; } else if (players[player2].fuel == 0) { numplayers = 1; } if (numplayers == 1) if (players[singleplayer].fuel == 0) { fuelpause--; if (fuelpause==0) QUIT_FLAG = 1; } if (numplayers == 2) if (players[singleplayer].fuel == 0) if (players[player2].fuel == 0) { fuelpause--; if (fuelpause==0) QUIT_FLAG = 1; } // Read Controller for (f=0;f<2;f++) ReadController(players[f].pad, players[f].paddata); // if (players[0].paddata[1] & PADL1) // for (f=0;f50) // if not immediately after game starts { RECT r; game_paused = 1; // pause r.x = 0; r.y = (1-activebuff)*256; r.w = 320; r.h = 256; MoveImage(&r,320,256); // copy screen DrawSync(0); r.x = 320; r.h = 1; for (f=0;f<256;f++) // remove all transparant black { r.y = 256+f; StoreImage(&r,(u_long *)screenline); DrawSync(0); for (g=0;g<320;g++) { // if (f==0) // printf("%x ",screenline[g]); if (screenline[g]==0) screenline[g]=0x8000; } LoadImage(&r,(u_long *)screenline); DrawSync(0); } screen3dobj = spawn3d(); create3dobject(screen3dobj,_PAK_screenmodel_INDEX); obj3d[screen3dobj].origin.vx = cameraview.vpx; obj3d[screen3dobj].origin.vy = cameraview.vpy; obj3d[screen3dobj].origin.vz = -1687; obj3d[screen3dobj].active = 2; obj3d[screen3dobj].ot = 0; obj3d[screen3dobj].handler.attribute = 0x00000000 | (3<<9) | (0<<30); screenpathpos = 0; // start of path studioobj[0] = spawn3d(); create3dobject(studioobj[0],_PAK_ndeskmodel_INDEX); obj3d[studioobj[0]].active = 1; obj3d[studioobj[0]].ot = 0; obj3d[studioobj[0]].origin.vx = cameraview.vpx; obj3d[studioobj[0]].origin.vy = cameraview.vpy+160; obj3d[studioobj[0]].origin.vz = -1720; obj3d[studioobj[0]].rotate.vz = 2048; for (f=0;f 25) { game_paused = 0; // unless already paused kill3d(screen3dobj); kill3d(studioobj[0]); pause_time = 25; } // 3D Camera GsSetRefView2(&cameraview); // Game Pause state while ( game_paused > 0 ) { if (game_paused<250) game_paused++; if ( (players[0].paddata[1] & PADstart) || (players[1].paddata[1] & PADstart)) if (game_paused>25) { game_paused = 0; kill3d(screen3dobj); kill3d(studioobj[0]); pause_time = 25; SsSeqReplay(musicseq); } if ((players[0].paddata[1] & PADselect) || (players[1].paddata[1] & PADselect)) if (game_paused>25) { game_paused = 0; kill3d(screen3dobj); kill3d(studioobj[0]); pause_time = 25; SsSeqReplay(musicseq); QUIT_FLAG = 1; quitwithpause = 1; } for (f=0;f<2;f++) ReadController(players[f].pad, players[f].paddata); screenpathpos++; GsSetRefView2(&cameraview); if (screenpathpos<50) { obj3d[screen3dobj].origin.vx = cameraview.vpx-screenpathpos*4; obj3d[screen3dobj].origin.vy = cameraview.vpy-screenpathpos*4; obj3d[screen3dobj].origin.vz = -1687 + screenpathpos*6; } // if (screenpathpos>0 && screenpathpos<25) // obj3d[screen3dobj].rotate.vx-=8; // if (screenpathpos>24 && screenpathpos<50) // obj3d[screen3dobj].rotate.vx-=6; if (screenpathpos>0 && screenpathpos<50) obj3d[screen3dobj].rotate.vy-=12; // if (screenpathpos>0 && screenpathpos<50) // obj3d[screen3dobj].rotate.vz-=2; // 3D position test /* if (players[0].paddata[1] & PADRleft) { objediting=(objediting+1)%MAX3DOBJECTS; while (obj3d[objediting].active == 0) objediting=(objediting+1)%MAX3DOBJECTS; } */ if (players[0].paddata[1] & PADRup) obj3d[screen3dobj].origin.vz-=(20); if (players[0].paddata[1] & PADRdown) obj3d[screen3dobj].origin.vz+=(20); if (players[0].paddata[1] & PADLup) obj3d[screen3dobj].origin.vy-=(20); if (players[0].paddata[1] & PADLdown) obj3d[screen3dobj].origin.vy+=(20); // FntPrint("Z %d Y %d\n",obj3d[studioobj[0]].origin.vz,obj3d[studioobj[0]].origin.vy); if (players[0].paddata[1] & PADLleft) obj3d[screen3dobj].rotate.vy-=(10); if (players[0].paddata[1] & PADLright) obj3d[screen3dobj].rotate.vy+=(10); /* if (players[0].paddata[1] & PADL2) cameraview.vpz-=10; if (players[0].paddata[1] & PADR2) cameraview.vpz+=10; if (players[0].paddata[1] & PADL1) { projection -= 2; GsSetProjection(projection); } if (players[0].paddata[1] & PADR1) { projection += 2; GsSetProjection(projection); } */ // UpdateObjectCoordinates(&obj3d[objediting]); /* if (players[0].paddata[1] & PADRdown) tt_r = (tt_r+254)%256; if (players[0].paddata[1] & PADRup) tt_r = (tt_r+2)%256; if (players[0].paddata[1] & PADL1) tt_attr = 0x40000000; if (players[0].paddata[1] & PADR1) tt_attr = 0x50000000; if (players[0].paddata[1] & PADL2) tt_attr = 0x60000000; if (players[0].paddata[1] & PADR2) tt_attr = 0x70000000; */ UpdateObjectCoordinates(&obj3d[screen3dobj]); DisplayObject3D(&obj3d[screen3dobj]); for (f=0;f<1;f++) { UpdateObjectCoordinates(&obj3d[studioobj[f]]); DisplayObject3D(&obj3d[studioobj[f]]); } print8(180,80,"GAME PAUSED"); print8(180,100," START CONTINUE"); print8(180,108,"SELECT QUIT"); // Pause section tie up frame GsSortOt(&TopOT[activebuff],&WorldOT[activebuff]); GsSortOt(&BotOT[activebuff],&WorldOT[activebuff]); DrawSync(0); GsDrawOt(&OverlayOT[activebuff]); DrawSync(0); FntPrint("Hsyncs:%d\n",hcnt); //print into stream // FntFlush(-1); hcnt = VSync(0); GsSwapDispBuff(); GsSortClear(0,0,0,&WorldOT[activebuff]); GsDrawOt(&WorldOT[activebuff]); activebuff = GsGetActiveBuff(); //Get buffer we can use GsSetWorkBase((PACKET *)GpuPacketArea[activebuff]); // Tell the system where the packet buffer is GsClearOt(0, 0, &WorldOT[activebuff]); // Clear the Ordering Table GsClearOt(0, OTPRI_SMOKE, &TopOT[activebuff]); // 3D above ground GsClearOt(0, OTPRI_GROUND+1, &BotOT[activebuff]); // 3D below ground GsClearOt(0, 0, &OverlayOT[activebuff]); // overlay (status, scores etc..) } if (game_paused == 0) if (pause_time>0) pause_time--; // printf("3d\n"); // 3D Objects for (f=0;f>2); backdr+=3; if (backlobj.rotate.vz>1600) showback = 0; } else if (backstyle == 1) { backx+=backdx; backdx+=12; if (backx>3000) showback = 0; } else if (backstyle == 2) { backlobj.rotate.vy -= backr; backrobj.rotate.vy += backr; backr+=(backdr>>2); backdr+=3; if (backlobj.rotate.vy<1024) showback = 0; } UpdateObjectCoordinates(&backlobj); UpdateObjectCoordinates(&backrobj); DisplayObject3D(&backlobj); DisplayObject3D(&backrobj); } // printf("Map\n"); // Move background track map camera if (numplayers == 1) { mapx = object[players[singleplayer].object].x-((SCREENWIDTH/2)<((SCREENWIDTH-32)<((SCREENHEIGHT-32)< object[players[pl1].object].chkpoint)) || (object[players[pl2].object].lap > object[players[pl1].object].lap) ) // if p2 is same lap and higher chkpoint, or higher lap { pltmp = pl2; pl2 = pl1; // make player 1 catch up pl1 = pltmp; } object[players[pl2].object].followactive = 1; object[players[pl2].object].followx = object[players[pl1].object].x; object[players[pl2].object].followy = object[players[pl1].object].y; object[players[pl2].object].angle = object[players[pl1].object].angle; object[players[pl2].object].control = followcontrol; object[players[pl2].object].chkpoint = object[players[pl1].object].chkpoint; // because he won't drive over them object[players[pl2].object].lap = object[players[pl1].object].lap; // has to be same lap as both on same screen } mapx = object[players[singleplayer].object].x+xdiff/2-((SCREENWIDTH/2)<>MYBITSHIFT; background[0].scrolly = mapy>>MYBITSHIFT; background[1].scrollx = background[0].scrollx; background[1].scrolly = background[0].scrolly; cameraview.vpx = cameraview.vrx = ( (mapx+((SCREENWIDTH/2)<>MYBITSHIFT )<<1; cameraview.vpy = cameraview.vry = ( (mapy+((SCREENHEIGHT/2)<>MYBITSHIFT )<<1; // Display the background GsSortFixBg16(&background[0], backgroundworkarea, &WorldOT[activebuff], OTPRI_GROUND); // if (trackinfo.type == 0) // GsSortFixBg16(&background[1], backgroundtopworkarea, &WorldOT[activebuff], OTPRI_OVERLAY); // Headlite test if (nighttime == 1) { for (f=0;f>MYBITSHIFT)/11.25)%32; headlight[f].rotate.vz = cang*128; headlight[f].aoff = cang*headlnprims; // offset into pre-calc table headlight[f].hox = (((veltabpt+cang)->dx*78)/256)*1.1; // pixel offset to centre of mapping box headlight[f].hoy = (((veltabpt+cang)->dy*78)/256)*1.1; headlight[f].origin.vx = cameraview.vpx+((((object[driver[f].object].x>>MYBITSHIFT)-(mapx>>MYBITSHIFT))-SCREENWIDTH/2+headlight[f].hox)*10); headlight[f].origin.vy = cameraview.vpy+((((object[driver[f].object].y>>MYBITSHIFT)-(mapy>>MYBITSHIFT))-SCREENHEIGHT/2+headlight[f].hoy)*10); } } // printf("Obj\n"); // Handle Objects ObjectHandler(); // printf("Chk\n"); // Show player status bars/times etc CheckPlayers(&WorldOT[activebuff]); // Show lap record flyer if (laprecordactive != 0) { laprecordsprite.x = laprecordx; laprecordsprite.y = laprecordy; GsSortSprite(&laprecordsprite,&OverlayOT[activebuff],0); laprecordx+=(laprecorddx-laprecordx)>>3; laprecordy+=(laprecorddy-laprecordy)>>3; laprecordactive--; if (laprecordactive == 50) { laprecorddx = 0; laprecorddy = -150; } } // Increase players laptimes if ( (racestatus == 1) && (game_paused == 0) ) { players[0].laptime+=2; players[1].laptime+=2; if (timetrial == 1) updateghostdata(); } // Do countdown if applicable if ( (countdownframe<5) && (game_paused == 0) ) { countdown.x = 96-SCREENWIDTH/2; countdown.y = 96-SCREENHEIGHT/2; countdown.r = 128; countdown.g = 128; countdown.b = 128; countdown.w = 14; countdown.h = 32; countdown.tpage = GetTPage(0,0,448,0); if (countdownframe>0) countdown.u = 14*(countdownframe-1); else countdown.u = 0; countdown.v = 16; countdown.cx = 448; countdown.cy = 254; countdown.attribute = 0x00000000; countdown.mx = countdown.w/2; countdown.my = countdown.h/2; countdown.scalex = 4096; countdown.scaley = 4096; countdown.rotate = 0; if (showback == 0) { // if (countdownframe>0) GsSortSprite(&countdown, &OverlayOT[activebuff], 0); countdownsize-=600; } if (countdownsize<600) { countdownframe++; // printf("count %d\n",countdownframe); countdownsize = 32737; if (countdownframe==4) { SsUtKeyOn(soundvab,5,0,62,0,120,120); racestatus = 1; // printf("Race has started\n"); if (MUSIC == 1) { SsSeqPlay(musicseq, SSPLAY_PLAY, SSPLAY_INFINITY); SsSetTempo(musicseq, 0, 204); } } else if (countdownframe>1 && countdownframe<4) SsUtKeyOn(soundvab,4,0,62,0,120,120); } } // printf("Status\n"); // Status screens OverlaySprite(-32,-SCREENHEIGHT/2,animspt+ANIM_OVERLAY,0); timetd(42-32,-SCREENHEIGHT/2+18,trackinfo.records.laprecord[selectedclass]); // GsSortOt(&TopOT[activebuff],&WorldOT[activebuff]); if (showback != 0) GsSortOt(&BackOT[activebuff],&OverlayOT[activebuff]); GsSortOt(&BotOT[activebuff],&WorldOT[activebuff]); // Debug information // if ( checkcollide(players[0].object,players[1].object,object[players[0].object].x,object[players[0].object].y) != 0) // FntPrint("Crash\n"); // FntPrint("Obj %d X%d Y%d Z%d\n",objediting,obj3d[objediting].origin.vx,obj3d[objediting].origin.vy,obj3d[objediting].origin.vz); // FntPrint("Screen %dX%d Y%d Z%d\n",screen3dobj,obj3d[screen3dobj].origin.vx,obj3d[screen3dobj].origin.vy,obj3d[screen3dobj].origin.vz); // FntPrint("Obj X%d Y%d Z%d\n",hlobj.origin.vx,hlobj.origin.vy,hlobj.origin.vz); // FntPrint("Camera X %d Y %d Z %d\n",cameraview.vpx,cameraview.vpy,cameraview.vpz); // FntPrint("Projection %d\n",projection); // FntPrint("RGB %d %d %d",tt_r,tt_g,tt_b); FntPrint("Hsyncs:%d\n",hcnt); //print into stream // FntPrint("Map %d,%d\n",mapx,mapy); // FntPrint("X:%d\n",object[1].x); // FntPrint("Scrolly:%d",background[0].scrolly); // FntFlush(-1); //flush all streams (displays text) // Finish frame if (nighttime == 1) { RECT r; // headlight background area GsBOXF bf; // screen dimmer u_short *hpt,drivern; DrawSync(0); // Wait for the previous frame to finish drawing bf.x = -160; bf.y = -128; bf.w = 320; bf.h = 256; bf.r = bf.g = bf.b = tt_r; bf.attribute = tt_attr; r.w = 256; r.h = 256; drivern = 0; // backup screen bits for (g=0;g>MYBITSHIFT)-(mapx>>MYBITSHIFT))+headlight[g].hox-128; // adjust to fit onto map r.y = ((object[driver[g].object].y>>MYBITSHIFT)-(mapy>>MYBITSHIFT))+headlight[g].hoy-128; r.y += (activebuff)*256; MoveImage(&r, 640+(drivern%2)*256,256+((drivern>>1)%2)*128); // copy screen to spare memory // FntPrint("%d,%d\n",640+(drivern%2)*128,256+((drivern>>1)%2)*128); // drivern++; } // Dim screen (draw dark version) DrawSync(0); GsClearOt(0, 0, &TopOT[activebuff]); // GsSortClear(0,0,0,&TopOT[activebuff]); // background[0].r = background[0].g = background[0].b = 16; // GsSortFixBg16(&background[1], backgroundtopworkarea, &TopOT[activebuff], 3); GsSortBoxFill(&bf,&TopOT[activebuff],62); // background[0].r = background[0].g = background[0].b = 128; GsDrawOt(&TopOT[activebuff]); DrawSync(0); // Overlay headlights drivern = 0; for (g=0;goffset)>>1; // move to primitive hpt+=2; // move to CBA V0 U0 *hpt = ((headlpcpt+headlight[g].aoff+f)->uv0); *hpt+=(((drivern>>1)%2)*128)<<8; hpt++; hpt++; // move to TSB V1 U1 *hpt = ((headlpcpt+headlight[g].aoff+f)->uv1); *hpt+=(((drivern>>1)%2)*128)<<8; hpt++; *hpt = (26+(drivern%2)*2)|0x0100; // Tpage + 15bit hpt++; *hpt = ((headlpcpt+headlight[g].aoff+f)->uv2); *hpt+=(((drivern>>1)%2)*128)<<8; } drivern++; DrawSync(0); GsClearOt(0, 0, &TopOT[activebuff]); UpdateHeadlightObjectCoordinates(&headlight[g]); DisplayHeadlightObject3D(&headlight[g]); GsDrawOt(&TopOT[activebuff]); DrawSync(0); } DrawSync(0); DisplayObjectOverlay(ghostdata.ghostobj,2); GsDrawOt(&OverlayOT[activebuff]); } else GsSortOt(&OverlayOT[activebuff],&WorldOT[activebuff]); // FntFlush(-1); DrawSync(0); hcnt = VSync(0); // Wait for the V-Blank period GsSwapDispBuff(); // Swap screens if (trackinfo.type == 1) // if 3D track { GsBOXF box; box.attribute = tt_attr; box.x = -32; box.y = -80; box.w = 64; box.h = 64; box.r = tt_r ; box.g = tt_g; box.b = tt_b; GsSortClear(0,0,0,&WorldOT[activebuff]); // GsSortBoxFill(&box,&WorldOT[activebuff],30); } GsDrawOt(&WorldOT[activebuff]); // Start drawing the OT } // printf("RACEMAIN QUIT\n"); SsUtAllKeyOff(0); SsSeqStop(musicseq); backlobj.origin.vx = -2870; backlobj.origin.vy = -1974; backrobj.origin.vx = 2894; backrobj.origin.vy = -1974; backlobj.rotate.vy = 2048; backrobj.rotate.vy = 2048; backlobj.rotate.vz = 0; backrobj.rotate.vz = 0; } void testcontrol(u_long obj) { playerdata *player; u_short stdpad; player = &(players[object[obj].extra->playerindex]); stdpad = player->paddata[1]; // get standard pad data if (stdpad & PADLright) object[obj].x+=(1<60000) object[obj].frame = (31<<8); } if (stdpad & PADRright) { object[obj].frame+=(1<<8); if (object[obj].frame>(31<<8)) object[obj].frame = 0; } } void testcollision() { int f,g,QUIT_FLAG; u_long obj,obj1,obj2; GsBOXF box; // Initialize OT for (f=0;f<2;f++) { WorldOT[f].length = OT_LENGTH; // Very important this, don't forget it WorldOT[f].org = OTTags[f]; // it initialises the OT } // Set up text area FntLoad(960,256); // Load standard 256x128 font pattern to the frame buffer at coords specified FntOpen(16-SCREENWIDTH/2,16-SCREENHEIGHT/2,256,200,0,512); // Setup a text window. x,y,w,h,isbg,n where x,y is coords in frame buffer // w,h are width and height. isbg is 0 for background clear/1 no clear, n = number of letters // Put sprite graphics in frame buffer Initializeframes(); // Clear all objects InitializeObjects(); obj=spawn(); newplayer(obj); object[obj].extra->controller = 0; object[obj].control = testcontrol; object[obj].extra->playerindex = 0; obj1 = obj; obj=spawn(); newplayer(obj); object[obj].extra->controller = 1; object[obj].control = testcontrol; object[obj].extra->playerindex = 1; object[obj].x+=(16<offset,object[obj1].frame>>8); FntPrint("X:%d,Y:%d\n",object[obj1].x>>MYBITSHIFT,object[obj1].y>>MYBITSHIFT); FntFlush(-1); DrawSync(0); // Wait for the previous frame to finish drawing VSync(0); // Wait for the V-Blank period GsSwapDispBuff(); // Swap screens GsSortClear(60,120,120,&WorldOT[activebuff]); GsDrawOt(&WorldOT[activebuff]); // Start drawing the OT } } void updateghostdata() { // updates ghost data, including replay data (CALLED EVERY FRAME) short entry; u_long obj,dx,dy; if (ghostdata.active == 0) return; obj = ghostdata.obj; dx = (object[obj].x>>MYBITSHIFT)-(ghostdata.cucurrent.x); dy = (object[obj].y>>MYBITSHIFT)-(ghostdata.cucurrent.y); ghostdata.cucurrent.x = object[obj].x>>MYBITSHIFT; ghostdata.cucurrent.y = object[obj].y>>MYBITSHIFT; entry = ((dx+16)&31)|(((dy+16)&31)<<5)|((long)((object[obj].angle>>MYBITSHIFT)/11.25)<<10); ghostdata.lap[ghostdata.current][ghostdata.index] = entry; entry = ghostdata.lap[ghostdata.out][ghostdata.index]; // get output entry if (entry < 0) // if end of lap ghostdata.outdisp = 0; if (ghostdata.outdisp==1) // if not at end of lap { ghostdata.x+=(entry&31)-16; ghostdata.y+=((entry>>5)&31)-16; ghostdata.angle = (entry>>10)&31; } ghostdata.index++; if (ghostdata.index>=(MAXGHOSTENTRIES-2)) ghostdata.index = MAXGHOSTENTRIES-2; } void initializeghostdata() { ghostdata.current = 0; ghostdata.previous = 1; ghostdata.best = 2; ghostdata.active = 0; } void startghostdata(u_long obj) { // re-starts ghost data on input object obj // makes output the previous or best lap short tmp; ghostdata.active = 0; // temporarily disable ghostdata.lap[ghostdata.current][ghostdata.index] = -1; // siginify end of lap ghostdata.index = 0; // restart time ghostdata.obj = obj; // input object ghostdata.stprevious.x = ghostdata.stcurrent.x; ghostdata.stprevious.y = ghostdata.stcurrent.y; ghostdata.cucurrent.x = object[obj].x>>MYBITSHIFT; // current & start coordinates ghostdata.cucurrent.y = object[obj].y>>MYBITSHIFT; ghostdata.stcurrent.x = ghostdata.cucurrent.x; ghostdata.stcurrent.y = ghostdata.cucurrent.y; tmp = ghostdata.current; // swap current and previous ghostdata.current = ghostdata.previous; ghostdata.previous = tmp; { ghostdata.out = ghostdata.best; // new output ghostdata.x = ghostdata.stbest.x; // output start coordinates ghostdata.y = ghostdata.stbest.y; } // { // ghostdata.out = ghostdata.previous; // new output // ghostdata.x = ghostdata.stprevious.x; // output start coordinates // ghostdata.y = ghostdata.stprevious.y; // } ghostdata.outdisp = 1; ghostdata.active = 1; // start it up again } void ghostdatabestlap() { // copies current to best short f; short p,b,l; ghostdata.lap[ghostdata.current][ghostdata.index] = -1; // siginify end of lap b = ghostdata.best; p = ghostdata.current; l = (MAXGHOSTENTRIES-1); for (f=0;fpaddata[1] gives standard pad readout (if dest is type playerdata) u_long *pt; u_char tmp; if (pad == 0) pt = (u_long *) bb0; else if (pad == 1) pt = (u_long *) bb1; *((u_long *)dest) = *pt; // 1st 4 bytes *(((u_long *)dest)+1) = *(pt+1); // 2nd 4 bytes *(((u_short *)dest)+1)=~(*(((u_short *)dest)+1)); // invert std controller tmp = *(((u_char *)dest)+2); *(((u_char *)dest)+2) = *(((u_char *)dest)+3); *(((u_char *)dest)+3) = tmp; } /* Controller state reading */ static u_long PadRead(void) { return(~(*(bb0+3) | *(bb0+2) << 8 | *(bb1+3) << 16 | *(bb1+2) << 24)); } void create3dobject(u_long obj,int index) { u_long *testpt; testpt = (u_long *) getpakaddress(index); obj3d[obj].origin.vx = 664; obj3d[obj].origin.vy = 646; obj3d[obj].origin.vz = 160; obj3d[obj].rotate.vx = 0; obj3d[obj].rotate.vy = 0; obj3d[obj].rotate.vz = 0; GsInitCoordinate2(WORLD, &(obj3d[obj].coord)); obj3d[obj].handler.coord2 = &(obj3d[obj].coord); testpt++; // skip header ID GsMapModelingData(testpt); testpt+=2; // skip past rest of header GsLinkObject4( (u_long)testpt, &(obj3d[obj].handler), 0); obj3d[obj].handler.attribute = 0x00000000; obj3d[obj].active = 1; obj3d[obj].ot = 1; // below track UpdateObjectCoordinates(&obj3d[obj]); } void UpdateObjectCoordinates(object3d *objpt) { MATRIX tmp; tmp.t[0] = objpt->origin.vx; tmp.t[1] = objpt->origin.vy; tmp.t[2] = objpt->origin.vz; RotMatrix(&(objpt->rotate), &tmp); objpt->coord.coord = tmp; objpt->coord.flg = 0; } void DisplayObject3D(object3d *objpt) { MATRIX tmp; GsGetLs(&(objpt->coord), &tmp); GsSetLightMatrix(&tmp); GsSetLsMatrix(&tmp); // GsSortObject4( &(objpt->handler), &WorldOT[activebuff], 14-OT_LENGTH, getScratchAddr(0)); if (objpt->ot == 0) GsSortObject4( &(objpt->handler), &TopOT[activebuff], 14-TOPOT_LENGTH, getScratchAddr(0)); else if (objpt->ot == 1) GsSortObject4( &(objpt->handler), &BotOT[activebuff], 14-BOTOT_LENGTH, getScratchAddr(0)); else if (objpt->ot == 2) GsSortObject4( &(objpt->handler), &BackOT[activebuff], 14-BACKOT_LENGTH, getScratchAddr(0)); } void UpdateHeadlightObjectCoordinates(headlight3D *objpt) { MATRIX tmp; tmp.t[0] = objpt->origin.vx; tmp.t[1] = objpt->origin.vy; tmp.t[2] = objpt->origin.vz; RotMatrix(&(objpt->rotate), &tmp); objpt->coord.coord = tmp; objpt->coord.flg = 0; } void DisplayHeadlightObject3D(headlight3D *objpt) { MATRIX tmp; GsGetLs(&(objpt->coord), &tmp); GsSetLightMatrix(&tmp); GsSetLsMatrix(&tmp); if (objpt->ot == 0) GsSortObject4( &(objpt->handler), &TopOT[activebuff], 14-TOPOT_LENGTH, getScratchAddr(0)); else GsSortObject4( &(objpt->handler), &BotOT[activebuff], 14-BOTOT_LENGTH, getScratchAddr(0)); } void nullcontrol(u_long obj) { } void playerexplode(u_long obj) { if (object[obj].frame < 1792) { object[obj].frame += 64; } else { players[object[obj].extra->playerindex].active = 0; kill (obj); } } void hurt(u_long obj, long damage) { //Give damage to object object[obj].health -= damage; if (object[obj].class == 1) players[object[obj].extra->playerindex].fuel -= 16<= (10<<8) ) kill(obj); } void makeexplosion(long x,long y) { u_long obj; obj = spawn(); object[obj].x = x; object[obj].y = y; object[obj].class = 0; object[obj].pri = OTPRI_SMOKE; object[obj].base_sprite = animspt + ANIM_EXPLO1; object[obj].frame = 0; object[obj].control = explosioncontrol; object[obj].hx = 8; object[obj].hy = 8; object[obj].maxx = 16; object[obj].maxy = 16; boom(); } void dustcontrol(u_long obj) { object[obj].frame+=8; object[obj].r-=2; object[obj].g=object[obj].r>>1; if (object[obj].r<=4) kill(obj); object[obj].x+=object[obj].dx; object[obj].y+=object[obj].dy; } void makedust(long x,long y,u_long owner) { u_long obj; long ang,dx,dy,rdx,rdy; ang = (object[owner].angle>>MYBITSHIFT)/11.25; dx = ((veltabpt+((ang+8)%32))->dx)<<1; dy = ((veltabpt+((ang+8)%32))->dy)<<1; rdx = ((veltabpt+((ang+16)%32))->dx)<<2; rdy = ((veltabpt+((ang+16)%32))->dy)<<2; obj = spawn(); object[obj].x = x+dx+rdx; object[obj].y = y+dy+rdy; object[obj].dx = dx>>3; object[obj].dy = dy>>3; object[obj].class = 4; object[obj].r = 140; object[obj].g = 80; object[obj].b = 0; object[obj].pri = OTPRI_MINES; object[obj].base_sprite = animspt + ANIM_DUSTCLOUD; object[obj].frame = (((x+y)&3)<<1)<<8; object[obj].control = dustcontrol; object[obj].hx = 8; object[obj].hy = 8; obj = spawn(); object[obj].x = x-dx+rdx; object[obj].y = y-dy+rdy; object[obj].dx = -dx>>3; object[obj].dy = -dy>>3; object[obj].class = 4; object[obj].r = 140; object[obj].g = 80; object[obj].b = 0; object[obj].pri = OTPRI_MINES; object[obj].base_sprite = animspt + ANIM_DUSTCLOUD; object[obj].frame = 0;//+((x+y)&1)<<8; object[obj].control = dustcontrol; object[obj].hx = 8; object[obj].hy = 8; } void smokecontrol(u_long obj) { // object[obj].frame += 16; object[obj].bright-=1; object[obj].x += object[obj].dx>>2; object[obj].y += object[obj].dy>>2; if (object[obj].bright <= 64 ) { kill(obj); } } void makesmoke(long x,long y,u_long obj) { u_long shot; long ang; shot = spawn(); makeshot(shot,x,y,obj,OTPRI_SMOKE); object[shot].class = 128+INV_SMOKESCREEN; object[shot].base_sprite = animspt + ANIM_SMOKE; object[shot].frame = 0; object[shot].control = smokecontrol; object[shot].dx = 0; object[shot].dy = 0; ang = (object[obj].angle>>MYBITSHIFT)/11.25; shot = spawn(); makeshot(shot,x,y,obj,OTPRI_SMOKE); object[shot].class = 128+INV_SMOKESCREEN; object[shot].base_sprite = animspt + ANIM_SMOKE; object[shot].frame = 0; object[shot].control = smokecontrol; object[shot].dx = ((veltabpt+((ang+8)%32))->dx); object[shot].dy = ((veltabpt+((ang+8)%32))->dy); shot = spawn(); makeshot(shot,x,y,obj,OTPRI_SMOKE); object[shot].class = 128+INV_SMOKESCREEN; object[shot].base_sprite = animspt + ANIM_SMOKE; object[shot].frame = 0; object[shot].control = smokecontrol; object[shot].dx = ((veltabpt+((ang-8)&31))->dx); object[shot].dy = ((veltabpt+((ang-8)&31))->dy); } void minecontrol(u_long obj) { int f; u_long dobj; if (object[obj].time > (FPS*120) ) // die after a short time { object[object[obj].owner].minecount--; kill(obj); return; } if (object[obj].time < 2) return; // some time before it activates for (f=0;f (FPS*120) ) // die after a short time { kill(obj); } if (object[obj].time < 1) return; // some time before it activates for (f=0;f>MYBITSHIFT)/8 + ((object[obj].y>>MYBITSHIFT)/8)*(mapwidth/8)); if ( (col&3) != 0) { makeexplosion(object[obj].x,object[obj].y); object[object[obj].owner].missilecount--; kill(obj); return; } // if (object[obj].time > 3) // { // object[obj].time = 0; // dobj = spawn(); // makeshot(dobj,object[obj].x,object[obj].y,obj,OTPRI_SMOKE); // object[dobj].class = 128+INV_SMOKESCREEN; // object[dobj].base_sprite = animspt + ANIM_SMOKE; // object[dobj].frame = 0; // object[dobj].control = smokecontrol; // object[dobj].dx = 0; // object[dobj].dy = 0; // } object[obj].frame+=32; if (object[obj].frame>=(3<<8)) object[obj].frame=1<<8; object[obj].x+=object[obj].dx; object[obj].y+=object[obj].dy; if ( (object[obj].x<0) || (object[obj].x>(mapwidth<(mapheight<25) ) ) /// make sure not hit owner during first few frames if ( checkcollide16(obj,driver[f].object,object[obj].x,object[obj].y) != 0) { dobj = driver[f].object; hurt(dobj,25); // object[dobj].dx = (object[dobj].x-object[obj].x)<<2; // make other recoil in explosion // object[dobj].dy = (object[dobj].y-object[obj].y)<<2; // object[dobj].momdx = object[dobj].dx; // object[dobj].momdy = object[dobj].momdy; object[dobj].dx = object[obj].dx<<3; object[dobj].dy = object[obj].dy<<3; object[dobj].momdx = object[obj].dx<<2; object[dobj].momdy = object[obj].dy<<2; object[dobj].speed/=3; object[dobj].spin = 20; makeexplosion(object[obj].x,object[obj].y); object[object[obj].owner].missilecount--; kill(obj); return; } mapoffset = ((object[obj].x>>MYBITSHIFT)>>3) + ((object[obj].y>>MYBITSHIFT)>>3)*(mapwidth>>3); // check if hit wall col = *(colmappt + mapoffset); if ( (col&3) != 0) { makeexplosion(object[obj].x,object[obj].y); object[object[obj].owner].missilecount--; kill(obj); return; } if (object[obj].time > FPS*16) // die after a while { makeexplosion(object[obj].x,object[obj].y); object[object[obj].owner].missilecount--; kill(obj); return; } // if ((object[obj].time&3) == 0) // { // object[obj].time = 0; // dobj = spawn(); // makeshot(dobj,object[obj].x,object[obj].y,obj,OTPRI_SMOKE); // object[dobj].class = 128+INV_SMOKESCREEN; // object[dobj].base_sprite = animspt + ANIM_SMOKE; // object[dobj].frame = 0; // object[dobj].control = smokecontrol; // object[dobj].dx = 0; // object[dobj].dy = 0; // } // rotate missile chk = *(chkmappt+ ((object[obj].x>>MYBITSHIFT)>>4) + ((object[obj].y>>MYBITSHIFT)>>4)*(mapwidth>>4) ); if (chk != 0) object[obj].lastchkpoint = chk; // last checkpoint visited dir = (*(cpudirmappt + mapoffset)); if ( (dir&64) != 0) // if ideal dir is valid { if (object[obj].lastchkpoint&1 == 1) dir = dir&7; // odd cpu dir else dir = (dir>>3)&7; // even cpu dir idealangle = (dir*45)<object[obj].angle) object[obj].angle+=2048; else if (idealangleobject[obj].angle) object[obj].angle-=2048; else if (idealangle=92160) object[obj].angle-=92160; } // move missile forward frameang = (object[obj].angle>>MYBITSHIFT)/11.25; object[obj].speed = 360; object[obj].dx = (((veltabpt+frameang)->dx)*object[obj].speed)>>6; object[obj].dy = (((veltabpt+frameang)->dy)*object[obj].speed)>>6; // animate object[obj].frame+=32; if (object[obj].frame>=(3<<8)) object[obj].frame=1<<8; // update object[obj].x+=object[obj].dx; object[obj].y+=object[obj].dy; if ( (object[obj].x<0) || (object[obj].x>(mapwidth<(mapheight<playerindex].inventory[trigger] == 0) // if no ammo return; x = object[obj].x; y = object[obj].y; // angle = ((long)((object[obj].angle>>MYBITSHIFT)/11.25)+16)%32; angle = (long )((object[obj].angle>>MYBITSHIFT)/11.25); dx = ((veltabpt+angle)->dx); dy = ((veltabpt+angle)->dy); // get coordinates at rear of car if (object[obj].shottime == 0) { switch (trigger) { case INV_CANNON: { // firecannon(x+dx*16,y+dy*16,obj); players[object[obj].extra->playerindex].inventory[trigger]-=1; break; } case INV_SMOKESCREEN: { players[object[obj].extra->playerindex].inventory[trigger]-=1; makesmoke(x-dx*16,y-dy*16,obj); object[obj].shottime = 3; break; } case INV_OIL: { players[object[obj].extra->playerindex].inventory[trigger]-=1; dropoil(x-dx*14,y-dy*14,obj); object[obj].shottime = 4; break; } case INV_MISSILE: { if (object[obj].missilecount < MAXMISSILECOUNT) { players[object[obj].extra->playerindex].inventory[trigger]-=1; firemissile(x+dx*4,y+dy*4,dx*8,dy*8,obj); object[obj].shottime = FPS/2; object[obj].missilecount++; } break; } case INV_IMISSILE: { if (object[obj].missilecount < MAXMISSILECOUNT) { players[object[obj].extra->playerindex].inventory[trigger]-=1; fireimissile(x+dx*4,y+dy*4,dx*4,dy*4,obj); object[obj].shottime = FPS/2; object[obj].missilecount++; } break; } case INV_MINE: { if (object[obj].minecount < MAXMINECOUNT) { players[object[obj].extra->playerindex].inventory[trigger]-=1; dropmine(x-dx*14,y-dy*14,obj); object[obj].shottime = FPS/4; object[obj].minecount++; } break; } case INV_TURBO: { players[object[obj].extra->playerindex].inventory[trigger]-=1; object[obj].effects = object[obj].effects | 2; if ( (players[object[obj].extra->playerindex].inventory[trigger] & 7) > 5) makesmoke(x-dx*16,y-dy*16,obj); break; } } } } void followcontrol(u_long obj) { // player control when being zipped to another player long dx,dy,collide,newx,newy,frameang,frameadj; int f; dx=(object[obj].followx-object[obj].x)>>3; dy=(object[obj].followy-object[obj].y)>>3; object[obj].x+=dx; object[obj].y+=dy; frameang = (object[obj].angle>>MYBITSHIFT)/11.25; // angle of car (0-32 as 0-360) frameadj = frameang; if (abs(dx) < 300 && abs(dy) < 300) // if reached destination { collide = 0; newx = object[obj].x; newy = object[obj].y; for (f=0;fplayerindex].fuel-=512; } void playercontrol(u_long obj) { // Player control function called from object u_short stdpad; u_long shot,dobj; int frameang,frameangperp; int f; long frameadj; long newx,newy,oldangle; u_long offset; u_char col,chk; short usespeed; playerdata *player; player = &(players[object[obj].extra->playerindex]); if (object[obj].shottime > 0) object[obj].shottime--; newx = object[obj].x + (object[obj].dx>>4); newy = object[obj].y + (object[obj].dy>>4); oldangle = object[obj].angle; object[obj].angle = object[obj].angle + (object[obj].dangle); if (object[obj].angle<0) object[obj].angle+=360<=(360<>MYBITSHIFT)/11.25; // angle of car (0-32 as 0-360) object[obj].frame = frameang<<8; // check if going to hit another car for (f=0;f= 0 && object[dobj].dy >= 0) // if both dy in same direction (+ve) if (object[obj].y > object[dobj].y) // if obj ahead { object[obj].dy = (object[obj].dy+16)*1.5; // object in front gets boost object[dobj].dy = 0; // object behind suffers object[dobj].speed/=2; } else { object[dobj].dy = (object[dobj].dy+16)*1.5; object[obj].dy = 0; // object behind suffers object[obj].speed/=2; } if (object[obj].dy < 0 && object[dobj].dy < 0) // if both dy in same direction (-ve) if (object[obj].y < object[dobj].y) // if obj ahead { object[obj].dy = (object[obj].dy-16)*1.5; object[dobj].dy = 0; // object behind suffers object[dobj].speed/=2; } else { object[dobj].dy = (object[dobj].dy-16)*1.5; object[obj].dy = 0; // object behind suffers object[obj].speed/=2; } if (object[obj].dx >= 0 && object[dobj].dx >= 0) // if both dx in same direction (+ve) if (object[obj].x > object[dobj].x) // if obj ahead { object[obj].dx = (object[obj].dx+16)*1.5; object[dobj].dx = 0; // object behind suffers object[dobj].speed/=2; } else { object[dobj].dx = (object[dobj].dx+16)*1.5; object[obj].dx = 0; // object behind suffers object[obj].speed/=2; } if (object[obj].dx < 0 && object[dobj].dx < 0) // if both dx in same direction (-ve) if (object[obj].x < object[dobj].x) // if obj ahead { object[obj].dx = (object[obj].dx-16)*1.5; object[dobj].dx = 0; // object behind suffers object[dobj].speed/=2; } else { object[dobj].dx = (object[dobj].dx-16)*1.5; object[obj].dx = 0; // object behind suffers object[obj].speed/=2; } // FntPrint("BANG %d\n",obj); prang(); newx = object[obj].x; // put car back to old coords and angle newy = object[obj].y; object[obj].angle = oldangle; } } // check if going to hit wall offset = (newx>>MYBITSHIFT)/8 + ((newy>>MYBITSHIFT)/8)*(mapwidth/8); // get collision data under car's next coordinates col = *(colmappt + offset); col = checktrackcollision16(obj,newx,newy); if (col!=0) { // FntPrint("Col:%d %d,%d = %d,%d\n",col,newx,newy,(newx>>MYBITSHIFT)/8,((newy>>MYBITSHIFT)/8)*(mapwidth/8) ); if ( (col&3) != 0 ) // if hit something solid { newx = object[obj].x; // put car back newy = object[obj].y; object[obj].angle = oldangle; object[obj].momdx/= 2; // reduce momentum object[obj].momdy/= 2; object[obj].speed/=1.5; } if ( (col&1) != 0) // hit horiz wall { object[obj].dy = -object[obj].dy; object[obj].momdy = -object[obj].momdy; } if ( (col&2) != 0) // hit vert wall { object[obj].dx = -object[obj].dx; object[obj].momdx = -object[obj].momdx; } } object[obj].x = newx; object[obj].y = newy; /* if (object[obj].health<=0) { object[obj].dead_flag = DEAD_DYING; object[obj].base_sprite = animspt + 4; object[obj].frame = 0; object[obj].dx = 0; object[obj].dy = 0; object[obj].health = 0; object[obj].control = playerexplode; } */ chk = *(chkmappt+ ((object[obj].x>>MYBITSHIFT)>>4) + ((object[obj].y>>MYBITSHIFT)>>4)*(mapwidth>>4) ); if (chk != 0) object[obj].lastchkpoint = chk; // last checkpoint visited if (chk == object[obj].chkpoint+1) { object[obj].chkpoint = chk; // update checkpoint number if new checkpoint checkchangeposition(obj); if (chk == 1) // if first lap starts { player->laptime = 0; if (timetrial == 1) startghostdata(obj); } } if ( (chk == 1) && (object[obj].chkpoint == trackinfo.numcheckpoints) ) // Done a lap { object[obj].lap++; if (object[obj].lap > trackinfo.numlaps) lapsfinished = 1; object[obj].chkpoint = 1; checkchangeposition(obj); player->lastlap = player->laptime; player->laptime = 0; if (player->lastlapbestlap) player->bestlap = player->lastlap; if (player->lastlaplastlap; laprecord(player); } if (timetrial == 1) { if (player->bestlap == player->lastlap) // if was best lap ghostdatabestlap(); // copy ghost data startghostdata(obj); object[ghostdata.ghostobj].health = 1; if (ghostdata.out == ghostdata.previous) // if showing previous lap ghostdata.laptime = player->lastlap; else if (ghostdata.out == ghostdata.best) // if showing best lap ghostdata.laptime = player->bestlap; } } stdpad = player->paddata[1]; // get standard pad data if (player->fuel == 0) // if out of fuel, ignore pad { stdpad = 0; object[obj].lap = 0; object[obj].chkpoint = 1; checkchangeposition(obj); } if (racestatus == 0) stdpad = 0; // if race not running then ignore pad object[obj].dangle = 0; if (stdpad & PADLleft) { object[obj].dangle=-4< 0) object[obj].steer -= 1; if (object[obj].steer > 0) object[obj].steer -= 1; } if (object[obj].spin>0) { object[obj].dangle += 32<trigger[0],obj); if (stdpad & PADR1) playerfire(player->trigger[1],obj); if (stdpad & PADL2) playerfire(player->trigger[2],obj); if (stdpad & PADR2) playerfire(player->trigger[3],obj); } frameang = (object[obj].angle>>MYBITSHIFT)/11.25; // angle of car (0-32 as 0-360) frameangperp = (frameang + 8)%32; // perpendicular angle frameadj = frameang; if ( (object[obj].effects&1) ) { frameang = (object[obj].prevangle>>MYBITSHIFT)/11.25; stdpad = stdpad & ( PADRdown^0xffff ); // stop from accelerating } else object[obj].prevangle = object[obj].angle; object[obj].maxspeed = player->maxspeed; //228 358 if (object[obj].effects & 2) // If TURBO pressed { object[obj].maxspeed*=1.3; object[obj].effects = object[obj].effects & 0xfd; } player->fuel-=abs(object[obj].speed)/30; // if (stdpad & PADRleft) // if SQUARE pressed // (object[obj].maxspeed = 228); // maxspeed is higher (high gear) offset = ((object[obj].y>>MYBITSHIFT)/8)*(mapwidth/8); offset = offset + ((object[obj].x>>MYBITSHIFT)/8); col = *(colmappt + offset); if ( (col&16) != 0 ) // if bit4 of col map (slow track) { object[obj].maxspeed=28; // reduce maximum speed if (object[obj].speed > 45) { if ( (object[obj].time & 2) == 0 ) makedust(object[obj].x,object[obj].y,obj); } else if (object[obj].speed > 25) { if ( (object[obj].time & 4) == 0 ) makedust(object[obj].x,object[obj].y,obj); } } else if ( (col&32) != 0 ) // if bit5 (speed up) { object[obj].maxspeed*=1.3; if (object[obj].speed > 0) object[obj].speed = object[obj].maxspeed; } object[obj].maxspeed-=object[obj].steer; if ( (stdpad & PADRdown) && (!(stdpad & PADRleft)) && (!(stdpad & PADRright)) ) // accelerate { if (object[obj].speed < object[obj].maxspeed) object[obj].speed+=2; else if (object[obj].speed>1) object[obj].speed-=2; // decrease speed if in low gear } else // decelerate { if (~(object[obj].effects&1)) // if not on oil if (object[obj].speed>0) object[obj].speed-=1; else if (object[obj].speed<0) object[obj].speed+=1; if ( abs(object[obj].momdx) < 40 ) // if momentum is small, make it none. object[obj].momdx = 0; if ( abs(object[obj].momdy) < 40 ) object[obj].momdy = 0; if (stdpad & PADRleft) // brake { object[obj].speed-=2; // frameadj+=32; object[obj].momdx-=object[obj].momdx/32; object[obj].momdy-=object[obj].momdy/32; //16 } if (object[obj].speed<-80) object[obj].speed = -80; } object[obj].dx = (((veltabpt+frameang)->dx)*object[obj].speed)>>6; object[obj].dy = (((veltabpt+frameang)->dy)*object[obj].speed)>>6; if (stdpad & PADRright) { object[obj].dx/=8; object[obj].dy/=8; if (object[obj].speed>1) object[obj].speed-=2; } if (object[obj].voice[0] == -1) // engine sound object[obj].voice[0] = SsUtKeyOn(soundvab,2,0,52,0,80,80); else SsUtChangePitch(object[obj].voice[0],soundvab,2,52,0,52+(object[obj].speed>>4),(object[obj].speed&15)<<3); if (object[obj].voice[1] == -1) // skid sound object[obj].voice[1] = SsUtKeyOn(soundvab,3,0,52,0,0,0); else SsUtSetVVol(object[obj].voice[1],object[obj].steer<<1,object[obj].steer<<1); object[obj].momdx+=object[obj].dx; object[obj].momdy+=object[obj].dy; // FntPrint("ddx %d ddy %d\n",object[obj].momdx-object[obj].dx,object[obj].momdy-object[obj].dy); object[obj].momdx-=object[obj].momdx/25; // 30 object[obj].momdy-=object[obj].momdy/25; object[obj].dx += object[obj].momdx; object[obj].dy += object[obj].momdy; // FntPrint("MOMDX %d DY %d\n",object[obj].momdx,(object[obj].dy>>4)>>MYBITSHIFT); // limitrange(object[obj].dx,-1024,1024); // limitrange(object[obj].dy,-1024,1024); if (player->fuel<=0) { // object[obj].control = gameoverplayercontrol; player->fuel=0; } object[obj].frame = 0 + (frameadj<<8); object[obj].effects = object[obj].effects&0xfe; // clear oil effects } void gameoverplayercontrol(u_long obj) { } void cpuplayercontrol(u_long obj) { u_long offset,dobj; long idealangle; u_short frameang; u_char dir,chk,col,dircmp; long newx,newy; int f; if (racestatus == 0) { frameang = ((object[obj].angle>>MYBITSHIFT)/11.25); object[obj].frame = frameang<<8; return; } newx = object[obj].x + (object[obj].dx>>4); newy = object[obj].y + (object[obj].dy>>4); // FntPrint("CPU:%d X:%d Y:%d Nx:%d Ny:%d\n",obj,object[obj].x,object[obj].y,newx,newy); object[obj].steer = 0; for (f=0;f= 0 && object[dobj].dy >= 0) // if both dy in same direction (+ve) if (object[obj].y > object[dobj].y) // if obj ahead { object[obj].dy = (object[obj].dy+16)*1.5; // object in front gets boost object[dobj].dy = 0; // object behind suffers object[dobj].speed/=2; } else { object[dobj].dy = (object[dobj].dy+16)*1.5; // object in front gets boost object[obj].dy = 0; // object behind suffers object[obj].speed/=2; } if (object[obj].dy < 0 && object[dobj].dy < 0) // if both dy in same direction (-ve) if (object[obj].y < object[dobj].y) // if obj ahead { object[obj].dy = (object[obj].dy-16)*1.5; // object in front gets boost object[dobj].dy = 0; // object behind suffers object[dobj].speed/=2; } else { object[dobj].dy = (object[dobj].dy-16)*1.5; // object in front gets boost object[obj].dy = 0; // object behind suffers object[obj].speed/=2; } if (object[obj].dx >= 0 && object[dobj].dx >= 0) // if both dx in same direction (+ve) if (object[obj].x > object[dobj].x) // if obj ahead { object[obj].dx = (object[obj].dx+16)*1.5; // object in front gets boost object[dobj].dx = 0; // object behind suffers object[dobj].speed/=2; } else { object[dobj].dx = (object[dobj].dx+16)*1.5; // object in front gets boost object[obj].dx = 0; // object behind suffers object[obj].speed/=2; } if (object[obj].dx < 0 && object[dobj].dx < 0) // if both dx in same direction (-ve) if (object[obj].x < object[dobj].x) // if obj ahead { object[obj].dx = (object[obj].dx-16)*1.5; // object in front gets boost object[dobj].dx = 0; // object behind suffers object[dobj].speed/=2; } else { object[dobj].dx = (object[dobj].dx-16)*1.5; // object in front gets boost object[obj].dx = 0; // object behind suffers object[obj].speed/=2; } // prang(); } } // FntPrint("%d Steer %d\n",obj,object[obj].steer); // check if going to hit wall offset = (newx>>MYBITSHIFT)/8 + ((newy>>MYBITSHIFT)/8)*(mapwidth/8); // get collision data under car's next coordinates col = *(colmappt + offset); if (col!=0) { if ( (col&3) != 0 ) // if hit something solid { newx = object[obj].x; // put car back newy = object[obj].y; object[obj].momdx/= 2; // reduce momentum object[obj].momdy/= 2; } if ( (col&1) != 0) // hit horiz wall { object[obj].dy = -object[obj].dy; object[obj].momdy = -object[obj].momdy; } if ( (col&2) != 0) // hit vert wall { object[obj].dx = -object[obj].dx; object[obj].momdx = -object[obj].momdx; } if ( (col&16) != 0) // if on dusty road (slow road) if (object[obj].speed > 45) { if ( (object[obj].time & 2) == 0 ) makedust(object[obj].x,object[obj].y,obj); } else if (object[obj].speed > 25) { if ( (object[obj].time & 4) == 0 ) makedust(object[obj].x,object[obj].y,obj); } } object[obj].x = newx; object[obj].y = newy; limitrange(object[obj].x,16<>MYBITSHIFT)>>4) + ((object[obj].y>>MYBITSHIFT)>>4)*(mapwidth>>4) ); if (chk != 0) object[obj].lastchkpoint = chk; // last checkpoint visited if (chk == object[obj].chkpoint+1) { object[obj].chkpoint = chk; // update checkpoint number if new checkpoint checkchangeposition(obj); } if ( (chk == 1) && (object[obj].chkpoint == trackinfo.numcheckpoints) ) // Done a lap { object[obj].lap++; object[obj].chkpoint = 1; checkchangeposition(obj); } offset = ((object[obj].x>>MYBITSHIFT)>>3) + ((object[obj].y>>MYBITSHIFT)>>3)*(mapwidth>>3); dir = (*(cpudirmappt + offset)); dircmp = 64; // if (object[obj].lastchkpoint&1 == 0) // if even, check against even valid // dircmp = 128; if ( (dir&dircmp) != 0) // if ideal dir is valid (i.e. car should change direction) { if (object[obj].lastchkpoint&1 == 1) dir = dir&7; // odd cpu dir else dir = (dir>>3)&7; // even cpu dir idealangle = (dir*45)<object[obj].angle) object[obj].angle+=12<object[obj].angle) object[obj].angle-=12<0) { object[obj].angle+=32<=(360< object[obj].maxspeed) object[obj].speed = object[obj].maxspeed; frameang = (object[obj].angle>>MYBITSHIFT)/11.25; // angle of car (0-32 as 0-360) object[obj].dx = (((veltabpt+frameang)->dx)*object[obj].speed)>>6; object[obj].dy = (((veltabpt+frameang)->dy)*object[obj].speed)>>6; object[obj].momdx+=object[obj].dx; object[obj].momdy+=object[obj].dy; object[obj].momdx-=object[obj].momdx/20; object[obj].momdy-=object[obj].momdy/20; object[obj].dx += object[obj].momdx; object[obj].dy += object[obj].momdy; // limitrange(object[obj].dx,-8192,8192); // limitrange(object[obj].dy,-8192,8192); object[obj].frame = frameang<<8; object[obj].effects = object[obj].effects&0xfe; // clear oil effects } void startgridcontrol(u_long obj) { } void shotcontrol(u_long obj) { object[obj].x += object[obj].dx; object[obj].y += object[obj].dy; if (object[obj].y<0 | object[obj].y>240<320<offset)+(object[obj].frame>>8)-0)->offset)/2; // get to mask data x = (nx>>MYBITSHIFT)-object[obj].hx; y = (ny>>MYBITSHIFT)-object[obj].hy; tmskip = y-((y>>4)<<4); // (track mask skip) lines to skip in track mask (how far down the object is in the top block) mrot = x-((x>>4)<<4); // (mask rotate) bits to rotate object mask right by index16 = (x>>4)+(y>>4)*(mapwidth>>4); // top left map block (object usually crosses 4 different map blocks) index8 = (x>>3)+(y>>3)*(mapwidth>>3); // top left col block col = checktrackcollision16col(index8); // check for collision on colmap if (col != 0) // if so, { // FntPrint("COL TL "); if (checktrackcollisionmask16(index16,mrot,pt,tmskip,16-tmskip) != 0) // check at pixel level // { // FntPrint("PIXEL\n"); return(col); // and return if hit // } // else // FntPrint("No\n"); } index16++; // top right map block if (mrot != 0) // don't do right if car wholly aligned horizontally on 16x16 block { col = checktrackcollision16col(index8+2); if (col != 0) { // FntPrint("COL TR "); if (checktrackcollisionmask16r(index16,16-mrot,pt,tmskip,16-tmskip) != 0) // check at pixel level // { // FntPrint("PIXEL\n"); return(col); // } // else // FntPrint("No\n"); } } if (tmskip == 0) // don't do bottom if car wholly aligned vertically on 16x16 block return(0); index16+=(mapwidth>>4)-1; // bottom left map block index8+=(mapwidth>>3); pt+=tmskip; // skip down object mask to bit in bottom block col = checktrackcollision16col(index8); if (col != 0) { // FntPrint("COL BL tm%d ",tmskip); if (checktrackcollisionmask16(index16,mrot,pt+16-tmskip,0,tmskip) != 0) // check at pixel level // { // FntPrint("PIXEL\n"); return(col); // } // else // FntPrint("No\n"); } index16++; // bottom right map block if (mrot != 0) // don't do right if car wholly aligned horizontally on 16x16 block { col = checktrackcollision16col(index8+2); if (col != 0) { // FntPrint("COL BR "); if (checktrackcollisionmask16r(index16,16-mrot,pt+16-tmskip,0,tmskip) != 0) // check at pixel level // { // FntPrint("PIXEL\n"); return(col); // } // else // FntPrint("No\n"); } } return(0); } u_char checktrackcollision16col(short index8) { // checks all 4 colmap blocks in a map block. returns 0 if none, otherwise a normal value (1-3) u_char col; col = (*(colmappt + index8))&3; // FntPrint("0"); if (col != 0) // if something solid return(col); // then quit col = (*(colmappt + index8 + 1))&3; // otherwise do next col block // FntPrint("1"); if (col != 0) return(col); index8+=(mapwidth>>3); col = (*(colmappt + index8))&3; // FntPrint("2"); if (col != 0) return(col); col = (*(colmappt + index8 + 1))&3; // FntPrint("3"); if (col != 0) return(col); return(0); } int checktrackcollisionmask16(short index16,short mrot,u_short *pt,short tmskip,int height) { // pt is pointer into mask data for object, tmskip is amount to skip into map mask (vertically) // mrot is right shift for object mask, index16 is index of map block // height is number of lines to do u_short *mpt,mi; // pointer to tile mask GsCELL *cpt; int f; // sort this bit out (dirty address obtaining) mpt = (u_short *)memoryblock[trackinfo.mpimblock].address; mpt+=2; mi = *(mpt+index16); // get index into .CEL mpt = trackinfo.maskaddress; //(u_short *)getpakaddress(_PAK_engtrackbitmask_INDEX); cpt = (GsCELL *) (( (u_char *)memoryblock[trackinfo.celmblock].address ) + 4 ); cpt+=mi; mpt+= (((cpt->u)>>4) + ((cpt->v)>>4)*16)*16; // printf("Addr:%x u:%d v:%d mi:%d tmskip:%d height:%d\n",mpt,cpt->u,cpt->v,mi,tmskip,height); mpt += tmskip; /* { // DEBUG section GsBOXF b; int bf,bg,by; b.attribute = 0; b.w = 4; b.h = 4; b.r = 120; b.g = 120; b.b = 120; by = tmskip; for (bg=0;bg>(15-bf))&1 != 0) { b.attribute = 0; b.r = b.g = b.b = 128; b.x = bf*4; b.y = (bg+by)*4; GsSortBoxFill(&b,&WorldOT[activebuff],0); } else { b.attribute = 1<<30; b.r = 80; b.g = b.b = 0; b.x = bf*4; b.y = (bg+by)*4; GsSortBoxFill(&b,&WorldOT[activebuff],0); } if ( (((*(pt+bg))>>mrot)>>(15-bf))&1 != 0) { b.attribute = 0; b.r = b.g = b.b = 128; b.x = bf*4; b.y = (bg+by)*4-80; GsSortBoxFill(&b,&WorldOT[activebuff],0); } else { b.attribute = 1<<30; b.r = 80; b.g = b.b = 0; b.x = bf*4; b.y = (bg+by)*4-80; GsSortBoxFill(&b,&WorldOT[activebuff],0); } } } */ for (f=0;f>mrot); if ( (*mpt & ((*pt)>>mrot)) != 0 ) return(1); pt++; mpt++; } return(0); } int checktrackcollisionmask16r(short index16,short mrot,u_short *pt,short tmskip,int height) { // pt is pointer into mask data for object, tmskip is amount to skip into map mask (vertically) // mrot is right shift for object mask, index16 is index of map block // same as previous function, but rotates car mask left (for right hand side) u_short *mpt,mi; // pointer to tile mask GsCELL *cpt; int f; // sort this bit out mpt = (u_short *)memoryblock[trackinfo.mpimblock].address; mpt+=2; mi = *(mpt+index16); // get index into .CEL mpt = trackinfo.maskaddress; //(u_short *)getpakaddress(_PAK_engtrackbitmask_INDEX); cpt = (GsCELL *) (( (u_char *)memoryblock[trackinfo.celmblock].address ) + 4 ); cpt+=mi; mpt+= (((cpt->u)>>4) + ((cpt->v)>>4)*16)*16; mpt += tmskip; for (f=0;f 0) return; pos = object[obj].lap*trackinfo.numcheckpoints + object[obj].chkpoint - 1; currpos = -1; for (f=0;f (object[obj2].lap*trackinfo.numcheckpoints + object[obj2].chkpoint - 1) ) { for (g=currpos;g>f;g-=1) // shift down from new pos to old pos carpositions[g] = carpositions[g-1]; carpositions[f] = object[obj].driverindex; break; } } } void CheckPlayers(GsOT *wot) { // Check Credits and players starting int f,g; short x0; GsGLINE line; u_long obj; if (timetrial == 1 && object[ghostdata.ghostobj].health > 0) time8(16,220,ghostdata.laptime); for (f=0;f<2;f++) { if (players[f].active != 0) { // FntPrint("Player %d CHK:%d LAP:%d \n",f+1, object[players[f].object].chkpoint, object[players[f].object].lap); if (players[f].laptime < 300 && object[players[f].object].lap>0) { time8(f*232+16,0,players[f].lastlap); // FntPrint("%d:%d\n",players[f].lastlap/100,players[f].lastlap%100); } else { time8(f*232+16,0,players[f].laptime); // FntPrint("%d:%d\n",players[f].laptime/100,players[f].laptime%100); } print8(f*232+16,8,"LAP"); int8(f*232+48,8,2,object[players[f].object].lap); if (timetrial == 0) { if (players[f].laptime > 0) // if running, for (g=0;g= MAXMISSILECOUNT ) { char8spritefast.r = 128; char8spritefast.g = char8spritefast.b = 0; } int8(f*232+16,16,3,players[f].inventory[players[f].trigger[2]]); // display ammo counters int8(f*232+56,16,3,players[f].inventory[players[f].trigger[3]]); char8spritefast.r = char8spritefast.g = char8spritefast.b = 128; int8(f*232+16,24,3,players[f].inventory[players[f].trigger[0]]); if ( players[f].trigger[1] == INV_MINE ) if ( object[players[f].object].minecount >= MAXMINECOUNT ) { char8spritefast.r = 128; char8spritefast.g = char8spritefast.b = 0; } int8(f*232+56,24,3,players[f].inventory[players[f].trigger[1]]); char8spritefast.r = char8spritefast.g = char8spritefast.b = 128; int8(f*232+16,32,4,players[f].fuel>>MYBITSHIFT); ammospr.u = players[f].trigger[2]*8; // display ammo images relevent for trigger selection ammospr.x = f*232+6-SCREENWIDTH/2; ammospr.y = 24-SCREENHEIGHT/2; GsSortFastSprite(&ammospr,&OverlayOT[activebuff],0); ammospr.u = players[f].trigger[3]*8; ammospr.x = f*232+46-SCREENWIDTH/2; GsSortFastSprite(&ammospr,&OverlayOT[activebuff],0); ammospr.u = players[f].trigger[0]*8; ammospr.x = f*232+6-SCREENWIDTH/2; ammospr.y = 32-SCREENHEIGHT/2; GsSortFastSprite(&ammospr,&OverlayOT[activebuff],0); ammospr.u = players[f].trigger[1]*8; ammospr.x = f*232+46-SCREENWIDTH/2; GsSortFastSprite(&ammospr,&OverlayOT[activebuff],0); } // FntPrint("BEST LAP: %d:%d\n",players[f].bestlap/100,players[f].bestlap%100); /* x0 = (f*160 + 16)-SCREENWIDTH/2; line.attribute = 0x40000000; line.x0 = x0; line.x1 = x0 + (object[players[f].object].speed); line.r0 = 128; line.r1 = 100; line.g0 = 80; line.g1 = 128; line.b0 = 0; line.b1 = 0; for (g=0;g<16;g++) { line.y0 = 200+g-SCREENHEIGHT/2; line.y1 = 200+g-SCREENHEIGHT/2; // GsSortGLine(&line, wot, 0); } */ } } } void makeshot(u_long shot,long x, long y, u_long owner, u_short pri) { // makes a standard shot with default vars object[shot].x = x; object[shot].y = y; object[shot].dx = 0; object[shot].dy = 0; object[shot].base_sprite = animspt + ANIM_SHOTS; object[shot].frame = 0; object[shot].hx = 8; object[shot].hy = 8; object[shot].maxx = 16; object[shot].maxy = 16; object[shot].control = shotcontrol; object[shot].owner = owner; object[shot].pri = pri; } void FrametoGs(singleframe *frame,GsSPRITE *sprite) { // Copies data from single frame to GsSprite structure sprite->attribute = frame->attribute; sprite->w = frame->w; sprite->h = frame->h; sprite->tpage = frame->tpage; sprite->u = frame->u; sprite->v = frame->v; sprite->cx = frame->cx; sprite->cy = frame->cy; } void ObjectHandler() { u_long f; object[0].time++; for (f=1;f>MYBITSHIFT)-object[obj1].hx; x2=(object[obj2].x>>MYBITSHIFT)-object[obj2].hx; y1=(ny>>MYBITSHIFT)-object[obj1].hy; y2=(object[obj2].y>>MYBITSHIFT)-object[obj2].hy; if (x1<=x2) // if obj1 is on left { if (x2<(x1+object[obj1].maxx)) // if x collision { if (y1y2) // if obj1 is below { if (y1<(y2+object[obj2].maxy)) collide = 1; } else // y1==y2 collide = 1; } } else if (x1>x2) { if (x1<(x2+object[obj2].maxx)) { if (y1y2) // if obj1 is below { if (y1<(y2+object[obj2].maxy)) collide = 1; } else // y1==y2 collide = 1; } } return(collide); } int checkmask16(u_long lobj, u_long robj, int shift, u_short skip1, u_short skip2, u_short height) { // checks left obj and right obj. skip is y offset to skip, height is height left to do. int f,hit; u_short *lpt,*rpt; lpt = maskspt + ((maskheaderpt+(object[lobj].base_sprite->offset)+(object[lobj].frame>>8)-0)->offset)/2; // get to mask data rpt = maskspt + ((maskheaderpt+(object[robj].base_sprite->offset)+(object[robj].frame>>8)-0)->offset)/2; // div by 2 because it adds in shorts lpt+=skip1; rpt+=skip2; // cdebuglpt = lpt; // cdebugrpt = rpt; hit = 0; // hasn't collided yet // if (cdebug == 1) // printf("Checkmask16 l %d on r %d\n",lobj,robj); for (f=0;f>shift) ) != 0 ) hit = 1; // if (cdebug == 1) // { // printf("F:%d %x,%x shft:%d sk1:%d,sk2:%d\n",f,*lpt,*rpt,shift,skip1,skip2); // } lpt++; rpt++; } return(hit); } int checkcollide16(u_long obj1, u_long obj2, long nx, long ny) { int collide; long x1,y1,x2,y2; collide = 0; x1=(nx>>MYBITSHIFT)-object[obj1].hx; x2=(object[obj2].x>>MYBITSHIFT)-object[obj2].hx; y1=(ny>>MYBITSHIFT)-object[obj1].hy; y2=(object[obj2].y>>MYBITSHIFT)-object[obj2].hy; if (x1<=x2) // if obj1 is on left { if (x2<(x1+16)) // if x collision { if (y1y2) // else if obj1 is below { if (y1<(y2+16)) if (checkmask16(obj1,obj2,x2-x1,0,y1-y2,16-(y1-y2)) != 0) collide = 1; } else // else if y1==y2 if (checkmask16(obj1,obj2,x2-x1,0,0,16) != 0) collide = 1; } } else if (x1>x2) // else if obj1 is on right { if (x1<(x2+16)) { if (y1y2) // else if obj1 is below { if (y1<(y2+16)) if (checkmask16(obj2,obj1,x1-x2,y1-y2,0,16-(y1-y2)) != 0) collide = 1; } else // else if y1==y2 if (checkmask16(obj2,obj1,x1-x2,0,0,16) != 0) collide = 1; } } return(collide); } void kill(u_long obj) { // kills object & extra object attached object[obj].active = 0; if (object[obj].extra != 0) (object[obj].extra)->active = 0; } void kill3d(u_long obj) { // kills 3d object obj3d[obj].active = 0; } u_long spawn3d() {// spawn new object3d int f; u_long n; n=0; for (f=1;foffset) + (object[obj].frame>>8)), &sprite); // copy frames data to GsSPRITE structure sprite.x = ((object[obj].x>>MYBITSHIFT)-(mapx>>MYBITSHIFT))-SCREENWIDTH/2; // adjust to fit onto map sprite.y = ((object[obj].y>>MYBITSHIFT)-(mapy>>MYBITSHIFT))-SCREENHEIGHT/2; if (object[obj].class == 3) // if ghost car { sprite.attribute = 0x40000000; if (object[obj].health == 0) return; } if (object[obj].class == 4) // if dustcloud { sprite.attribute = 0x70000000; sprite.r = object[obj].r; sprite.g = object[obj].g; sprite.b = object[obj].b; } else sprite.r = sprite.g = sprite.b = object[obj].bright; if (object[obj].spritetype == 0) fast = 0; if (fast != 0) { sprite.x-= object[obj].hx; sprite.y-= object[obj].hy; // handle for fast sprite } if ( (object[obj].class >= 1) && (object[obj].class <= 3) ) // if player, cpu car or ghost car { sprite.cx = object[obj].cx; sprite.cy = object[obj].cy; if (object[obj].followactive != 0) sprite.r = sprite.g = sprite.b = 64; if (nighttime == 1) ov = 1; } if (object[obj].pri >= (1<>MYBITSHIFT)*4096; GsSortSprite(&sprite, &WorldOT[activebuff], object[obj].pri); } else // fast sprite draw { GsSortFastSprite(&sprite, &WorldOT[activebuff], object[obj].pri); } } else { if (fast == 0) // rotation/scaling sprite draw { sprite.mx = object[obj].hx; sprite.my = object[obj].hy; sprite.scalex = 4096; sprite.scaley = 4096; sprite.rotate = (object[obj].angle>>MYBITSHIFT)*4096; GsSortSprite(&sprite, &OverlayOT[activebuff], 2); } else // fast sprite draw { GsSortFastSprite(&sprite, &OverlayOT[activebuff], 2); } } } void OverlaySprite(long x, long y,animation *base_sprite,long frame) { // display sprite on overlay OT GsSPRITE sprite; FrametoGs(framespt+((base_sprite->offset) + (frame>>8)), &sprite); // copy frames data to GsSPRITE structure sprite.x = x; sprite.y = y; sprite.r = sprite.g = sprite.b = 128; GsSortFastSprite(&sprite, &OverlayOT[activebuff], 0); } void DisplayObjectOverlay(u_long obj, int pri) { // Display object obj in overlay OT at priority pri GsSPRITE sprite; int fast=1; // 1 = use fast sprite, 0 = use normal sprite FrametoGs(framespt+((object[obj].base_sprite->offset) + (object[obj].frame>>8)), &sprite); // copy frames data to GsSPRITE structure sprite.x = ((object[obj].x>>MYBITSHIFT)-(mapx>>MYBITSHIFT))-SCREENWIDTH/2; // adjust to fit onto map sprite.y = ((object[obj].y>>MYBITSHIFT)-(mapy>>MYBITSHIFT))-SCREENHEIGHT/2; if (object[obj].class == 3) // if ghost car { sprite.attribute = 0x40000000; if (object[obj].health == 0) return; } if (object[obj].spritetype == 0) fast = 0; if (fast != 0) { sprite.x-= object[obj].hx; sprite.y-= object[obj].hy; // handle for fast sprite } if ( (object[obj].class >= 1) && (object[obj].class <= 3) ) // if player, cpu car or ghost car { sprite.cx = object[obj].cx; sprite.cy = object[obj].cy; } sprite.r = sprite.g = sprite.b = object[obj].bright; if (object[obj].pri >= (1<>MYBITSHIFT)*4096; GsSortSprite(&sprite, &WorldOT[activebuff], object[obj].pri); } else // fast sprite draw { GsSortFastSprite(&sprite, &OverlayOT[activebuff], pri); } } void resetdrivers() { int f; for (f=0;f>2)); } void newcpuplayer(u_long obj) { u_long eobj; newplayer(obj); // set up a new player object[obj].control = cpuplayercontrol; // and override the significant parts object[obj].maxspeed = 210+rand()%20; object[obj].class = 2; object[obj].lap = 0; object[obj].cx = 576+16; object[obj].cy = 242; } void newplayer(u_long obj) { u_long eobj; int f; object[obj].x = 160<celmblock; if (mb != 0) freememory(mb); mb = th->mpimblock; if (mb != 0) freememory(mb); mb = th->celtopmblock; if (mb != 0) freememory(mb); mb = th->mpitopmblock; if (mb != 0) freememory(mb); mb = th->colmblock; if (mb != 0) freememory(mb); } void Initializebackground(trackinfoheader *th) { // Unpack background data and set up background array. int f,g; void *celladdress,*mpiaddress,*coladdress; void *celltopaddress,*mpitopaddress; u_short ctpage; int mblock; GsCELL *cpt,*fcpt; // First, unpack all the track data and set pointers to that data // ground track mblock=allocatememory(getpaklength(th->celindex)); // get memory for cells if (mblock == 0) printf("CRASH: no memory for cells\n"); th->celmblock = mblock; celladdress = memoryblock[mblock].address; unpak(th->celindex, memoryblock[mblock].address); mblock=allocatememory(getpaklength(th->mpiindex)); // get memory for index (mpi) if (mblock == 0) printf("CRASH: no memory for map index table\n"); th->mpimblock = mblock; mpiaddress = memoryblock[mblock].address; unpak(th->mpiindex, mpiaddress); // overlay track mblock=allocatememory(getpaklength(th->celtopindex)); // get memory for cells if (mblock == 0) printf("CRASH: no memory for overlay cells\n"); th->celtopmblock = mblock; celltopaddress = memoryblock[mblock].address; unpak(th->celtopindex, memoryblock[mblock].address); mblock=allocatememory(getpaklength(th->mpitopindex)); // get memory for index (mpi) if (mblock == 0) printf("CRASH: no memory for overlay map index table\n"); th->mpitopmblock = mblock; mpitopaddress = memoryblock[mblock].address; unpak(th->mpitopindex, mpitopaddress); // other track data mblock=allocatememory(getpaklength(th->colindex)); // get memory for colmap if (mblock == 0) printf("CRASH: no memory for track col map\n"); th->colmblock = mblock; coladdress=memoryblock[mblock].address; unpak(th->colindex, coladdress); // Initialize background structures for (f=0;f<2;f++) { background[f].attribute = 0x41000000; // 8 bit CLUT background[f].x = 0; background[f].y = 0; background[f].w = SCREENWIDTH; background[f].h = SCREENHEIGHT; background[f].scrollx = 0; background[f].scrolly = 0; background[f].r = 128; background[f].g = 128; background[f].b = 128; background[f].map = &backmap[f]; // pointer to MAP header background[f].mx = SCREENWIDTH/2; background[f].my = SCREENHEIGHT/2; background[f].scalex = 4096; background[f].scaley = 4096; background[f].rotate = 0; } // Set up ground map address info and Tpages cpt = (GsCELL *)( ((u_long *)celladdress + 1) ); // get address of first cell in map background[0].map->base = cpt; background[0].map->index = (u_short *)( ((u_long *)mpiaddress + 1) ); for (f=0;f<*((u_long *)celladdress);f++) // for each cell { ctpage = cpt->tpage; ctpage = 0; cpt->cba = (768/16)|((240+ctpage)<<6); cpt->tpage = GetTPage(1,0,768+ctpage*128,0); // recalculate tpages cpt++; } // Set up overlay map address info and Tpages cpt = (GsCELL *)( ((u_long *)celltopaddress + 1) ); // get address of first cell in map background[1].map->base = cpt; background[1].map->index = (u_short *)( ((u_long *)mpitopaddress + 1) ); for (f=0;f<*((u_long *)celltopaddress);f++) // for each cell { ctpage = cpt->tpage; cpt->cba = (768/16)|((240+ctpage)<<6); cpt->tpage = GetTPage(1,0,768+ctpage*128,0); // recalculate tpages cpt++; } // Setup sizes for GsMAP structures for (f=0;f<2;f++) { background[f].map->cellw = 16; background[f].map->cellh = 16; background[f].map->ncellw = *((u_short *) mpiaddress); background[f].map->ncellh = *(((u_short *) mpiaddress)+1); } // Set up some global variables mapwidth = background[0].map->ncellw *16; mapheight = background[0].map->ncellh *16; mapx = 0; mapy = 0; colmappt = (u_char *) coladdress; // pointer to col map colmappt+=2; // skip size info chkmappt=colmappt+(background[0].map->ncellw*2 * background[0].map->ncellh*2); // pointer to cpu chkpoint map cpudirmappt=chkmappt+(background[0].map->ncellw*background[0].map->ncellh); // pointer to cpu dir map // Calculate total number of checkpoints th->numcheckpoints = 0; for (f=0;f<(background[0].map->ncellw*background[0].map->ncellh);f++) if ( *(chkmappt + f) > th->numcheckpoints ) th->numcheckpoints = *(chkmappt + f); // calculate total number of cpu checkpoints } void InitializeTrack(trackinfoheader *th) { // Loads track graphics into frame buffer int mblock; u_long *laddress; mblock=allocatememory(getpaklength(th->tim1index)); // allocate memory for track pixel data if (mblock == 0) printf("CRASH: no memory for track1.tim\n"); laddress = (u_long *)memoryblock[mblock].address; unpak(th->tim1index, (void *)laddress); TransferTextures(laddress+1); // Copy track to framebuffer. +1 means skip header (4bytes). freememory(mblock); mblock=allocatememory(getpaklength(th->tim2index)); // allocate memory for track pixel data if (mblock == 0) printf("CRASH: no memory for track2.tim\n"); laddress = (u_long *)memoryblock[mblock].address; unpak(th->tim2index, (void *)laddress); TransferTextures(laddress+1); // Copy track to framebuffer. +1 means skip header (4bytes). freememory(mblock); } void getgridnfo(int index, trackinfoheader *thpt) { // copy grid information from track .NFO file to thpt int mblock,f; tracknfofile *nfopt; nfopt = (tracknfofile *)getpakaddress(index); for (f=0;f<4;f++) { thpt->grid[f].x = nfopt->grid[f].x; thpt->grid[f].y = nfopt->grid[f].y; thpt->grid[f].angle = nfopt->grid[f].angle; } } void Initializeframes() { //Copy frames/pixel data from system memory to frame buffer for the animation frames u_long f; u_long offset; u_long *laddress; int mblock; singleframe *framept; u_long numberofframes; RECT recttrans; // Copy animation and frames information // printf("Check frames loaded\n"); if (frames_loaded == 0) // unless already loaded { // printf("Loading frames\n"); frames_loaded = 1; mblock=allocatememory(getpaklength(_PAK_frames_INDEX)); // allocate memory for frames if (mblock == 0) printf("CRASH: no memory for animation frames\n"); laddress = (u_long *)memoryblock[mblock].address; // pointer to frames memory address unpak(_PAK_frames_INDEX,(void *)laddress); // unpack frames data numberofframes = *(laddress+2); // get number of frames in the file (we use this later) framespt = (singleframe *)(laddress+4); // start of single frames is skip 4 u_long header animspt = (animation *)( (u_char *)laddress + (*(laddress+1)) ); // value of 2nd u_long in frames memory is offset to animations framept = framespt; // for (f=0;f<4;f++) // printf("Anim %d, offset %d length %d\n",f,*(animspt+f),*((u_long *)(animspt+f) +1)); // Assign Correct Texture page and Clut data to frame data // N.B. TIM X and Y positions are dependent on this routine // This bit is total RUBBISH. SORT IT OUT NOW!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! (well, next time) for (f=0;fattribute = 0; if (framept->tpage<2) { framept->cx = 576+framept->tpage*16; framept->cy = 240; framept->tpage = GetTPage(0,0,512+framept->tpage*64,0); } else { framept->tpage = GetTPage(0,0,448,0); framept->cx = 448; framept->cy = 255; } // if (f>(numberofframes-14)) // printf("%d CX=%d CY=%d TP=%d\n",f,framept->cx,framept->cy,framept->tpage); framept++; } // Do Masks mblock=allocatememory(getpaklength(_PAK_masks_INDEX)); if (mblock == 0) printf("CRASH: no memory for masks\n"); laddress = (u_long *)memoryblock[mblock].address; unpak(_PAK_masks_INDEX,(void *)laddress); laddress++; maskspt = (u_short *)(laddress-1); // point to start of headers // maskspt+=( *(laddress)*sizeof(maskheader) )/2; // and point past header to start of data laddress++; // skip to start of headers maskheaderpt = (maskheader *)laddress; // set up pointer to mask headers } // Copy frames sprite pixel data // printf("Sprite pixel"); mblock=allocatememory(getpaklength(_PAK_cartim_INDEX)); // allocate memory for car sprite pixel data if (mblock == 0) printf("CRASH: no memory for car.tim\n"); laddress = (u_long *)memoryblock[mblock].address; unpak(_PAK_cartim_INDEX, (void *)laddress); TransferTextures(laddress+1); // Copy car to framebuffer. +1 means skip header (4bytes). freememory(mblock); // We don't need this in main memory anymore // Copy extra sprite pixel data // printf("Extra pixel"); mblock=allocatememory(getpaklength(_PAK_extratim_INDEX)); // allocate memory for extra frames if (mblock == 0) printf("CRASH: no memory for extra frames\n"); laddress = (u_long *)memoryblock[mblock].address; // pointer to frames memory address unpak(_PAK_extratim_INDEX,(void *)laddress); // unpack frames data TransferTextures(laddress+1); // Warning: This memory block is used for the car colours as well. // Copy car colours // unpak(_PAK_carcolours_INDEX, (void *)laddress); // unpack car colours to same area // recttrans.x = 576; // recttrans.y = 241; // recttrans.w = 64; // recttrans.h = 3; // LoadImage (&recttrans, laddress); // Transfer colours to frame buffer unpak(_PAK_ghostcolours_INDEX, (void *)laddress); // unpak ghost car colours recttrans.x = 624; recttrans.y = 240; recttrans.w = 16; recttrans.h = 1; LoadImage (&recttrans, laddress); // Transfer ghost colours freememory(mblock); // We don't need this in main memory anymore // printf("init frames out\n"); } void TransferTIM(int index) { // Unpacks TIM from pak and copies to framebuffer int mblock; u_long *addr; mblock = allocatememory(getpaklength(index)); // printf("Transfer TIM block %d, index %d\n",mblock,index); if (mblock == 0) printf("\nCRASH no memory for TIM\n"); addr = (u_long *)memoryblock[mblock].address; unpak(index,(void *)addr); TransferTextures(addr+1); freememory(mblock); } void TransferTextures(u_long *tim) { // Give me // (address of TIM data(not including 4 byte header) GsIMAGE imgtrans; RECT recttrans; GsGetTimInfo(tim, &imgtrans); // Set up the IMAGE struct from the TIM data recttrans.x = imgtrans.px; // pixel data destination coords recttrans.y = imgtrans.py; recttrans.w = imgtrans.pw; // pixel data size recttrans.h = imgtrans.ph; // printf("rt.x:%d, rt.y:%d, rt.w:%d, rt.h:%d\n",recttrans.x,recttrans.y,recttrans.w,recttrans.h); LoadImage (&recttrans, imgtrans.pixel); // Transfer it to the frame buffer recttrans.x = imgtrans.cx; //CLUT data dest coords recttrans.y = imgtrans.cy; recttrans.w = imgtrans.cw; //CLUT data size recttrans.h = imgtrans.ch; // printf("Clut x:%d y:%d\n",recttrans.x,recttrans.y); LoadImage (&recttrans, imgtrans.clut); // Transfer palette DrawSync(0); // Wait for it to finish transferring } void ResetPlayer(int p) { // Resets player to initial state int f; players[p].active = 0; players[p].score = 0; players[p].health = 0; players[p].carclut = carcolours[p]; for (f=0;flength; // printf("Length %d\n",length); return(length); } u_long getpakaddress(int index) { // returns address of file in PAK (used to get address of uncompressed files) u_long length,*pakheadpt,version,hsize; entryheader *headpt; pakheadpt = (u_long *)( ((u_char *)PAKFILEADDR) + _PAKHEAD); // pointer to pak headers version = *pakheadpt; hsize = *(pakheadpt+1); headpt = (entryheader *)(pakheadpt+2); headpt+=index; // skip to file header we want return((u_long)PAKFILEADDR+headpt->offset); } void unpak(int index, void *dest) { // unpacks index (#number of file in pak) to destination void *source; u_long length,*pakheadpt,version,hsize; entryheader *headpt; pakheadpt = (u_long *)( ((u_char *)PAKFILEADDR) + _PAKHEAD); // pointer to pak headers version = *pakheadpt; hsize = *(pakheadpt+1); headpt = (entryheader *)(pakheadpt+2); headpt+=index; // skip to file header we want source = (void *)( ((u_char *)PAKFILEADDR) + headpt->offset ); length = headpt->length; if (headpt->method == 0) printf("DOH: trying to unpack a stored file\n"); else if (headpt->method == 1) unpakRLE(source, dest, length); } void unpakRLE(void *source, void *dest, u_long length) { // Unpacks from address source to dest. length is total size after unpacking u_char *src,*dst; u_char rlec,tmp; u_long count; int f; src = (u_char *) source; dst = (u_char *) dest; // printf("Unpack %d,%d,%d,%d,%d\n",*src,*(src+1),*(src+2),*(src+3),*(src+4)); // printf("From %x to %x\n",src,dst); count = 0; while (count127) // if single byte { tmp = *src; // read data src++; for (f=0;f<(256-rlec)+1;f++) { *dst=tmp; // write it many times dst++; } count+=(256-rlec)+1; } else // if multiple bytes { for (f=0;f96) ) // if invalid character return; c-=33; char8sprite.u = (c%32)<<3; // position on font texture char8sprite.v = (c>>5)<<3; char8sprite.x = x-160+4; // 3D makes origin in center of screen char8sprite.y = y-120+4; // +4 because of mx,my origin char8sprite.scalex = scalex; char8sprite.scaley = scaley; char8sprite.rotate = rotate; GsSortSprite(&char8sprite,&OverlayOT[activebuff],0); } void char8fast(short x,short y, char c) { // prints character c from font 8 at pixels x,y if (c==32) return; c-=33; char8spritefast.u = (c%32)<<3; // position on font texture char8spritefast.v = (c>>5)<<3; char8spritefast.x = x-160; char8spritefast.y = y-120; GsSortFastSprite(&char8spritefast,&OverlayOT[activebuff],0); } void print8(short x,short y, char *text) { // prints text at x,y (null terminated) char c; c = *text; while (c!=0) { char8fast(x,y,c); text++; x+=8; c=*text; } } void int8(short x,short y,short d, int num) { // prints number with d digits at x,y char n[16]; if (d>14) { d=14; printf("Too many digits to int8\n"); } n[d] = 0; while (d>0) { n[d-1]=(num%10)+48; // char8fast(x,y,n[d-1]); d--; num/=10; // x+=8; } print8(x,y,n); } void time8(short x,short y,u_long time) { // prints time at x,y in format m:ss:hh int s,m; s = time/100; m = s/60; timechar[0]=48+(m%10); // minutes s-=m*60; timechar[2]=48+(s/10); // seconds timechar[3]=48+(s%10); timechar[5]=48+((time/10)%10); // hundredths timechar[6]=48+(time%10); print8(x,y,timechar); } void timetd(short x,short y,u_long time) { // prints time using thind digits (4x6) in m:ss:hh int s,m,tc; s = time/100; m = s/60; tdsprite.x = x; tdsprite.y = y; tc=(m%10); // minutes tdsprite.u = tc<<2; GsSortFastSprite(&tdsprite,&OverlayOT[activebuff],0); tdsprite.x+=4; tdsprite.u = 40; // : GsSortFastSprite(&tdsprite,&OverlayOT[activebuff],0); tdsprite.x+=4; s-=m*60; tc=(s/10); // seconds tdsprite.u = tc<<2; GsSortFastSprite(&tdsprite,&OverlayOT[activebuff],0); tdsprite.x+=4; tc=(s%10); tdsprite.u = tc<<2; GsSortFastSprite(&tdsprite,&OverlayOT[activebuff],0); tdsprite.x+=4; tdsprite.u = 40; // : GsSortFastSprite(&tdsprite,&OverlayOT[activebuff],0); tdsprite.x+=4; tc=((time/10)%10); // hundredths tdsprite.u = tc<<2; GsSortFastSprite(&tdsprite,&OverlayOT[activebuff],0); tdsprite.x+=4; tc=(time%10); tdsprite.u = tc<<2; GsSortFastSprite(&tdsprite,&OverlayOT[activebuff],0); tdsprite.x+=4; } void updatefontcolour() { // creates clut address in char sprites from global fontcolour if (fontcolour>0) { char8sprite.cx=((fontcolour%4)<<4)+640; char8sprite.cy=(fontcolour>>2)+241; char8spritefast.cx=((fontcolour%4)<<4)+640; char8spritefast.cy=(fontcolour>>2)+241; } else { char8sprite.cx = char8spritefast.cx = 640; char8sprite.cy = char8spritefast.cy = 240; } } void initsound() { int mblockvh,mblockvb,prog,tone; char *atrpt,*pt; // Sound FX mblockvh = allocatememory(getpaklength(_PAK_soundvh_INDEX)); if (mblockvh == 0) printf("CRASH: no memory for sound VH"); mblockvb = allocatememory(getpaklength(_PAK_soundvb_INDEX)); if (mblockvb == 0) printf("CRASH: no memory for sound VB"); unpak(_PAK_soundvh_INDEX, (void *)memoryblock[mblockvh].address); vhptr = (void *)memoryblock[mblockvh].address; unpak(_PAK_soundvb_INDEX, (void *)memoryblock[mblockvb].address); soundvab = SsVabTransfer( (u_char *)memoryblock[mblockvh].address, (u_char *)memoryblock[mblockvb].address, -1, 1); if (soundvab<0) printf("CRASH: COULD NOT TRANSFER SOUND\n"); // else // printf("Sound VAB on %d\n",soundvab); // freememory(mblockvh); // don't free otherwise sound won't work freememory(mblockvb); // Sound Fix Hack for (prog=0;prog<15;prog++) { for (tone=0;tone<5;tone++) { atrpt = (char *)vhptr; atrpt += 2048+32; pt = atrpt+(tone*32+(prog*32*16)); *(pt+17) = 0-(127+1); *(pt+16) = (15&15) | ((15-(0&15))<<4); } } // MUSIC if (MUSIC == 1) { mblockvh = allocatememory(getpaklength(_PAK_musicvh_INDEX)); if (mblockvh == 0) printf("CRASH: no memory for music VH\n"); unpak(_PAK_musicvh_INDEX, (void *)memoryblock[mblockvh].address); musicvhptr = (void *)memoryblock[mblockvh].address; musicvab = SsVabTransfer( (u_char *)memoryblock[mblockvh].address, (u_char *)getpakaddress(_PAK_musicvb_INDEX), -1, 1); if (musicvab<0) printf("CRASH: Could not transfer music sound\n"); // else // printf("Music VAB on %d\n",musicvab); // Fix Hack for (prog=0;prog<15;prog++) { for (tone=0;tone<5;tone++) { atrpt = (char *)musicvhptr; atrpt += 2048+32; pt = atrpt+(tone*32+(prog*32*16)); *(pt+17) = 0-(127+1); *(pt+16) = (15&15) | ((15-(0&15))<<4); } } SsSetMVol(mainvolume,mainvolume); SsSetMute(SS_MUTE_OFF); musicseq = SsSeqOpen((u_long *)getpakaddress(_PAK_raceseq_INDEX), musicvab); if (musicseq < 0) printf("Failed to open SEQ\n"); SsSeqSetVol(musicseq,musicvolume,musicvolume); funkseq = SsSeqOpen((u_long *)getpakaddress(_PAK_funkseq_INDEX), musicvab); if (funkseq < 0) printf("Failed to open FUNK seq\n"); SsSeqSetVol(funkseq,musicvolume,musicvolume); } // STd0 test // vhptr = (void *)getpakaddress(_PAK_std0vh_INDEX); // soundvab = SsVabTransfer ( (u_char *)getpakaddress(_PAK_std0vh_INDEX), (u_char *)getpakaddress(_PAK_std0vb_INDEX),-1,1); // if (soundvab<0) // printf("CRASH: COULD NOT TRANSFER std0 SOUND\n"); // else // printf("Sound VAB on %d\n",soundvab); } void closesound() { SsVabClose(soundvab); } void ny_title() { GsIMAGE img; RECT r; long x,y; GsSPRITE t1,t2; u_long *tim; int f,count; if (FPS == 50) SetVideoMode(MODE_PAL); // Set video mode else SetVideoMode(MODE_NTSC); GsInitGraph(SCREENWIDTH,SCREENHEIGHT,4,1,0); // Initialise graphics system. Screen is 320x240 pixels. // Non Interlaced/GPU offset for double buffering, no dithering, 16bit VRAM GsDefDispBuff(0,0,0,256); // Define Double buffering. Buffer 0 starts at 0,0 // Buffer 1 starts at 0,256 GsInit3D(); // Initialise 3D libraries projection = 96; GsDISPENV.screen.x = 4; t1.attribute = 2<<24; t1.w=160; t1.h=240; t1.tpage = GetTPage(2,0,512,0); t1.u = 0; t1.v = 0; t1.r = t1.g = t1.b = 0; t1.mx = 160; t1.my = 120; t1.scalex = t1.scaley = ONE; t1.rotate = 4096*50; t2 = t1; t2.tpage = GetTPage(2,0,512,256); t2.u = 0; t2.mx = 0; tim = (u_long *)getpakaddress(_PAK_nytitletim_INDEX); tim++; TransferTextures(tim); // copy title screen to frame buffer r.x = 512+160; // copy second half of image to seperate tpage r.y = 0; r.w = 160; r.h = 240; MoveImage(&r,512,256); for (f=0;f<2;f++) { WorldOT[f].length = OT_LENGTH; WorldOT[f].org = OTTags[f]; } x = 0<0) { activebuff = GsGetActiveBuff(); GsSetWorkBase((PACKET *)GpuPacketArea[activebuff]); GsClearOt(0, 0, &WorldOT[activebuff]); if (count>275) { t1.rotate -= 4096; t2.rotate = t1.rotate; t1.scalex = t1.scaley = ONE/(count-275); t2.scalex = t2.scaley = t1.scalex; } if (count>197) t1.r = t1.g = t1.b = t2.r = t2.g = t2.b = t1.r + 1; if (count<63) t1.r = t1.g = t1.b = t2.r = t2.g = t2.b = t1.r - 2; t1.x = t2.x = x>>MYBITSHIFT; t1.y = t2.y = y>>MYBITSHIFT; GsSortSprite(&t1,&WorldOT[activebuff],1); GsSortSprite(&t2,&WorldOT[activebuff],1); DrawSync(0); VSync(0); GsSwapDispBuff(); GsSortClear(0,0,0,&WorldOT[activebuff]); GsDrawOt(&WorldOT[activebuff]); count--; } } void specialthanks() { int count,f; GsSPRITE sp1,sp2,mspr[MAXMICHAEL],tmpspr; short *sintab; sintab = (short *)getpakaddress(_PAK_sintab_INDEX); TransferTIM(_PAK_specialtim_INDEX); TransferTIM(_PAK_michaeltim_INDEX); for (f=0;f<2;f++) { WorldOT[f].length = OT_LENGTH; WorldOT[f].org = OTTags[f]; } for (f=0;f64) // spin in 'michael de cruz' { mspr[0].x = (32767-*(sintab+(154-(count-64))))/102; mspr[0].y = (32767-*(sintab+(154-(count-64))))/164; mspr[0].rotate = (32767-*(sintab+(154-(count-64))))*8; mspr[0].scalex = mspr[0].scaley = 4096-(32767-*(sintab+(154-(count-64))))/6; } if (count>128 && count<192) // fade 'for ...' in sp2.r = sp2.g = sp2.b = sp2.r + 2; if (count==392) // clear michael mspr[0].y = -200; if (count>400) { sp1.r = sp1.g = sp1.b = sp1.r - 2; sp2.r = sp2.g = sp2.b = sp2.r - 2; for (f=0;f 1) mspr[f].r = mspr[f].g = mspr[f].b = mspr[f].r - 2; } } for (f=MAXMICHAEL-1;f>0;f-=1) { mspr[f].x = mspr[f-1].x; mspr[f].y = mspr[f-1].y; mspr[f].rotate = mspr[f-1].rotate; mspr[f].scalex = mspr[f-1].scalex; mspr[f].scalex = mspr[f-1].scalex; } if (count>64) for (f=0;f 128) GsSortSprite(&sp2,&WorldOT[activebuff],0); DrawSync(0); VSync(0); GsSwapDispBuff(); GsSortClear(0,0,0,&WorldOT[activebuff]); GsDrawOt(&WorldOT[activebuff]); count++; } } void showrankings() { int count,screen,f,g; int rankd[4],rankp[4],tmp; for (f=0;f<4;f++) { // printf("%d %d\n",carpositions[f],players[object[driver[carpositions[f]].object].extra->playerindex].cash); players[object[driver[carpositions[f]].object].extra->playerindex].score+=8-f*1.3; players[object[driver[carpositions[f]].object].extra->playerindex].cash+=(600-f*150); // printf("%d %d\n\n",carpositions[f],players[object[driver[carpositions[f]].object].extra->playerindex].cash); } for (f=0;f<4;f++) // set up list of drivers and scores { rankd[f]=carpositions[f]; rankp[f]=players[object[driver[rankd[f]].object].extra->playerindex].score; } for (g=0;g<4;g++) for (f=1;f<4;f++) { if (rankp[f]>rankp[f-1]) tmp = rankp[f]; rankp[f] = rankp[f-1]; rankp[f-1] = tmp; tmp = rankd[f]; rankd[f] = rankd[f-1]; rankd[f-1]=tmp; } projection=96; // 512 GsSetProjection(projection); cameraview.vrx = 0; cameraview.vry = 0; cameraview.vrz = 0; cameraview.vpx = 0; cameraview.vpy = 0; cameraview.vpz = -1800; cameraview.rz = 0; cameraview.super = WORLD; GsSetRefView2(&cameraview); count = 0; screen = 0; while (count<600) { activebuff = GsGetActiveBuff(); GsSetWorkBase((PACKET *)GpuPacketArea[activebuff]); GsClearOt(0, 0, &WorldOT[activebuff]); GsClearOt(0, OTPRI_BACK, &BackOT[activebuff]); // background object GsClearOt(0, 0, &OverlayOT[activebuff]); GsSetRefView2(&cameraview); if (count == 300) screen = 1; if (screen == 0) { print8(112,64,"RACE RESULTS"); print8(16,80,"POS"); print8(160,80,"PTS"); print8(190,80,"CASH"); for (f=0;f<4;f++) { if (count > f*25) { int8(16,f*8+100,1,f+1); if (driver[carpositions[f]].class == 1) // if human print8(32,f*8+100,players[object[driver[carpositions[f]].object].extra->playerindex].name); else print8(32,f*8+100,"CPU"); int8(160,f*8+100,1,8-f*1.3); int8(190,f*8+100,3,600-f*150); } } } else if (screen == 1) { print8(112,64,"OVERALL RANKINGS"); for (f=0;f<4;f++) { if (driver[rankd[f]].class == 1) print8(32,f*8+100,players[object[driver[rankd[f]].object].extra->playerindex].name); else print8(32,f*8+100,"CPU"); int8(160,f*8+100,3,players[object[driver[rankd[f]].object].extra->playerindex].score); } } UpdateObjectCoordinates(&backlobj); UpdateObjectCoordinates(&backrobj); DisplayObject3D(&backlobj); DisplayObject3D(&backrobj); GsSortOt(&BackOT[activebuff],&WorldOT[activebuff]); DrawSync(0); GsDrawOt(&OverlayOT[activebuff]); DrawSync(0); VSync(0); GsSwapDispBuff(); GsSortClear(0,0,0,&WorldOT[activebuff]); GsDrawOt(&WorldOT[activebuff]); count++; } }