diff options
Diffstat (limited to 'lunar.c')
-rw-r--r-- | lunar.c | 1843 |
1 files changed, 1843 insertions, 0 deletions
@@ -0,0 +1,1843 @@ +/* + + lunar - Simple X11 Lunar Lander + + Copyright (C) 2005 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + This code is rather messy as it is a quick tidy up from the K&R original. + +*/ +static char rcs_id[]="$Id$"; + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xos.h> +#include <X11/Xatom.h> +#include <X11/keysym.h> + +#include <stdlib.h> +#include <stdio.h> +#include <math.h> +#include <fcntl.h> +#include <string.h> + +#include "Xbit.h" + +#define WINX 100 +#define WINY 100 +#define WINW 320 +#define WINWH 160 +#define WINH 200 +#define WINHH 100 + +#define RND(x) (rand()%(x)) + +#define ITOF(x) ((x)<<8) +#define FTOI(x) ((x)>>8) + +#define ABS(x) ((x)<0 ? (-(x)) : (x)) +#define SGN(x) ((x) ? ((x)/ABS(x)) : (0)) +#define DABS(x) ((x)<0.0 ? (-(x)) : (x)) + +#define AT(p,x,y) *((p)+(x)+(y)*WINW) + +#define HISCFILE "hisc.ll" +#define LEVELDIR "LEVELS/" + +#define CH(x,y) ((x)*SCALE),((y)*SCALE),SCALE,SCALE +#define CHSC(x,y,sx,sy) ((x)*SCALE),((y)*SCALE),SCALE*sx,SCALE*sy + +static XSizeHints size_hints; +static ulong black,white; +static Display *disp; +static Window top,sub; +static Colormap cm; +static XFontStruct *font; +static XImage *img; +static uchar *coll_data; + +static double si[3600],co[3600]; + +static XFuncControl Key(Window w, XPressRelease s, XEvent *e); +static XFuncControl ProcessTitle(void),ProcessIntro(void),ProcessGame(void), + ProcessGameOver(void),ProcessHiScore(void); + +/* Hiscore +*/ +#define NOHI 3 + +typedef struct +{ + char name[4]; + int score; + int no; +} HiSc; + +static HiSc hisc[NOHI]= + { + {"N.B",100,1}, + {"N.B",100,1}, + {"N.B",100,1}, + }; + +/* Keycontrols +*/ +static XWindowKeyCallback + key[2]= + { + {0,Key}, + {0,NULL} + }; + +#define NONE -1 +#define FINE_LEFT 0 +#define FINE_RIGHT 1 +#define THRUST 2 +#define LEFT 3 +#define RIGHT 4 +#define QUIT 5 +#define SCALE_UP 6 +#define SCALE_DOWN 7 +#define PAUSE 8 + +static int inkey=NONE; +static KeySym last_keysym; +static int keymap[PAUSE+1]= + {False,False,False,False,False, + False,False,False,False}; + + +/* Collision data codes - NB : order is important for Vector collisions - a + collision of '4' will override a collision + with '3' +*/ +#define CNONE 0 +#define CPAD 1 +#define CASTEROID 2 +#define CMOUNTAIN 3 +#define ISCMINE(c) ((c)&(0x80)) +#define CMINE(x) ((x)+0x80) + + +/* Colour vars +*/ +#define NOCOLS 8 +#define RNDCOL pix[(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 IBLACK 0 +#define IWHITE 1 +#define IRED 2 +#define IGREEN 3 +#define IBLUE 4 +#define IYELLOW 5 +#define ICYAN 6 +#define IBROWN 7 + +#define BLACK pix[IBLACK] +#define WHITE pix[IWHITE] +#define RED pix[IRED] +#define GREEN pix[IGREEN] +#define BLUE pix[IBLUE] +#define YELLOW pix[IYELLOW] +#define CYAN pix[ICYAN] +#define BROWN pix[IBROWN] + + +/* 2D Vector defs +*/ +#define VECPT 32 +#define VECLN 32 + +typedef struct + { + double x,y; + } VecPt; + + +typedef struct + { + int p1,p2; + } VecLine; + + +typedef struct + { + VecPt pos; + int ang; + int no_pt; + VecPt pt[VECPT]; + int no_ln; + VecLine ln[VECLN]; + VecPt work[VECPT]; + ulong colour; + int collcode; + int coll[VECLN]; + } VecObject; + + +/* Lander vector object +*/ +static VecObject base_lander= + { + {0.0,0.0}, /* pos */ + 0, /* ang */ + + 7, /* no_pt */ + { /* pt[] */ + { 0.0, -10.0}, + {-10.0, 10.0}, + { 10.0, 10.0}, + { -7.0, 10.0}, + { 7.0, 10.0}, + {-10.0, 20.0}, + { 10.0, 20.0}, + }, + + 5, /* no_ln */ + { /* ln[] */ + {0,1}, + {1,2}, + {2,0}, + {3,5}, + {4,6}, + }, + + { /* work[] */ + {0.0,0.0}, + }, + + IWHITE, /* colour */ + CNONE, /* collcode */ + {0,}, /* coll */ + }; + +static VecObject lander; + + +/* Particle definitions +*/ +#define NOPLIST 4 +#define PSHORT 0 +#define PMID 1 +#define PLONG 2 +#define PULTRA 3 + +typedef struct + { + double x,y; + } ParticlePt; + +typedef struct part + { + int life; + ParticlePt p; + ParticlePt i; + struct part *next; + } Particle; + +static Particle *p_head[NOPLIST]; +static Particle *p_tail[NOPLIST]; + + +/* Now we are using MIT-SHM structures, there should be more than enough + power for some background stars to give the player a visual cue of bearing +*/ +#define NOBDSTAR 50 +typedef struct + { + double x,y; + } BdStar; + +static BdStar bdrop[NOBDSTAR]; + + +/* Level data - should improve this really... Using up 3Mb statically for + level definitions is probably a bit _too_ much! :-} +*/ +#define MAXLEVEL 32 +#define MAXLEVPOLY 64 + +#define MOUNTAIN 0 +#define ASTEROIDCW 1 +#define ASTEROIDACW 2 +#define PAD 3 +#define MINE 4 + +typedef struct + { + int no; + char *name; + int type[MAXLEVPOLY]; + int draw[MAXLEVPOLY]; + VecObject v[MAXLEVPOLY]; + } LevelDef; + +static int no_levels; +static char *level_set; +static LevelDef levdata[MAXLEVEL]; +static LevelDef level; + + +/* Damage vars +*/ +#define DAMAGE_NONE 0 +#define DAMAGE_LEFT 1 +#define DAMAGE_RIGHT 2 +#define DAMAGE_MAIN 3 + +static int damage; + +/* Control vars +*/ +static int FUEL =500; +static int SCORE =0; +static int FONT =1; +static int LEVEL =0; +static int SHOWVEC =0; +static double GRAVITY =0.1; +static double MAXGRAV =8.0; +static double JET =0.5; + +static int SCALE =1; +static void POKEIMG_S(XImage *img, int x, int y, ulong col); +static void POKEIMG_NS(XImage *img, int x, int y, ulong col); +static void (*POKEIMG)(XImage *img, int x, int y, ulong col); + +static int quit=False; +static int do_intro; +static int score,rot,fuel,landing,lev; +static double shipxi,shipyi; +static int offx,offy; + + +/* Protos +*/ +static void ClearKeys(void); +static void DefineLevel(void); +static void DrawObject(VecObject *o); +static void ClearParticles(void); +static void InitBdrop(void); +static int Paused(int k); +static void ReadScores(void); +static void WriteScores(void); +static void ReadLevels(void); + + +int main(int argc,char *argv[]) +{ + 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;f<3600;f++) + { + si[f]=sin(M_PI/1800.0*f); + co[f]=cos(M_PI/1800.0*f); + } + + for(f=0;f<NOPLIST;f++) + p_head[f]=p_tail[f]=NULL; + + umask(0); + srand(getpid()); + + size_hints.flags=PPosition|PSize|PMinSize|PMaxSize; + size_hints.x=WINX; + size_hints.y=WINY; + size_hints.width=size_hints.min_width=WINW*SCALE; + size_hints.height=size_hints.min_height=WINH*SCALE; + size_hints.max_width=WINW; + size_hints.max_height=WINH; + + evmask=KeyPressMask|KeyReleaseMask; + + top=OpenWin(argc,argv,argv[0], + WINX,WINY,WINW*SCALE,WINH*SCALE, + WINW*SCALE,WINH*SCALE, + evmask, + &size_hints,&black,&white); + + key[0].w=top; + + DisableDoubleBuffer(); + disp=GetDisplay(); + + DisablePixmap(); + + if (noshm) + DisableShm(); + + img=CreateXImage(); + + if (!(coll_data=malloc(WINW*WINH))) + { + fprintf(stderr,"Couldn't malloc() collision data\n"); + exit(1); + } + + AllocColoursRGB(NOCOLS,pix,cols); + + ReadLevels(); + + base_lander.colour=pix[base_lander.colour]; + + XAutoRepeatOff(disp); + + XISetFont(FONT); + + ReadScores(); + + XDoWindows(NULL,NULL,key,ProcessTitle); + + while(!quit) + { + score=SCORE; + fuel=FUEL; + lev=LEVEL; + landing=0; + do_intro=True; + + do + { + DefineLevel(); + ClearParticles(); + InitBdrop(); + + if (do_intro) + { + do_intro=False; + ClearKeys(); + XDoWindows(NULL,NULL,key,ProcessIntro); + } + + memcpy(&lander,&base_lander,sizeof(VecObject)); + shipxi=0.0; + shipyi=0.0; + damage=DAMAGE_NONE; + + XDoWindows(NULL,NULL,key,ProcessGame); + } while (fuel>0); + + if (score>-1) + { + 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_A: + case XK_a: + keymap[FINE_LEFT]=False; + break; + + case XK_D: + case XK_d: + keymap[FINE_RIGHT]=False; + break; + + case XK_Z: + case XK_z: + keymap[LEFT]=False; + break; + + case XK_C: + case XK_c: + keymap[RIGHT]=False; + break; + + case XK_period: + keymap[THRUST]=False; + break; + + case XK_P: + case XK_p: + keymap[PAUSE]=False; + break; + + case XK_Q: + case XK_q: + keymap[QUIT]=False; + break; + + case XK_bracketleft: + keymap[SCALE_DOWN]=False; + break; + + case XK_bracketright: + keymap[SCALE_UP]=False; + break; + + default: + keymap[NONE]=False; + break; + } + } + + if (s==XPRESS) + { + inkey=NONE; + + switch(last_keysym=XLookupKeysym((XKeyEvent *)e,ShiftMapIndex)) + { + case XK_A: + case XK_a: + inkey=FINE_LEFT; + break; + + case XK_D: + case XK_d: + inkey=FINE_RIGHT; + break; + + case XK_Z: + case XK_z: + inkey=LEFT; + break; + + case XK_C: + case XK_c: + inkey=RIGHT; + break; + + case XK_period: + inkey=THRUST; + break; + + case XK_P: + case XK_p: + inkey=PAUSE; + break; + + case XK_Q: + case XK_q: + inkey=QUIT; + break; + + case XK_bracketleft: + inkey=SCALE_DOWN; + break; + + case XK_bracketright: + inkey=SCALE_UP; + break; + + default: + inkey=NONE; + break; + } + + if (inkey!=NONE) + keymap[inkey]=True; + } + + return XFUNCCONT; +} + + +static int GetKey(void) +{ + int k=inkey; + + if ((inkey=SCALE_UP)||(inkey=SCALE_UP)||(inkey==PAUSE)|| + (inkey==QUIT)||(inkey=THRUST)) + 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(void) +{ + ClsXImage(img); + memset(coll_data,CNONE,WINW*WINH); +} + + +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<SCALE;sx++) + for(sy=0;sy<SCALE;sy++) + XPutPixel(img,x*SCALE+sx,y*SCALE+sy,c); +} + + +static void Plot(int x, int y, ulong c) +{ + if ((x>=0)&&(x<WINW)&&(y>=0)&&(y<WINH)) + POKEIMG(img,x,y,c); +} + + +/* ----------------------------------------------- LEVEL UTILS +*/ +static void DefineLevel(void) +{ + memcpy(&level,&levdata[lev],sizeof(LevelDef)); +} + + +static void DrawLevel(void) +{ + int f; + + for(f=0;f<level.no;f++) + if (level.draw[f]) + { + level.v[f].pos.x+=shipxi; + level.v[f].pos.y+=shipyi; + + switch(level.type[f]) + { + case MOUNTAIN: + break; + case ASTEROIDCW: + if ((level.v[f].ang+=20)>3599) + level.v[f].ang-=3600; + break; + case ASTEROIDACW: + if ((level.v[f].ang-=20)<0) + level.v[f].ang+=3600; + break; + case PAD: + break; + case MINE: + break; + } + + DrawObject(&level.v[f]); + } +} + + +/* ----------------------------------------------- VECTOR OBJECT +*/ +static int vecscale=0; + +#define SetScale(s) vecscale=(s) + + +static void LinePlot(int x,int y,ulong c,int col,int *retcoll) +{ + if ((x<0)||(x>=WINW)||(y<0)||(y>=WINH)) + return; + + POKEIMG(img,x,y,c); + + if (AT(coll_data,x,y)>*retcoll) + *retcoll=AT(coll_data,x,y); + + if (col!=CNONE) + AT(coll_data,x,y)=col; +} + + +static void Line(VecPt *p1,VecPt *p2, ulong c, int collcode, int *coll) +{ + int f; + int dx,dy,ix,iy,incrE,incrNE,d,x,y,ymode; + int p1x,p1y,p2x,p2y; + + p1x=(int)p1->x; + p1y=(int)p1->y; + p2x=(int)p2->x; + p2y=(int)p2->y; + + if ((p1x<0)&&(p2x<0)) + return; + + if ((p1x>=WINW)&&(p2x>=WINW)) + return; + + if ((p1y<0)&&(p2y<0)) + return; + + if ((p1y>=WINH)&&(p2y>=WINH)) + return; + + dx=p2x-p1x; + dy=p2y-p1y; + + ix=SGN(dx); + iy=SGN(dy); + + dx=ABS(dx); + dy=ABS(dy); + + if (dy>dx) + { + ymode=True; + d=dx*2-dy; + incrE=dx*2; + incrNE=(dx-dy)*2; + } + else + { + ymode=False; + d=dy*2-dx; + incrE=dy*2; + incrNE=(dy-dx)*2; + } + + x=p1x; + y=p1y; + + LinePlot(x,y,c,collcode,coll); + + if (ymode) + while(y!=p2y) + { + if (d<=0) + { + d+=incrE; + y+=iy; + } + else + { + d+=incrNE; + y+=iy; + x+=ix; + } + + LinePlot(x,y,c,collcode,coll); + } + else + while(x!=p2x) + { + if (d<=0) + { + d+=incrE; + x+=ix; + } + else + { + d+=incrNE; + y+=iy; + x+=ix; + } + + LinePlot(x,y,c,collcode,coll); + } +} + + +static void Rotate(VecPt *p1,VecPt *p2,int a) +{ + double dx,dy,dco,dsi; + + dco=co[a]; + dsi=si[a]; + + dx=p1->x; + dy=p1->y; + + p2->x=(dco*dx+(-dsi)*dy); + p2->y=(dsi*dx+dco*dy); +} + + +static void Scale(VecPt *p) +{ + p->x/=vecscale; + p->y/=vecscale; +} + + +static void DrawObject(VecObject *o) +{ + int f; + + if (o->ang) + for(f=0;f<o->no_pt;f++) + { + Rotate((o->pt)+f,(o->work)+f,o->ang); + o->work[f].x+=o->pos.x; + o->work[f].y+=o->pos.y; + + if (vecscale) + Scale(o->work+f); + + o->work[f].x+=WINWH; + o->work[f].y+=WINHH; + } + else + for(f=0;f<o->no_pt;f++) + { + o->work[f].x=o->pt[f].x+o->pos.x; + o->work[f].y=o->pt[f].y+o->pos.y; + + if (vecscale) + Scale(o->work+f); + + o->work[f].x+=WINWH; + o->work[f].y+=WINHH; + } + + for(f=0;f<o->no_ln;f++) + { + o->coll[f]=CNONE; + Line(&o->work[o->ln[f].p1], + &o->work[o->ln[f].p2], + o->colour, + o->collcode, + &o->coll[f]); + } +} + + +/* ----------------------------------------------- PARTICLE ROUTINES +*/ +static void ClearParticles(void) +{ + Particle *p; + int f; + + for(f=0;f<NOPLIST;f++) + { + while(p=p_head[f]) + { + p_head[f]=p->next; + free(p); + } + p_tail[f]=NULL; + } +} + + +static void AddParticle(int ang,int rad,int list,int weight) +{ + Particle *new; + + if (new=(Particle *)malloc(sizeof(Particle))) + { + new->next=NULL; + + switch(list) + { + case PSHORT: + new->life=3; + break; + + case PMID: + new->life=7; + break; + + case PLONG: + new->life=20; + break; + + case PULTRA: + new->life=40; + break; + + default: + free(new); + return; + } + + new->p.x=-(double)rad*si[ang]; + new->p.y=(double)rad*co[ang]; + new->i.x=-si[ang]/weight; + new->i.y=co[ang]/weight; + + if (p_head[list]) + { + p_tail[list]->next=new; + p_tail[list]=new; + } + else + p_head[list]=p_tail[list]=new; + } +} + + +static void DrawParticles(void) +{ + Particle *p; + int f; + + for(f=0;f<NOPLIST;f++) + { + while ((p=p_head[f])&&(!p->life)) + { + if (!(p_head[f]=p->next)) + p_tail[f]=NULL; + free(p); + } + + p=p_head[f]; + while(p) + { + p->life--; + p->p.x+=p->i.x+shipxi; + p->p.y+=p->i.y+shipyi; + if (vecscale) + Plot(WINWH+(int)p->p.x/vecscale, + WINHH+(int)p->p.y/vecscale,RNDCOL); + else + Plot(WINWH+(int)p->p.x,WINHH+(int)p->p.y,RNDCOL); + p=p->next; + } + } +} + + +static int NoParticles(void) +{ + int f,ret; + + ret=True; + + for(f=0;f<NOPLIST;f++) + ret&=!p_head[f]; + + return ret; +} + + +static void Explosion(int n,int p) +{ + int f; + + for(f=0;f<n;f++) + AddParticle(RND(3600),RND(20),p,1); +} + + +/* ----------------------------------------------- BACKDROP ROUTINES +*/ +static void InitBdrop(void) +{ + int f; + + for(f=0;f<NOBDSTAR;f++) + { + bdrop[f].x=(double)RND(WINW); + bdrop[f].y=(double)RND(WINH); + } +} + + +static void DrawBdrop(void) +{ + int f; + + for(f=0;f<NOBDSTAR;f++) + { + if (vecscale) + { + bdrop[f].x+=shipxi/vecscale; + bdrop[f].y+=shipyi/vecscale; + } + else + { + bdrop[f].y+=shipyi; + bdrop[f].x+=shipxi; + } + + if (bdrop[f].x<0.0) + bdrop[f].x+=(double)WINW; + + if (bdrop[f].x>=(double)WINW) + bdrop[f].x-=(double)WINW; + + + if (bdrop[f].y<0.0) + bdrop[f].y+=(double)WINH; + + if (bdrop[f].y>=(double)WINH) + bdrop[f].y-=(double)WINH; + + Plot((int)bdrop[f].x,(int)bdrop[f].y,YELLOW); + } +} + + +/* ----------------------------------------------- TITLE SCREEN +*/ +static void DoDebugMenu(void) +{ + static int debug_menu=False; + KeySym k; + int f; + + if ((k=RawKey())=='r') + debug_menu=!debug_menu; + + if (debug_menu) + { + Cls (); + + for(f=0;f<128;f++) + XIprintf(img,CH((f%40)*8,(f/40)*8),CYAN,"%c",f); + + XIprintf(img,CH(0,150),CYAN, + "(y/u) LEVEL %2d : %s",LEVEL,levdata[LEVEL].name); + XIprintf(img,CH(0,160),CYAN, + "(s) SHOW VECTOR : %5d",SHOWVEC); + XIprintf(img,CH(0,170),CYAN, + "(f) FONT : %5d",FONT); + XIprintf(img,CH(0,180),CYAN, + "(h) SCORE : %5d",SCORE); + XIprintf(img,CH(0,190),CYAN, + "(l) FUEL : %5d",FUEL); + + XIprintf(img,CH(0,100),CYAN,"(x) SET TO DEFAULTS"); + XIprintf(img,CH(0,108),CYAN,"(o) SET TO DEBUG"); + XIprintf(img,CH(0,116),CYAN,"(w) SET TO SMALLER DEBUG"); + + switch(k) + { + case 'u': + if (--LEVEL==-1) + LEVEL=no_levels-1; + break; + case 'y': + if (++LEVEL==no_levels) + LEVEL=0; + break; + case 'f': + FONT^=1; + XISetFont(FONT); + break; + case 'h': + SCORE=hisc[0].score; + break; + case 'l': + if ((FUEL+=500)>10000) + FUEL=0; + break; + case 's': + SHOWVEC=!SHOWVEC; + break; + case 'x': + SCORE=0; + FUEL=500; + break; + case 'o': + FUEL=10000; + SCORE=hisc[0].score; + break; + case 'w': + FUEL=100; + SCORE=hisc[0].score; + break; + } + } +} + + +static XFuncControl ProcessTitle(void) +{ + static unsigned int ctr=0; + char s[80]; + int f,k; + + ctr++; + + Cls(); + + for(f=0;f<10;f++) + Centre(15+f,((ctr+f)%(NOCOLS-2))+2,"LUNAR LANDER"); + + Centre(25,WHITE,"LUNAR LANDER"); + + Centre(45,RED,level_set); + Centre(46,YELLOW,level_set); + Centre(47,WHITE,level_set); + + if ((ctr/100)%2) + { + Centre(70,RNDCOL,"TOP PILOTS"); + for(f=0;f<NOHI;f++) + { + sprintf(s,"%2d %-3s %8d %3d", + f+1,hisc[f].name,hisc[f].score,hisc[f].no); + Centre(95+f*10,WHITE,s); + } + } + else + { + Centre(100,RED,"PRESENTED BY"); + Centre(115,RED,"NODDYBOX '95 - '05"); + Centre(130,RED,"www.noddybox.demon.co.uk"); + } + + if ((ctr/10)%2) + Centre(180,WHITE,"PRESS THRUST TO PLAY"); + + DoDebugMenu(); + + Update(); + + k=GetKey(); + + if (k==THRUST) + return XFUNCSTOP; + else if (k==QUIT) + { + quit=True; + return XFUNCSTOP; + } + else + return XFUNCCONT; +} + + +/* ----------------------------------------------- INTRO LEVEL +*/ + +static XFuncControl ProcessIntro(void) +{ + char s[80]; + + Cls(); + + sprintf(s,"Level %d",lev+1); + Centre(45,RED,s); + Centre(46,YELLOW,s); + Centre(47,WHITE,s); + + sprintf(s,"%s",levdata[lev].name); + Centre(64,RED,s); + Centre(65,RED,s); + Centre(66,YELLOW,s); + Centre(67,WHITE,s); + + Update(); + + if (GetKey()!=NONE) + { + ClearKeys(); + return XFUNCSTOP; + } + + return XFUNCCONT; +} + + +/* ----------------------------------------------- PLAY LEVEL +*/ +static void CheckCollisions(int *land,int *dead) +{ + int f,r; + + *dead=False; + *land=False; + + if ((lander.coll[3]==CPAD)&&(lander.coll[4]==CPAD)) + { + if ((DABS(shipxi)<=0.7)&&(shipyi>-2.0)) + { + *land=True; + landing++; + score+=fuel; + fuel+=200; + lev=(lev+1)%no_levels; + do_intro=True; + + for(r=0;r<500;r++) + { + AddParticle(2700-RND(500),RND(10)+10,PULTRA,1); + AddParticle(900+RND(500),RND(10)+10,PULTRA,1); + } + + shipxi=0.0; + shipyi=0.0; + return; + } + else + { + for(r=0;r<NOPLIST;r++) + Explosion(200,r); + *dead=True; + if ((fuel-=50)<0) + fuel=0; + shipxi=0.0; + shipyi=0.0; + return; + } + } + + for(f=0;f<lander.no_ln;f++) + if ((lander.coll[f]==CMOUNTAIN)||(lander.coll[f]==CASTEROID)|| + (lander.coll[f]==CPAD)) + { + for(r=0;r<NOPLIST;r++) + Explosion(200,r); + *dead=True; + if ((fuel-=50)<0) + fuel=0; + shipxi=0.0; + shipyi=0.0; + return; + } + + for(f=0;f<lander.no_ln;f++) + if (ISCMINE(lander.coll[f])) + { + damage++; + + for(r=0;r<level.no;r++) + if (level.v[r].collcode==lander.coll[f]) + { + level.draw[r]=False; + r=level.no; + } + + Explosion(100,PMID); + return; + } +} + + +static XFuncControl ProcessGame(void) +{ + static int first=True; + static int scale=2; + static int ctr=0; + static int dead,landed; + int f,na,k; + char *dmg; + + if (first) + { + ctr=0; + first=False; + SetScale(scale); + dead=False; + landed=False; + } + + ctr++; + + k=GetKey(); + + if (Paused(k)) + return XFUNCCONT; + + Cls(); + + /* Process movement + */ + if ((!dead)&&(!landed)) + { + shipyi-=GRAVITY; + + if ((keymap[FINE_LEFT])&&(damage<DAMAGE_RIGHT)) + { + if (fuel) + { + if ((lander.ang-=20)<0) + lander.ang+=3600; + + for(f=0;f<50;f++) + { + na=(lander.ang+2600+RND(200))%3600; + AddParticle(na,8+RND(3),PLONG,10); + } + } + } + else if ((keymap[FINE_RIGHT])&&(damage<DAMAGE_LEFT)) + { + if (fuel) + { + if ((lander.ang+=20)>3599) + lander.ang-=3600; + + for(f=0;f<5;f++) + { + na=(lander.ang+800+RND(200))%3600; + AddParticle(na,8+RND(3),PSHORT,10); + } + } + } + else if ((keymap[LEFT])&&(damage<DAMAGE_RIGHT)) + { + if (fuel) + { + if ((lander.ang-=80)<0) + lander.ang+=3600; + + for(f=0;f<5;f++) + { + na=(lander.ang+2600+RND(200))%3600; + AddParticle(na,8+RND(3),PMID,10); + } + } + } + else if ((keymap[RIGHT])&&(damage<DAMAGE_LEFT)) + { + if (fuel) + { + if ((lander.ang+=80)>3599) + lander.ang-=3600; + + for(f=0;f<5;f++) + { + na=(lander.ang+800+RND(200))%3600; + AddParticle(na,8+RND(3),PMID,10); + } + } + } + + if ((keymap[THRUST])&&(fuel>0)&&(damage<DAMAGE_MAIN)) + { + shipxi-=JET*si[lander.ang]; + shipyi+=JET*co[lander.ang]; + for(f=0;f<10;f++) + { + na=(lander.ang+3400+RND(400))%3600; + AddParticle(na,11+RND(5),RND(PLONG),10); + } + fuel--; + } + + if (shipxi<-MAXGRAV) + shipxi=-MAXGRAV; + + if (shipxi>MAXGRAV) + shipxi=MAXGRAV; + + if (shipyi<-MAXGRAV) + shipyi=-MAXGRAV; + + if (shipyi>MAXGRAV) + shipyi=MAXGRAV; + } + + DrawBdrop(); + DrawParticles(); + DrawLevel(); + + if (!dead) + { + DrawObject(&lander); + + if ((ctr/10)%2) + { + if (damage==DAMAGE_NONE) + dmg=NULL; + else if (damage==DAMAGE_LEFT) + dmg="LEFT THRUSTER DAMAGED"; + else if (damage==DAMAGE_RIGHT) + dmg="BOTH SIDE THRUSTERS DAMAGED"; + else + dmg="ALL THRUSTERS DAMAGED!"; + + if(dmg) + Centre(48,WHITE,dmg); + } + } + + XIprintf(img,CH(0,0),WHITE,"Fuel : %4d",fuel); + + if (SHOWVEC) + XIprintf(img,CH(0,WINH-8),WHITE,"xi: %2.3f yi: %2.3f ang: %4d", + shipxi,shipyi,lander.ang); + + /* Check collisions + */ + if ((!landed)&&(!dead)) + CheckCollisions(&landed,&dead); + + Update(); + + if (k==SCALE_UP) + { + if (--scale<2) + scale=0; + + InitBdrop(); + SetScale(scale); + } + + if (k==SCALE_DOWN) + { + if (scale) + scale++; + else + scale=2; + + InitBdrop(); + SetScale(scale); + } + + if (k==QUIT) + { + score=-1; + fuel=0; + first=True; + return XFUNCSTOP; + } + + if ((dead)&&(NoParticles())) + { + first=True; + return XFUNCSTOP; + } + + if ((landed)&&(NoParticles())) + { + first=True; + return XFUNCSTOP; + } + + return XFUNCCONT; +} + + +/* ----------------------------------------------- PAUSE CODE +*/ +static void DoPause(void) +{ + static unsigned int c=0; + + c++; + + if ((c/10)%2) + Centre(WINH/4,WHITE,"PAUSED"); + else + Centre(WINH/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 ctr=0; + + Cls (); + + ctr++; + + if ((ctr/10)%2) + Centre(120,WHITE,"GAME OVER"); + + Update(); + + 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<NOHI;f++) + read(fd,hisc+f,sizeof(HiSc)); + + close(fd); +} + + +static void WriteScores(void) +{ + int fd,f; + + if ((fd=open(HISCFILE,O_WRONLY|O_CREAT|O_TRUNC,0777))==-1) + { + fprintf(stderr,"Failed to write hiscores!\n"); + return; + } + + for(f=0;f<NOHI;f++) + write(fd,hisc+f,sizeof(HiSc)); + + close(fd); +} + + +static XFuncControl ProcessHiScore(void) +{ + static char *hisc_let="ABCDEFGHIJKLMNOPQRSTUVWXYZ.\177"; + static char *let; + static int len=0; + static char name[4]; + static int first=True; + static int pos=0; + static int bounce=0; + char s[80]; + int f,done=False; + + if (first) + { + len=0; + strcpy(name,""); + let=hisc_let; + bounce=0; + + for(f=NOHI-1;f>-1;f--) + if (hisc[f].score<score) + pos=f; + + if (pos<NOHI-1) + for(f=NOHI-1;f>pos;f--) + hisc[f]=hisc[f-1]; + + strcpy(hisc[pos].name,""); + hisc[pos].score=score; + hisc[pos].no=landing; + first=False; + } + + Cls(); + + Centre(16,WHITE,"CONGRATULATIONS!"); + Centre(30,YELLOW,"ENTER YOUR INITIALS"); + Centre(40,YELLOW,"FOR THE TOP PILOTS"); + + hisc[pos].name[len]=*let; + hisc[pos].name[len+1]=0; + + for(f=0;f<NOHI;f++) + { + sprintf(s,"%2d %-3s %8d %3d", + f+1,hisc[f].name,hisc[f].score,hisc[f].no); + Centre(110+f*10,(f==pos) ? (CYAN) : (RED),s); + } + + if ((keymap[LEFT])&&(!bounce)) + { + bounce=30; + if (let==hisc_let) + let=hisc_let+strlen(hisc_let)-1; + else + let--; + } + + if ((keymap[RIGHT])&&(!bounce)) + { + bounce=30; + if (!(*++let)) + let=hisc_let; + } + + switch (GetKey()) + { + case THRUST: + len++; + break; + case NONE: + bounce=0; + break; + } + + if (bounce) + bounce--; + + Update(); + + if (len==3) + { + first=True; + return XFUNCSTOP; + } + else + return XFUNCCONT; +} + + +/* ----------------------------------------------- LEVEL READING CODE +*/ +static int err(const char *p) +{ + fprintf(stderr,"%s\n",p); + exit(1); +} + + +static const char *FName(const char *p) +{ + static char s[1024]; + + strcpy(s,LEVELDIR); + strcat(s,p); + + return s; +} + + +static char *GetLine(FILE *fp) +{ + static char s[1204]; + + fgets(s,1024,fp); + + if (s[strlen(s)-1]=='\n') + s[strlen(s)-1]='\0'; + + return s; +} + + +static int GetNum(FILE *fp,int *x,int *y) + +{ + sscanf(GetLine(fp),"%d,%d",x,y); +} + + +static void ReadLevels(void) +{ + FILE *fp; + char s[1024],*p,*name[MAXLEVEL]; + int f,x,y,poly_no,pt_no,l,no_mine; + + if (!(fp=fopen(FName("lunar.desc"),"r"))) + err("Couldn't open lunar.desc"); + + level_set=strdup(GetLine(fp)); + no_levels=atoi(GetLine(fp)); + + if ((no_levels<1)||(no_levels>MAXLEVEL)) + err("illegal no of levels"); + + for(f=0;f<no_levels;f++) + if (!(name[f]=strdup(GetLine(fp)))) + err("not enough level names in lunar.desc"); + + fclose(fp); + + for(f=0;f<no_levels;f++) + { + if (!(fp=fopen(FName(name[f]),"r"))) + { + sprintf(s,"Couldn't open lunar level file '%s'",name[f]); + err(s); + } + + levdata[f].name=strdup(GetLine(fp)); + poly_no=0; + no_mine=0; + + p=GetLine(fp); + + while (!feof(fp)) + { + switch(*p) + { + case 'M': + levdata[f].type[poly_no]=MOUNTAIN; + levdata[f].v[poly_no].collcode=CMOUNTAIN; + levdata[f].v[poly_no].colour=WHITE; + break; + case 'X': + levdata[f].type[poly_no]=ASTEROIDCW; + levdata[f].v[poly_no].collcode=CASTEROID; + levdata[f].v[poly_no].colour=WHITE; + break; + case 'Y': + levdata[f].type[poly_no]=ASTEROIDACW; + levdata[f].v[poly_no].collcode=CASTEROID; + levdata[f].v[poly_no].colour=WHITE; + break; + case 'P': + levdata[f].type[poly_no]=PAD; + levdata[f].v[poly_no].collcode=CPAD; + levdata[f].v[poly_no].colour=GREEN; + break; + case 'O': + levdata[f].type[poly_no]=MINE; + levdata[f].v[poly_no].collcode=CMINE(no_mine++); + levdata[f].v[poly_no].colour=RED; + break; + } + + levdata[f].v[poly_no].ang=0; + levdata[f].draw[poly_no]=True; + + GetNum(fp,&x,&y); + + levdata[f].v[poly_no].pos.x=(double)x; + levdata[f].v[poly_no].pos.y=(double)y; + + GetNum(fp,&x,&y); + + pt_no=0; + + while((x!=-666)||(y!=-666)) + { + levdata[f].v[poly_no].pt[pt_no].x=(double)x; + levdata[f].v[poly_no].pt[pt_no].y=(double)y; + + pt_no++; + GetNum(fp,&x,&y); + } + + levdata[f].v[poly_no].no_pt=pt_no; + levdata[f].v[poly_no].no_ln=pt_no; + + for(l=0;l<pt_no;l++) + { + levdata[f].v[poly_no].ln[l].p1=l; + levdata[f].v[poly_no].ln[l].p2=(l+1)%pt_no; + } + + poly_no++; + p=GetLine(fp); + + if (poly_no==MAXLEVPOLY) + { + sprintf(s,"Too many polygons in '%s'",name[f]); + err(s); + } + } + + levdata[f].no=poly_no; + + fclose(fp); + } + + for(f=0;f<no_levels;f++) + free(name[f]); +} |