summaryrefslogtreecommitdiff
path: root/lunar.c
diff options
context:
space:
mode:
Diffstat (limited to 'lunar.c')
-rw-r--r--lunar.c1843
1 files changed, 1843 insertions, 0 deletions
diff --git a/lunar.c b/lunar.c
new file mode 100644
index 0000000..33d7041
--- /dev/null
+++ b/lunar.c
@@ -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]);
+}