#include #include #include #include #include #include #include #include #include #include #include #include #include "Xbit.h" #define WINX 100 #define WINY 100 #define WINW 220 #define WINWH 110 #define WINH 250 #define WINHH 125 #define TAGLINE 242 #define RND(x) (rand()%(x)) #define ITOF(x) ((x)<<8) #define FTOI(x) ((x)>>8) #define AT(p,x,y) *((p)+(x)+(y)*WINW) #define HISCFILE "hisc.gal" #define CH(x,y) ((x)*SCALE),((y)*SCALE),SCALE,SCALE XSizeHints size_hints; ulong black,white; Display *disp; Window top,sub; Colormap cm; XFontStruct *font; XImage *img; char *data; char *coll_data; double si[3600],co[3600]; static struct timespec frame_start; static XFuncControl Key(Window w, XPressRelease s, XEvent *e); static XFuncControl ProcessTitle(void),ProcessIntro(void),ProcessGame(void), ProcessGameOver(void),ProcessHiScore(void), ProcessStartLevel(void); /* Hiscore */ #define NOHI 10 typedef struct { char name[4]; int score; int level; double percent; } HiSc; HiSc hisc[NOHI]= { {"A.B",10000,1,50.0}, {"C.D",5000,1,50.0}, {"E.F",3000,1,50.0}, {"G.H",1500,1,50.0}, {"I.J",1000,1,50.0}, {"K.L",800,1,50.0}, {"M.N",600,1,50.0}, {"O.P",400,1,50.0}, {"Q.R",200,1,50.0}, {"S.T",100,1,50.0}, }; /* Keycontrols */ static XWindowKeyCallback key[2]= { {0,Key}, {0,NULL} }; #define NONE -1 #define LEFT 0 #define RIGHT 1 #define FIRE 2 #define QUIT 3 #define PAUSE 4 int inkey=NONE; KeySym last_keysym; int keymap[PAUSE+1]={False,False,False,False,False}; /* Sprite defs */ #define SPRW 8 #define SPRH 8 #define CHXSPR(a) ((a)*SPRW) #define SPRMAXANIM 10 #define SPRANIMFRAME 10 unsigned int framectr =0; unsigned int sprframe =0; typedef struct { ulong data[SPRH][SPRW]; } SpriteDef; typedef struct { int no; char data[SPRH*SPRW]; } CollData; typedef struct { int no; SpriteDef *spr[SPRMAXANIM]; } SpriteAnim; #define NOSPR 11 #define SHIP 0 #define ABULLET 1 #define SBULLET 2 #define GALAX1 3 #define GALAX2 4 #define GALAX3 5 #define FLAG1 6 #define FLAG10 7 #define ENTITY1 8 #define ALIENUFO 9 #define SAFESHIP 10 #include "sprdef.h" /* Collision data codes */ #define CNONE 0 #define CSHIP 1 #define CALIEN 2 #define CABULL 3 #define CUBULLM 64 #define CUBULL(x) (CUBULLM+(x)) /* Alien data */ #define NOSHOT 0 #define SISHOT 1 #define GASHOT 2 #define VISHOT 3 typedef struct { int y; int x; int xi; int yi; int inuse; } Shot; typedef struct { int alive; int type; Shot shot; int x,y; int sx,sy; /* Use for workspace for 'alternative' coords */ } Alien; #define MAXALIEN 100 int no_aliens; int aliens_alive; Alien alien[MAXALIEN]; /* Explosion FX data */ #define MAXEXP 20 #define EXPPART 20 #define EXPLIFE 10 struct { int inuse; int cx[EXPPART],cy[EXPPART],x[EXPPART],y[EXPPART]; } exp_fx[MAXEXP]; int no_exp; /* Level data */ #define NOLEV 11 struct { char *title; int sx,sy; int shot_type; } levdata[NOLEV]= { {"A CLASSIC...",0,1,SISHOT}, {"RUN AWAY - WATCH YOUR BACK",1,-2,GASHOT}, {"DIZZY",0,1,SISHOT}, {"VICIOUS CARNIVAL DUCKS!",0,1,VISHOT}, {"THE ALIEN WARFLEET - WAVE 1",-1,0,GASHOT}, {"BOUNCY",0,1,GASHOT}, {"THE 1ST COLLECTIVE",0,2,SISHOT}, {"THE 2ND COLLECTIVE",0,2,SISHOT}, {"THE 3RD COLLECTIVE",0,2,GASHOT}, {"THE ALIEN WARFLEET - WAVE 2",-1,0,GASHOT}, {"THE ALIEN MOTHERBRAIN",0,1,GASHOT}, }; /* Colour vars */ #define NOCOLS 8 #define RNDCOL (RND(NOCOLS-2)+2) #define RGB(r,g,b) {(r)*255,(g)*255,(b)*255} static ulong pix[NOCOLS]; static Colour cols[NOCOLS]= { RGB(0,0,0), /* BLACK */ RGB(255,255,255), /* WHITE */ RGB(255,100,100), /* RED */ RGB(100,255,100), /* GREEN */ RGB(100,100,255), /* BLUE */ RGB(255,255,0), /* YELLOW */ RGB(0,255,255), /* CYAN */ RGB(165,42,42) /* BROWN */ }; #define BLACKI 0 #define WHITEI 1 #define REDI 2 #define GREENI 3 #define BLUEI 4 #define YELLOWI 5 #define CYANI 6 #define BROWNI 7 #define BLACK pix[BLACKI] #define WHITE pix[WHITEI] #define RED pix[REDI] #define GREEN pix[GREENI] #define BLUE pix[BLUEI] #define YELLOW pix[YELLOWI] #define CYAN pix[CYANI] #define BROWN pix[BROWNI] /* Parallax vars */ #define NOPLXST 30 #define NOPLXPL 3 struct { int x; int y; ulong c; } plxst[NOPLXST][NOPLXPL]; /* User bullet vars */ #define MAXBULL 100 #define BULLYI -3 typedef struct { int inuse,x,y; } UserBullet; UserBullet ubull[MAXBULL]; int bullctr=0; /* Control vars */ int NOLIVES =3; int NOBULL =4; int BULLDEL =10; int LEVEL =0; int SCORE =0; int PAUSESKIP =0; int FONT =0; int SCALE =1; static void POKEIMG_NS(XImage *img, int x, int y, ulong c); static void POKEIMG_S(XImage *img, int x, int y, ulong c); void (*POKEIMG)(XImage *img, int x, int y, ulong c); /* Game and control vars */ #define SHXINIT WINW/2-SPRW/2 #define SHIPY WINH-16 #define BLYINIT SHIPY-8 int quit=False; int score,level,levelndx,lives; int shipx,hit,miss,dead; int aliendead; static void ReadScores(void); static void WriteScores(void); static void ClearExplosions(void); static void NewExplosion(int x, int y, int w); static void UpdateExplosions(void); static void InitAlien(int f, int x, int y, int t); static void SinCos(int cx, int cy, int a, int rx, int ry, int *x, int *y); static void DefineAliens(void); static void MoveAliens(void); static void CheckAlienShot(Alien *a); static void DrawAndKillAliens(void); static void ClearBullets(void); static void ClearKeys(void); static void DoDebugMenu(void); static int Paused(int k); static void StartFrame(void); static void EndFrame(void); int main(int argc, char *argv[]) { void IntrHandler(); int f,r,x,y,arg,noshm; unsigned long evmask; arg=1; noshm=False; if (argc>arg) if (!strcmp(argv[arg],"-noshm")) { noshm=True; arg++; } if (argc>arg) { SCALE=atoi(argv[arg]); if (SCALE<1) { fprintf(stderr,"scale param must be +ve\n"); exit(1); } } if (SCALE==1) POKEIMG=POKEIMG_NS; else POKEIMG=POKEIMG_S; for(f=0;fdata[y][x]= pix[anim[f].spr[r]->data[y][x]]; XAutoRepeatOff(disp); XISetFont(FONT); ReadScores(); XDoWindows(NULL,NULL,key,ProcessTitle); while(!quit) { score=SCORE; level=LEVEL; levelndx=LEVEL%NOLEV; lives=NOLIVES; hit=0; miss=0; do { shipx=SHXINIT; dead=False; ClearBullets(); ClearExplosions(); ClearKeys(); XDoWindows(NULL,NULL,key,ProcessStartLevel); DefineAliens(); ClearBullets(); ClearExplosions(); ClearKeys(); XDoWindows(NULL,NULL,key,ProcessGame); } while (lives>0); if (score>-1) { ClearBullets(); ClearExplosions(); ClearKeys(); XDoWindows(NULL,NULL,key,ProcessGameOver); ReadScores(); if (score>hisc[NOHI-1].score) { XDoWindows(NULL,NULL,key,ProcessHiScore); WriteScores(); } } ClearKeys(); XDoWindows(NULL,NULL,key,ProcessTitle); } XAutoRepeatOn(disp); DestroyXImage(img); XCloseDisplay(disp); } /* ----------------------------------------------- KEY AND UTILITY */ static XFuncControl Key(Window w, XPressRelease s, XEvent *e) { if (s==XRELEASE) { last_keysym=XK_VoidSymbol; switch(XLookupKeysym((XKeyEvent *)e,ShiftMapIndex)) { case XK_Z: case XK_z: keymap[LEFT]=False; break; case XK_C: case XK_c: keymap[RIGHT]=False; break; case XK_period: keymap[FIRE]=False; break; case XK_P: case XK_p: keymap[PAUSE]=False; break; case XK_Q: case XK_q: keymap[QUIT]=False; break; default: break; } } if (s==XPRESS) { inkey=NONE; switch(last_keysym=XLookupKeysym((XKeyEvent *)e,ShiftMapIndex)) { case XK_Z: case XK_z: inkey=LEFT; break; case XK_C: case XK_c: inkey=RIGHT; break; case XK_period: inkey=FIRE; break; case XK_P: case XK_p: inkey=PAUSE; break; case XK_Q: case XK_q: inkey=QUIT; break; default: inkey=NONE; break; } if (inkey!=NONE) keymap[inkey]=True; } return (XFUNCCONT); } static int GetKey(void) { int k=inkey; if ((inkey==PAUSE)||(inkey==QUIT)||(inkey=FIRE)) inkey=NONE; return(k); } static KeySym RawKey(void) { KeySym k=last_keysym; last_keysym=XK_VoidSymbol; return(k); } static void ClearKeys(void) { int f; inkey=NONE; for(f=0;f<5;f++) keymap[f]=False; } /* ----------------------------------------------- GRAPHICS UTILS */ static void Cls(int plx,int xi,int yi,int clr) { static unsigned int col; int f,r,x,y; if (!((framectr++)%SPRANIMFRAME)) { sprframe++; for(f=0;f=WINH) plxst[f][r].y-=WINH; plxst[f][r].x+=((r+1)*xi); if (plxst[f][r].x<0) plxst[f][r].x+=WINW; if (plxst[f][r].x>=WINW) plxst[f][r].x-=WINW; if (!(col%10)) plxst[f][r].c=pix[RNDCOL]; (*POKEIMG)(img,plxst[f][r].x,plxst[f][r].y,plxst[f][r].c); } } } static void Update(void) { DrawXImage(img); XSync(disp,False); } static void Centre(int y, ulong c, const char *s) { XIprintf(img,CH(WINWH-strlen(s)*4,y),c,"%s",s); } static void POKEIMG_NS(XImage *img, int x, int y, ulong c) { XPutPixel(img,x,y,c); } static void POKEIMG_S(XImage *img, int x, int y, ulong c) { int sx,sy; for(sx=0;sx=0)&&(x=0)&&(yno=0; for(sy=0;sy=0)&&((y+sy)data[sy][sx]!=BLACKI) if (((x+sx)>=0)&&((x+sx)data[coll->no++]=AT(coll_data,x+sx,y+sy); AT(coll_data,x+sx,y+sy)=collcode; } (*POKEIMG)(img,x+sx,y+sy,spr[s]->data[sy][sx]); } } /* ----------------------------------------------- USER BULLET UTILS */ static void ClearBullets(void) { int f; bullctr=0; for(f=0;f10) NOLIVES=1; break; case 'd': if (BULLDEL==10) BULLDEL=1; else BULLDEL=10; break; case 'm': if (NOBULL==4) NOBULL=MAXBULL; else NOBULL=4; break; case 's': LEVEL++; break; case 'x': SCORE=0; NOLIVES=3; BULLDEL=10; NOBULL=4; LEVEL=0; break; case 'o': PAUSESKIP=1; NOLIVES=1; BULLDEL=1; NOBULL=MAXBULL; LEVEL=NOLEV-1; SCORE=hisc[0].score; break; case 'w': PAUSESKIP=1; LEVEL=NOLEV-1; break; } } } /* ----------------------------------------------- START LEVEL */ static XFuncControl ProcessStartLevel(void) { char s[80]; StartFrame(); Cls(True,levdata[levelndx].sx,levdata[levelndx].sy,True); sprintf(s,"LEVEL %d",level+1); Centre (50,YELLOW,s); Centre (100,RED,levdata[levelndx].title); Update(); EndFrame(); if (GetKey()!=NONE) return(XFUNCSTOP); return(XFUNCCONT); } /* ----------------------------------------------- PLAY LEVEL */ static XFuncControl ProcessGame(void) { static unsigned int ctr=0; static int immune=0; int f,k; CollData coll; ctr++; k=GetKey(); if (!PAUSESKIP) if (Paused(k)) return (XFUNCCONT); StartFrame(); Cls(True,levdata[levelndx].sx,levdata[levelndx].sy,True); if (lives) for(f=0;f1) shipx-=2; if (keymap[RIGHT]) if (shipx1)) lives++; return(XFUNCSTOP); } return(XFUNCCONT); } /* ----------------------------------------------- PAUSE CODE */ static void DoPause(void) { static unsigned int c=0; c++; if ((c/10)%2) Centre(WINH/2-4,WHITE,"PAUSED"); else Centre(WINH/2-4,YELLOW,"PAUSED"); Update(); } static int Paused(int k) { static int paused=False; if (k==PAUSE) paused=!paused; if (paused) DoPause(); return(paused); } /* ----------------------------------------------- GAME OVER */ static XFuncControl ProcessGameOver(void) { static int posx=32; static unsigned int ctr=0; StartFrame(); Cls (True,0,1,True); ctr++; XIprintf(img,CH(posx,32),YELLOW,"SHOTS FIRED : %6d",hit+miss); XIprintf(img,CH(posx,48),YELLOW,"HIT : %6d",hit); XIprintf(img,CH(posx,64),YELLOW,"MISS : %6d",miss); if (hit+miss) XIprintf(img,CH(posx,80),YELLOW,"PERCENTAGE : %5.1f", ((double)hit/(hit+miss))*100.0); else XIprintf(img,CH(posx,80),YELLOW,"PERCENTAGE : 0.0"); if ((ctr/10)%2) Centre(120,WHITE,"GAME OVER"); Update(); EndFrame(); if (GetKey()==NONE) return(XFUNCCONT); else return(XFUNCSTOP); } /* ----------------------------------------------- HI SCORES */ static void ReadScores(void) { int fd,f; if ((fd=open(HISCFILE,O_RDONLY))==-1) return; for(f=0;f-1;f--) if (hisc[f].scorepos;f--) hisc[f]=hisc[f-1]; strcpy(hisc[pos].name,""); hisc[pos].score=score; hisc[pos].level=level+1; if (hit+miss) hisc[pos].percent=((double)hit/(hit+miss))*100.0; else hisc[pos].percent=0.0; first=False; } StartFrame(); Cls (True,1,1,True); Centre(16,WHITE,"CONGRATULATIONS!"); Centre(30,YELLOW,"ENTER YOUR INITIALS"); Centre(40,YELLOW,"FOR THE ALL TIME HEROES"); hisc[pos].name[len]=*let; hisc[pos].name[len+1]=0; for(f=0;fWINW/3)) { y=-y; x+=y; } } break; default: printf("undefined level %d?\n",levelndx); exit(1); } } static void MoveAliens(void) { int f,x,y; int flag=False; alienctr++; switch(levelndx) { case 0: /* CLASSIC */ for (f=0;fWINW-SPRW-1)) flag=True; } if (flag) { alienxi=-alienxi; for (f=0;fWINH) alien[f].y-=WINH; } break; case 1: /* RUN AWAY */ for (f=0;f3599) alien[f].sx-=3600; } else { if ((alien[f].sx-=10)<0) alien[f].sx+=3600; } SinCos(WINW/2,WINH/2,alien[f].sx,alien[f].sy*2, alien[f].sy/2,&alien[f].x,&alien[f].y); } break; case 3: /* CARNIVAL */ for(f=0;f=WINW) alien[f].x-=WINW+SPRW; break; case 4: /* FLEET WAVE 1 */ break; case 5: /* BOUNCY */ for(f=0;f=WINW)) alien[f].sx=-alien[f].sx; alien[f].y+=alien[f].sy; if ((alien[f].y<=0)||(alien[f].y>=SHIPY)) alien[f].sy=-alien[f].sy; } break; case 6: /* THE COLLECTIVE 1 */ for(f=0;fdata[alien[f].sy][alien[f].sx]!=BLACK) { alien[f].x=WINW/2-(SPRW+2)*4+alien[f].sx*(SPRW+2); alien[f].y=WINH/2-(SPRH+2)*4+alien[f].sy*(SPRH+2); } else { alien[f].x=-100; alien[f].y=SHIPY; } } break; case 7: /* THE COLLECTIVE 2 */ for(f=0;fdata[alien[f].sy][alien[f].sx]!=BLACK) { alien[f].x=WINW/2-(SPRW+2)*4+alien[f].sx*(SPRW+2); alien[f].y=WINH/2-(SPRH+2)*4+alien[f].sy*(SPRH+2); } else { alien[f].x=-100; alien[f].y=SHIPY; } } break; case 8: /* THE COLLECTIVE 3 */ for(f=0;fdata[alien[f].sy][alien[f].sx]!=BLACK) { alien[f].x=WINW/2-(SPRW+2)*4+alien[f].sx*(SPRW+2); alien[f].y=WINH/2-(SPRH+2)*4+alien[f].sy*(SPRH+2); } else { alien[f].x=-100; alien[f].y=SHIPY; } } break; case 9: /* FLEET WAVE 2 */ for(y=f=0;y<3;y++) for(x=0;x<20;x++,f++) if (alien[f].alive) { if (y%2) { if ((alien[f].x+=(y+1))>=WINW) alien[f].x-=WINW+SPRW; } else { if ((alien[f].x-=(y+1))<=-SPRW) alien[f].x+=WINW+SPRW; } } break; case 10: /* ALIEN MOTHERBRAIN */ for(f=0;f<44;f++) if (alien[f].alive) { SinCos(WINW/2,WINH/2,f*81,alien[f].sx, alien[f].sx,&alien[f].x,&alien[f].y); alien[f].sx+=alien[f].sy; if ((alien[f].sx<10)||(alien[f].sx>WINW/3)) { alien[f].sy=-alien[f].sy; alien[f].sx+=alien[f].sy; } } break; default: printf("undefined level %d?\n",levelndx); exit(1); } } static void CheckAlienShot(Alien *a) { if ((levdata[levelndx].shot_type==NOSHOT)||(dead)) return; if (a->shot.inuse) return; if (RND(200)<(1+level)) { a->shot.inuse=True; a->shot.x=ITOF(a->x); a->shot.y=a->y+SPRH; switch (levdata[levelndx].shot_type) { case VISHOT: a->shot.yi=1+RND(2); break; default: a->shot.yi=1; break; } switch (levdata[levelndx].shot_type) { case SISHOT: a->shot.xi=0; break; case VISHOT: if (shipxx) a->shot.xi=-16*a->shot.yi; else if (shipxx) a->shot.xi=16*a->shot.yi; else a->shot.xi=0; break; case GASHOT: if (shipxx) a->shot.xi=-64; else if (shipx>a->x) a->shot.xi=64; else a->shot.xi=0; break; default: a->shot.xi=0; break; } } } static void DrawAndKillAliens(void) { CollData coll; int f,r; aliendead=True; for(f=0;fWINH) alien[f].shot.inuse=False; else Sprite(ABULLET,FTOI(alien[f].shot.x),alien[f].shot.y, &coll,CABULL); } } } static void StartFrame(void) { clock_gettime(CLOCK_REALTIME, &frame_start); } static void EndFrame(void) { static const long FRAME = 1000000000 / 50; struct timespec now; struct timespec diff; clock_gettime(CLOCK_REALTIME, &now); diff.tv_sec = now.tv_sec - frame_start.tv_sec; diff.tv_nsec = now.tv_nsec - frame_start.tv_nsec; if (diff.tv_sec > 0) { diff.tv_sec--; diff.tv_nsec += 1000000000; } if (diff.tv_nsec >= 1000000000) { diff.tv_sec++; diff.tv_nsec -= 1000000000; } if (diff.tv_sec == 0 && diff.tv_nsec < FRAME) { diff.tv_nsec = FRAME - diff.tv_nsec; nanosleep(&diff, NULL); } }