#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "cg6.h" #include "font.h" #include "bitmap.h" #include "bitmaps.h" static const char *fbpath = "/dev/cgsix0"; static const char *kbpath = "/dev/kbd1"; static const char *mspath = "/dev/mouse1"; #define MOVE_SCALE .03125 #define how_many(x,y) (((x)+(y)-1)/(y)) #define round_up(x,y) (how_many(x,y)*(y)) static int fbfd; static int kbfd; static int msfd; static volatile struct cg6fb *fb; static volatile struct brooktree *bt; static volatile unsigned char *vram; #define RPSIZE 64 static unsigned long int randpool[RPSIZE]; static int rp; static int rmp; static volatile unsigned long int hardtime; static unsigned long int softtime; static unsigned long int drawtime; static unsigned long int queuetime; #define EVQ_SIZE 64 /* must be a power of two */ static int evq[EVQ_SIZE]; static int evqh; static int evqt; #define EV_TYPE 0x000f0000 #define EVT_NIL 0x00000000 #define EVT_KBD 0x00010000 #define EVK_CODE 0x0000007f /* must be low bits */ #define EVK_UPDN 0x00000080 #define EVK_UP 0x00000080 #define EVK_DN 0x00000000 #define EVT_TICK 0x00020000 #define EVT_DRAW 0x00030000 #define EVT_MOUSE 0x00040000 #define EVM_TYPE 0x0000c000 #define EVM_T_DX 0x00004000 #define EVM_T_DY 0x00008000 #define EVM_T_BUT 0x0000c000 #define EVM_SIGN 0x00002000 #define EVM_S_NEG 0x00002000 #define EVM_S_POS 0x00000000 #define EVM_DELTA 0x00001fff /* must be low bits */ #define EVM_UPDN 0x00000004 #define EVM_UP 0x00000004 #define EVM_DN 0x00000000 #define EVM_BUTID 0x00000003 #define EVM_BUT_L 0x00000000 #define EVM_BUT_M 0x00000001 #define EVM_BUT_R 0x00000002 #define MAXX 475 #define MAXY 320 #define MARGIN 10 #define SCORELINE 20 #ifdef MAGNIFY #define XOFF ((1152-(2*MAXX))/2) #define YOFF (((900-(2*(MAXY+SCORELINE)))/2)+(2*SCORELINE)) #define MAG 2 #else #define XOFF ((1152-MAXX)/2) #define YOFF (((900-(MAXY+SCORELINE))/2)+SCORELINE) #define MAG 1 #endif typedef struct mobj MOBJ; typedef struct fship FSHIP; typedef struct fsops FSOPS; typedef struct fuse FUSE; typedef struct shapechg SHAPECHG; typedef struct expl EXPL; typedef struct mine MINE; typedef struct pest PEST; typedef struct trimpet TRIMPET; typedef struct menace MENACE; typedef struct tentawarble TENTAWARBLE; struct tentawarble { FSHIP *ship; int stillfor; int diving; int divetime; } ; struct menace { FSHIP *ship; int laser; #define ML_ACTIVE 0x00000001 #define ML_HV 0x00000002 #define ML_H 0x00000000 #define ML_V 0x00000002 #define ML_IO 0x00000004 #define ML_OUT 0x00000000 #define ML_IN 0x00000004 #define ML_NO 0 int llen; int lmax; } ; struct trimpet { FSHIP *ship; int hibernating; FUSE *hibfuse; } ; struct pest { FSHIP *ship; float a; float da; } ; struct shapechg { int max; int ticks; int x; } ; struct fuse { unsigned long int when; void (*fn)(void *); void *arg; } ; struct mobj { float x; float y; float vx; float vy; unsigned int flags; int bmx; BITMAP *bitmap; FUSE *chgfuse; } ; struct mine { MOBJ obj; int type; #define MINE_STATIC 1 #define MINE_PEST 2 } ; struct expl { EXPL *link; MOBJ obj; } ; struct fsops { void (*initnew)(FSHIP *); void (*cleanup)(FSHIP *); void (*tickship)(FSHIP *); void (*tickshot)(MOBJ *); void (*drawship)(FSHIP *); void (*drawshot)(MOBJ *); void (*behit)(FSHIP *, int); #define BH_SHOT 1 #define BH_SHIP 2 int points; } ; struct fship { FSHIP *link; FSOPS *ops; MOBJ obj; int refs; int nshots; int maxnshots; MOBJ *shots; void *private; } ; #define FSF_DEAD 0x00000001 #define FSF_HIT 0x00000006 #define FSF_HIT_P 0x00000002 #define FSF_HIT_E 0x00000004 #define FSF_EXPL 0x00000008 static int debugk = 0; static FSHIP *player; static FSHIP *fships; static int level; static int exiting; static int player_still; static int flash; static volatile int paused; #define PAUSE_TABKEY 0x00000001 static EXPL *expls; static int ncrystals; static int acrystals; static MOBJ **crystals; static int nbombs; static int abombs; static MOBJ **bombs; static int nmines; static int amines; static MINE **mines; static int state; #define STATE_NASCENT 0 #define STATE_WAITBEGIN 1 #define STATE_PLAYING 2 #define STATE_EXPLODED 3 #define STATE_BONUS 4 static int exploded; static int invul; static int score; static int nextlife; static int lives; static int smartbombs; static int wavetime; static int bonustime; static int bonusphase; static int bonus_score; static MOBJ emitter_l_obj; static MOBJ emitter_r_obj; static MOBJ exit_l_obj; static MOBJ exit_r_obj; static MOBJ exit_closure_obj; static float exit_x; static float exit_vx; static int fuses_n; static int fuses_a; static FUSE **fuses; static int wantrender; static int drawbit; static const unsigned char cmap[8][3] = { { 0, 0, 0 }, { 255, 0, 0 }, { 0, 255, 0 }, { 255, 255, 0 }, { 0, 0, 255 }, { 255, 0, 255 }, { 0, 255, 255 }, { 255, 255, 255 } }; static FSOPS fsops_player; static FSOPS fsops_annoyer; static FSOPS fsops_worrier; static FSOPS fsops_pest; static FSOPS fsops_dumple; static FSOPS fsops_trimpet; static FSOPS fsops_shrapwarden; static FSOPS fsops_parasite; static FSOPS fsops_zarklephaser; static FSOPS fsops_husket; static FSOPS fsops_bane; static FSOPS fsops_menace; static FSOPS fsops_tentawarble; static __inline__ float sqrf(float) __attribute__((__const__)); static __inline__ float sqrf(float f) { return(f*f); } static void setcmap(int bit) { int i; int v; bt->addr = 0; for (i=0;i<=077;i++) { v = (bit ? (i >> 3) : i) & 7; if ((flash & 1) & (v == 0)) v = 7; bt->cmap = cmap[v][0] * 0x01000000; bt->cmap = cmap[v][1] * 0x01000000; bt->cmap = cmap[v][2] * 0x01000000; } } static void drain(void) { while (fb->s & 0x10000000) ; } static void draw(void) { while ((fb->draw & 0xa0000000) == 0xa0000000) ; } static void fbrlines(int fg, int n, int x0, int y0, ...) { va_list ap; int x; int y; va_start(ap,y0); fb->fg = fg; fb->aliney = YOFF + (MAG * y0); fb->alinex = XOFF + (MAG * x0); for (;n>0;n--) { x = va_arg(ap,int); y = va_arg(ap,int); fb->rliney = (MAG * y); fb->rlinex = (MAG * x); draw(); } va_end(ap); } static void fbrect_(int fg, int x1, int y1, int x2, int y2) { fb->fg = fg; fb->arecty = y1; fb->arectx = x1; fb->arecty = y2; fb->arectx = x2; draw(); } static void fbrect(int fg, int x1, int y1, int x2, int y2) { fbrect_(fg,XOFF+(MAG*x1),YOFF+(MAG*y1),XOFF+(MAG*x2),YOFF+(MAG*y2)); } #ifdef MAGNIFY static unsigned long int exp16to32(unsigned short int sv) { unsigned long int v; v = sv * 0x00010001; v = ((v & 0x0000ff00) << 8) | ((v & 0x00ff0000) >> 8) | (v & 0xff0000ff); v = ((v & 0x00f000f0) << 4) | ((v & 0x0f000f00) >> 4) | (v & 0xf00ff00f); v = ((v & 0x0c0c0c0c) << 2) | ((v & 0x30303030) >> 2) | (v & 0xc3c3c3c3); v = ((v & 0x22222222) << 1) | ((v & 0x44444444) >> 1) | (v & 0x99999999); return(v); } #endif static void fbbitmap(int fg, BITMAP *bm, int bmx, int x, int y) { const unsigned long int *bp; int i; bp = bm->bits; if (bm->nvariants > 1) { if (bm->vmap) bmx = bm->vmap[bmx]; bp += bm->h * bmx; } x -= bm->xhot; y -= bm->yhot; #ifdef MAGNIFY if (bm->shadow & 0xffff0000) { fb->fg = fg; fb->x0 = XOFF + (2 * x); fb->x1 = XOFF + (2 * x) + 31; fb->y0 = YOFF + (2 * y); fb->incx = 0; fb->incy = 1; for (i=bm->h;i>0;i--) { unsigned long int v; v = exp16to32(*bp++>>16); fb->font = v; fb->font = v; } bp -= bm->h; } if (bm->shadow & 0x0000ffff) { fb->fg = fg; fb->x0 = XOFF + (2 * x) + 32; fb->x1 = XOFF + (2 * x) + 63; fb->y0 = YOFF + 2 * y; fb->incx = 0; fb->incy = 1; for (i=bm->h;i>0;i--) { unsigned long int v; v = exp16to32(*bp++&0x0000ffff); fb->font = v; fb->font = v; } } #else fb->fg = fg; fb->x0 = XOFF + x; fb->x1 = XOFF + x + 31; fb->y0 = YOFF + y; fb->incx = 0; fb->incy = 1; for (i=bm->h;i>0;i--) fb->font = *bp++; #endif } static void fbmobj(int fg, MOBJ *o) { fbbitmap(fg,o->bitmap,o->bmx,o->x,o->y); } static void setupfb(void) { void *mrv; struct fbgattr a; int i; fbfd = open(fbpath,O_RDWR,0); if (fbfd < 0) { fprintf(stderr,"%s: %s: %s\n",__progname,fbpath,strerror(errno)); exit(1); } if ( (ioctl(fbfd,FBIOGATTR,&a) < 0) || (a.fbtype.fb_type != FBTYPE_SUNFAST_COLOR) || (a.fbtype.fb_width != 1152) || (a.fbtype.fb_height != 900) || (a.fbtype.fb_depth != 8) ) { close(fbfd); fprintf(stderr,"%s: %s: not a cg6\n",__progname,fbpath); exit(1); } mrv = mmap(0,0x16000+a.fbtype.fb_size,PROT_READ|PROT_WRITE,MAP_SHARED,fbfd,0x70000000); if (mrv == MAP_FAILED) { fprintf(stderr,"%s: can't mmap %s: %s\n",__progname,fbpath,strerror(errno)); exit(1); } fb = mrv; bt = (void *)(0x2000+(unsigned char *)mrv); vram = 0x16000 + (unsigned char *)mrv; bt->addr = 0; for (i=0;i<256;i++) { bt->cmap = ((cmap[i&7][0] >> 1) + 63) * 0x01000000; bt->cmap = ((cmap[i&7][1] >> 1) + 63) * 0x01000000; bt->cmap = ((cmap[i&7][2] >> 1) + 63) * 0x01000000; } drawbit = 0; fb->bg = 0; fb->pixelm = ~0; fb->s = 0; fb->mode = 0x00229540; /* not all bits known */ fb->alu = 0xc0000000 /* GX_PLANE_MASK */ | 0x20000000 /* GX_PIXEL_ONES */ | 0x00800000 /* GX_ATTR_SUPP (?) */ | 0x00000000 /* GX_RAST_BOOL (?) */ | 0x00000000 /* GX_PLOT_PLOT (?) */ | 0x08000000 /* GX_PATTERN_ONES */ | 0x01000000 /* GX_POLYG_OVERLAP */ | 0x0000ff00 /* ALU = set to fg color */ ; fb->clip = 0; fb->offx = 0; fb->offy = 0; fb->clipminx = 0; fb->clipminy = 0; fb->clipmaxx = 1151; fb->clipmaxy = 899; drain(); fb->pm = ~0; fbrect_(0x80,0,0,1151,899); drain(); fbrect(0,0,-SCORELINE,MAXX-1,MAXY-1); drain(); fb->clipminx = 0; fb->clipminy = 0; fb->clipmaxx = 1151; fb->clipmaxy = 899; fb->pm = 0x07; fb->alu = 0xee22; fb->mode = 0x40000; } static void setupkb(void) { int type; int mode; kbfd = open(kbpath,O_RDWR,0); if (kbfd < 0) { fprintf(stderr,"%s: can't open %s: %s\n",__progname,kbpath,strerror(errno)); exit(1); } if ( (ioctl(kbfd,KIOCTYPE,&type) < 0) || (type != KB_SUN3) ) { close(kbfd); fprintf(stderr,"%s: %s isn't a type-3\n",__progname,kbpath); exit(1); } mode = 1; ioctl(kbfd,KIOCSDIRECT,&mode); mode = TR_UNTRANS_EVENT; ioctl(kbfd,KIOCTRANS,&mode); mode = 1; ioctl(kbfd,FIONBIO,&mode); } static void setupms(void) { int mode; msfd = open(mspath,O_RDWR,0); if (msfd < 0) { fprintf(stderr,"%s: can't open %s: %s\n",__progname,mspath,strerror(errno)); exit(1); } mode = 1; ioctl(msfd,FIONBIO,&mode); } static void handle_sigalrm(int sig __attribute__((__unused__))) { if (! paused) hardtime ++; } static void setuptimer(void) { struct itimerval itv; struct sigaction sa; hardtime = 0; softtime = 0; drawtime = 0; queuetime = 0; sa.sa_handler = handle_sigalrm; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGALRM,&sa,0); itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 10000; itv.it_interval = itv.it_value; setitimer(ITIMER_REAL,&itv,0); } static void initevq(void) { evqh = 0; evqt = 0; } static unsigned long int rnd(void) { int prp; prp = rp++; if (rp >= RPSIZE) rp = 0; randpool[prp] += randpool[rp]; return(randpool[prp]); } static void initrandom(void) { int i; struct timeval tv; randpool[0] = 1; randpool[1] = getpid(); gettimeofday(&tv,0); randpool[2] = tv.tv_sec; randpool[3] = tv.tv_usec; for (i=4;i> 1) ^ ((v & 1) ? 0xedb88320 : 0); randpool[rmp] = v; rmp ++; if (rmp >= RPSIZE) rmp = 0; } } static void fship_setmaxnshots(FSHIP *fs, int n) { free(fs->shots); fs->shots = n ? malloc(n*sizeof(MOBJ)) : 0; fs->maxnshots = n; } static void mobj_init(MOBJ *o) { o->x = 0; o->y = 0; o->vx = 0; o->vy = 0; o->flags = 0; o->bmx = 0; o->bitmap = 0; o->chgfuse = 0; } static FSHIP *makefship(FSOPS *ops) { FSHIP *fs; fs = malloc(sizeof(FSHIP)); fs->maxnshots = 0; fs->shots = 0; fs->nshots = 0; mobj_init(&fs->obj); fs->ops = ops; fs->refs = 0; fs->private = 0; fs->link = fships; fships = fs; (*ops->initnew)(fs); return(fs); } static void mobj_move(MOBJ *o) { o->x += o->vx; o->y += o->vy; } static void mobj_limitxy(MOBJ *o) { if (o->x < MARGIN) o->x = MARGIN; else if (o->x >= MAXX-MARGIN) o->x = MAXX - MARGIN - .0001; if (o->y < MARGIN) o->y = MARGIN; else if (o->y >= MAXY-MARGIN) o->y = MAXY - MARGIN - .0001; } static int mobj_bouncexy(MOBJ *o) { int rv; rv = 0; if (o->x < MARGIN) { o->x = MARGIN; o->vx = - o->vx; rv = 1; } else if (o->x >= MAXX-MARGIN) { o->x = MAXX - MARGIN - .0001; o->vx = - o->vx; rv = 1; } if (o->y < MARGIN) { o->y = MARGIN; o->vy = - o->vy; rv = 1; } else if (o->y >= MAXY-MARGIN) { o->y = MAXY - MARGIN - .0001; o->vy = - o->vy; rv = 1; } return(rv); } static void incscore(int pts) { score += pts; while (score > nextlife) { lives ++; if (level < 11) nextlife += 600; else if (level < 26) nextlife += 1600; else nextlife += 3000; } } static void destroy_tickshot(MOBJ *s) { if ((s->x < 0) || (s->y < 0) || (s->x >= MAXX) || (s->y >= MAXY)) s->flags |= FSF_DEAD; } static void noshoot_tickshot(MOBJ *s __attribute__((__unused__))) { abort(); } static void noshoot_drawshot(MOBJ *o __attribute__((__unused__))) { abort(); } static void nocleanup_cleanup(FSHIP *fs __attribute__((__unused__))) { } static void justfree_cleanup(FSHIP *fs) { free(fs->private); } static void die_behit(FSHIP *fs, int how __attribute__((__unused__))) { fs->obj.flags |= FSF_DEAD; incscore(fs->ops->points); } static MOBJ *fship_getshot(FSHIP *fs) { MOBJ *s; if (fs->nshots >= fs->maxnshots) return(0); s = &fs->shots[fs->nshots++]; mobj_init(s); s->flags = fs->obj.flags ^ FSF_HIT; s->x = fs->obj.x; s->y = fs->obj.y; fs->refs ++; return(s); } static void fuse_bubble_up(void) { unsigned int x; unsigned int u; FUSE *fx; FUSE *fu; x = fuses_n - 1; fx = fuses[x]; while (x > 0) { u = (x-1) / 2; fu = fuses[u]; if (fx->when >= fu->when) break; fuses[x] = fu; x = u; } fuses[x] = fx; } static FUSE *add_fuse(int when, void (*fn)(void *), void *arg) { FUSE *f; f = malloc(sizeof(FUSE)); f->when = queuetime + when; f->fn = fn; f->arg = arg; if (fuses_n >= fuses_a) fuses = realloc(fuses,(fuses_a=fuses_n+6)*sizeof(FUSE *)); fuses[fuses_n++] = f; fuse_bubble_up(); return(f); } static void cancel_fuse(FUSE *f) { f->fn = 0; f->arg = 0; } static void mobj_chg(void *ov) { MOBJ *o; o = ov; o->bmx ++; if (o->bmx >= o->bitmap->nvariants) o->bmx = 0; o->chgfuse = add_fuse(o->bitmap->chg,mobj_chg,ov); } static void mobj_setbitmap(MOBJ *o, BITMAP *b) { o->bitmap = b; if (o->chgfuse) cancel_fuse(o->chgfuse); o->chgfuse = (b->nvariants > 1) ? add_fuse(b->chg,mobj_chg,o) : 0; } static void player_initnew(FSHIP *fs) { fship_setmaxnshots(fs,8); mobj_setbitmap(&fs->obj,&bitmap_player); } static void player_cleanup(FSHIP *fs __attribute__((__unused__))) { abort(); } static void set_exiting(void) { exiting = 1; player->obj.vx = exit_vx + ((player->obj.x > exit_x) ? -.03 : .03); player->obj.vy = .2; } static void clear_enemies(int getscore) { FSHIP *fs; int i; for (fs=fships;fs;fs=fs->link) { if (fs != player) { fs->obj.flags |= FSF_DEAD | FSF_EXPL; if (getscore) incscore(fs->ops->points); } for (i=fs->nshots-1;i>=0;i--) fs->shots[i].flags |= FSF_DEAD; } } static int touches(MOBJ *a, MOBJ *b) { int ax; int ay; int bx; int by; int as; int bs; int ay0; int ay1; int by0; int by1; int y; ay = a->y; by = b->y; ay -= a->bitmap->yhot; by -= b->bitmap->yhot; if ((ay+a->bitmap->h <= by) || (by+b->bitmap->h <= ay)) return(0); ax = a->x; bx = b->x; ax -= a->bitmap->xhot; bx -= b->bitmap->xhot; if ((ax+32 <= bx) || (bx+32 <= ax)) return(0); if (ax < bx) { as = 0; bs = bx - ax; } else { as = ax - bx; bs = 0; } if (((a->bitmap->shadow >> as) & (b->bitmap->shadow >> bs)) == 0) return(0); if (ay < by) { ay0 = by - ay; by0 = 0; } else { ay0 = 0; by0 = ay - by; } ay1 = ay + a->bitmap->h; by1 = by + b->bitmap->h; if (ay1 < by1) { by1 = b->bitmap->h - (by1 - ay1); ay1 = a->bitmap->h; } else { ay1 = a->bitmap->h - (ay1 - by1); by1 = b->bitmap->h; } for (y=ay1-ay0-1;y>=0;y--) { if ((a->bitmap->bits[ay0+y] >> as) & (b->bitmap->bits[by0+y] >> bs)) return(1); } return(0); } static int touchesrect(MOBJ *a, int x1, int y1, int x2, int y2) { BITMAP *b; unsigned long int m; int ax; int ay; int y; b = a->bitmap; if (debugk) { printf("touchesrect: r=[%d..%d]x[%d..%d] bm h%d hot(%d,%d) @ %d,%d: ", x1,x2,y1,y2,b->h,b->xhot,b->yhot,(int)a->x,(int)a->y); } if (x1 > x2) { int t; t = x1; x1 = x2; x2 = t; } if (y1 > y2) { int t; t = y1; y1 = y2; y2 = t; } ax = (int)a->x - b->xhot; x1 -= ax; x2 -= ax; if ((x2 < 0) || (x1 >= 31)) { if (debugk) printf("0 [x1=%d x2=%d]\n",x1,x2); return(0); } ay = (int)a->y - b->yhot; y1 -= ay; y2 -= ay; if ((y2 < 0) || (y1 >= b->h)) { if (debugk) printf("0 [y1=%d y2=%d]\n",y1,y2); return(0); } if (x1 < 0) x1 = 0; if (x2 > 31) x2 = 31; if ((x1 == 0) && (x2 == 31)) { m = 0xffffffff; } else { m = (~((~0UL) << (x2-x1+1))) << (31-x2); } if (! (b->shadow & m)) { if (debugk) printf("0 [shadow %08lx m %08lx]\n",b->shadow,m); return(0); } if (y1 < 0) y1 = 0; if (y2 >= b->h) y2 = b->h - 1; for (y=y1;y<=y2;y++) if (b->bits[y] & m) { if (debugk) printf("1 [bits[%d] %08lx m %08lx]\n",y,b->bits[y],m); return(1); } if (debugk) printf("0 [none m %08lx]\n",m); return(0); } static void mobj_cleanup(MOBJ *o) { if (o->chgfuse) cancel_fuse(o->chgfuse); o->bmx = 0; o->bitmap = 0; o->chgfuse = 0; } static void place_crystals(int n) { int i; MOBJ *c; while (ncrystals > 0) mobj_cleanup(crystals[--ncrystals]); if (n > acrystals) { crystals = realloc(crystals,n*sizeof(*crystals)); while (acrystals < n) crystals[acrystals++] = 0; } for (;n>0;n--) { c = crystals[ncrystals]; if (! c) { c = malloc(sizeof(MOBJ)); crystals[ncrystals] = c; mobj_init(c); } ncrystals ++; mobj_setbitmap(c,&bitmap_crystal); while (1) { c->x = MARGIN + (rnd() % (MAXX-(2*MARGIN))); c->y = MARGIN + (rnd() % (MAXY-(2*MARGIN))); if ( touches(c,&emitter_l_obj) || touches(c,&emitter_r_obj) || touches(c,&player->obj) ) continue; for (i=0;i= amines) { i = amines; amines = nmines + 16; mines = realloc(mines,amines*sizeof(MINE *)); for (;iobj); } m->type = type; nmines ++; return(m); } static void place_mines(int n) { int j; MINE *m; nmines = 0; for (;n>0;n--) { m = newmine(MINE_STATIC); mobj_setbitmap(&m->obj,&bitmap_mine); while (1) { m->obj.x = MARGIN + (rnd() % (MAXX-(2*MARGIN))); m->obj.y = MARGIN + (rnd() % (MAXY-(2*MARGIN))); if ( touches(&m->obj,&emitter_l_obj) || touches(&m->obj,&emitter_r_obj) || touches(&m->obj,&player->obj) ) continue; for (j=0;jobj,&mines[j]->obj)) goto again; /* continue 2 */ } for (j=0;jobj,crystals[j])) goto again; /* continue 2 */ } break; again:; } } } static void place_bombs(int n) { int i; MOBJ *b; while (nbombs > 0) mobj_cleanup(bombs[--nbombs]); if (n > abombs) { bombs = realloc(bombs,n*sizeof(*bombs)); while (abombs < n) bombs[abombs++] = 0; } for (;n>0;n--) { b = bombs[nbombs]; if (! b) { b = malloc(sizeof(MOBJ)); bombs[nbombs] = b; mobj_init(b); } nbombs ++; mobj_setbitmap(b,&bitmap_bomb); while (1) { b->x = MARGIN + (rnd() % (MAXX-(2*MARGIN))); b->y = MARGIN + (rnd() % (MAXY-(2*MARGIN))); if ( touches(b,&emitter_l_obj) || touches(b,&emitter_r_obj) || touches(b,&player->obj) ) continue; for (i=0;iobj)) goto again; /* continue 2 */ } for (i=0;i>1]; } else { o = ov[rnd()%(level>>1)]; } } else { o = ov[rnd()%12]; } fs = makefship(o); fs->obj.flags |= FSF_HIT_E; } static void schedule_emit(void *arg __attribute__((__unused__))) { int m; /* One-out-of value is three-piece linear: up to level 11, fixed at 512 levels 11-39, linear, 512 at 11, 256 at 39 levels 39-100, linear, 256 at 39, 1 at 100 levels 100 and up, 1 */ if (level < 11) m = 512; else if (level < 39) m = 512 - (((level-11)*128)/14); else if (level < 100) m = 256 - (((level-39)*255)/61); else m = 1; if ((rnd() % m) == 0) emit_enemy(); add_fuse(1,schedule_emit,0); } static void shapechange(void *sv) { SHAPECHG *s; s = sv; s->x ++; if (s->x >= s->max) s->x = 0; add_fuse(s->ticks,shapechange,sv); } static void set_exit_bitmap(void) { mobj_setbitmap(&exit_closure_obj,(ncrystals>0)?&bitmap_exit_closure:&bitmap_empty); } static void enterstate(int new) { if (new == state) return; switch (state) { } switch (new) { } state = new; } static void destroymine(int inx) { mobj_cleanup(&mines[inx]->obj); nmines --; if (inx != nmines) { MINE *t; t = mines[inx]; mines[inx] = mines[nmines]; mines[nmines] = t; } } static void clear_some_mines(void) { int i; for (i=nmines-1;i>=0;i--) { switch (mines[i]->type) { case MINE_STATIC: break; case MINE_PEST: destroymine(i); break; default: abort(); break; } } } static void changed_exit_x(void) { exit_l_obj.x = exit_x; exit_r_obj.x = exit_x; exit_closure_obj.x = exit_x; } static void reset_playbegin(void) { player->obj.x = MAXX / 2; player->obj.y = MAXY / 2; player->obj.vx = 0; player->obj.vy = 0; exit_x = MAXX / 2; changed_exit_x(); set_exit_bitmap(); exiting = 0; enterstate(STATE_WAITBEGIN); clear_enemies(0); clear_some_mines(); } static int ncrystals_for_level(int lev) { switch (lev) { case 1: return(10); break; case 2: return(12); break; case 3: case 4: return(15); break; case 5: case 6: return(20); break; case 7: case 8: return(20); break; } if (lev < 17) return(30); return(35); } static int nmines_for_level(int lev) { switch (lev) { case 1: case 2: return(0); break; case 3: return(2); break; case 4: case 5: return(5); break; } if (lev < 15) return(10); return(15); } static int nbombs_for_level(int lev) { if (lev < 3) return(0); if (lev < 11) return((rnd()%9)<(lev-2)); if (lev < 20) return(1+((rnd()%9)<(lev-12))); return(2); } static void setup_level(void) { exit_vx = (level < 26) ? 0 : (rnd() & 1) ? -.1 : .1; reset_playbegin(); place_crystals(ncrystals_for_level(level)); place_mines(nmines_for_level(level)); place_bombs(nbombs_for_level(level)); wavetime = 0; set_exit_bitmap(); } static void bonus_end(void *arg __attribute__((__unused__))) { level ++; setup_level(); state = STATE_WAITBEGIN; } static void bonus_addscore(void *arg __attribute__((__unused__))) { if (bonus_score) { bonus_score --; incscore(1); add_fuse(1,bonus_addscore,0); } else { add_fuse(50,bonus_end,0); } } static void bonus_countdown(void *arg __attribute__((__unused__))) { int max; max = 500 * (level+1); if (bonustime < max) { bonustime += 5; bonus_score ++; add_fuse(1,bonus_countdown,0); } else { bonustime = max; add_fuse(40,bonus_addscore,0); } } static void start_bonus_count(void *arg __attribute__((__unused__))) { if (bonustime > 500*(level+1)) { bonusphase = 2; add_fuse(200,bonus_end,0); } else { bonusphase = 3; bonus_score = 0; add_fuse(1,bonus_countdown,0); } } static void endlevel(void) { clear_enemies(0); if (invul) { bonus_end(0); } else { bonusphase = 1; bonustime = wavetime; state = STATE_BONUS; add_fuse(100,start_bonus_count,0); } } static void wander(FSHIP *fs, float vmin, float vmax) { if ((rnd() & 127) == 0) { float a; float v; a = ((rnd() & 4095) * M_PI) / 2048; v = vmin + (((rnd() & 4095) * (vmax-vmin)) / 4095); fs->obj.vx = cosf(a) * v; fs->obj.vy = sinf(a) * v; } } static void render_fontstring(int fg, int x, int y, const char *s) { const unsigned char *sp; int c; BITMAP *b; for (sp=(const void *)s;(c=*sp);sp++) { b = bitmaps_font[c]; fbbitmap(fg,b,0,x,y); x += b->chg; } } static void player_tickship(FSHIP *fst) { if (exiting) { set_exiting(); if (fst->obj.y > MAXY+8) endlevel(); player_still = 0; return; } player_still = (fabs(fst->obj.vx) < .2) && (fabs(fst->obj.vy) < .2); mobj_limitxy(&fst->obj); if ( (ncrystals == 0) && (fst->obj.y >= MAXY-MARGIN-1) && (fst->obj.x >= exit_x-16) && (fst->obj.x <= exit_x+16) ) { set_exiting(); } } static void player_drawship(FSHIP *s) { switch (state) { case STATE_WAITBEGIN: case STATE_PLAYING: fbmobj(077,&s->obj); if (player_still) render_fontstring(077,MAXX-20,-5,"S"); if (invul) render_fontstring(077,MAXX-10,-5,"I"); break; case STATE_EXPLODED: { int i; int n; int x; int y; x = s->obj.x; y = s->obj.y; drain(); n = exploded >> 1; if (n > 8) n = 8; for (i=0;iobj.x = MARGIN; fs->obj.vx = .6; } else { fs->obj.x = MAXX - 1 - MARGIN; fs->obj.vx = -.6; } fs->obj.y = MAXY / 2; fs->obj.vy = 0; mobj_setbitmap(&fs->obj,&bitmap_annoyer); } static void annoyer_tickship(FSHIP *fst) { mobj_bouncexy(&fst->obj); wander(fst,.2,.8); } static void annoyer_drawship(FSHIP *s) { fbmobj(011,&s->obj); } static FSOPS fsops_annoyer = { &annoyer_initnew, &nocleanup_cleanup, &annoyer_tickship, &noshoot_tickshot, &annoyer_drawship, &noshoot_drawshot, &die_behit, 1 }; static void worrier_initnew(FSHIP *fs) { fship_setmaxnshots(fs,5); if (rnd() & 1) { fs->obj.x = MARGIN; fs->obj.vx = .6; } else { fs->obj.x = MAXX - 1 - MARGIN; fs->obj.vx = -.6; } fs->obj.y = MAXY / 2; fs->obj.vy = 0; mobj_setbitmap(&fs->obj,&bitmap_worrier); } static void worrier_tickship(FSHIP *fst) { int tm; mobj_bouncexy(&fst->obj); tm = 255; if (((fst->obj.x-player->obj.x)*fst->obj.vx) > 0) tm >>= 1; if (((fst->obj.y-player->obj.y)*fst->obj.vy) > 0) tm >>= 1; if ((rnd() & tm) == 0) { float a; float v; a = atan2f(fst->obj.vy,fst->obj.vx); a = ((rnd() & 4095) * M_PI) / 2048; v = ((rnd() & 4095) * .6) / 4095; fst->obj.vx = cosf(a) * v; fst->obj.vy = sinf(a) * v; if (player->obj.x > fst->obj.x) fst->obj.vx += .02; else fst->obj.vx -= .02; if (player->obj.y > fst->obj.y) fst->obj.vy += .02; else fst->obj.vy -= .02; } if ((rnd() & 255) == 0) { MOBJ *s; s = fship_getshot(fst); if (s) { float a; float v; a = atan2f(player->obj.y-fst->obj.y,player->obj.x-fst->obj.x); a += (((rnd() & 4095) - (rnd() & 4095)) * M_PI) / 4096; v = .5 + ((rnd() & 4095) / 4095.0); s->vx = cosf(a) * v; s->vy = sinf(a) * v; mobj_setbitmap(s,&bitmap_worrier_shot); } } } static void worrier_drawship(FSHIP *s) { fbmobj(022,&s->obj); } static void worrier_drawshot(MOBJ *o) { fbmobj(022,o); } static FSOPS fsops_worrier = { &worrier_initnew, &nocleanup_cleanup, &worrier_tickship, &destroy_tickshot, &worrier_drawship, &worrier_drawshot, &die_behit, 2 }; static void pest_setv(PEST *p) { p->ship->obj.vx = cosf(p->a) * .6; p->ship->obj.vy = sinf(p->a) * .6; } static void pest_newda(PEST *p) { p->da = ((rnd() & 4095) / 800000) + .01; if (rnd() & 1) p->da = - p->da; } static void pest_initnew(FSHIP *fs) { PEST *p; p = malloc(sizeof(*p)); fs->private = p; p->ship = fs; if (rnd() & 1) { fs->obj.x = MARGIN; p->a = 0; } else { fs->obj.x = MAXX - 1 - MARGIN; p->a = M_PI / 2; } pest_newda(p); fs->obj.y = MAXY / 2; pest_setv(p); mobj_setbitmap(&fs->obj,&bitmap_pest); } static void pest_tickship(FSHIP *fst) { PEST *p; p = fst->private; if (mobj_bouncexy(&fst->obj)) { p->a = atan2f(fst->obj.vy,fst->obj.vx); } if ((rnd() & 127) == 0) { p->a = ((rnd() & 4095) * 2 * M_PI) / 4096; } else { p->a += p->da; } if ((rnd() & 127) == 0) pest_newda(p); pest_setv(p); if ((rnd() & 127) == 0) { MINE *m; m = newmine(MINE_PEST); mobj_setbitmap(&m->obj,&bitmap_pest_mine); m->obj.x = fst->obj.x; m->obj.y = fst->obj.y; } } static void pest_drawship(FSHIP *s) { fbmobj(033,&s->obj); } static FSOPS fsops_pest = { &pest_initnew, &justfree_cleanup, &pest_tickship, &noshoot_tickshot, &pest_drawship, &noshoot_drawshot, &die_behit, 4 }; static void dumple_initnew(FSHIP *fs) { if (rnd() & 1) { fs->obj.x = MARGIN; fs->obj.vx = .3; } else { fs->obj.x = MAXX - 1 - MARGIN; fs->obj.vx = -.3; } fs->obj.y = MAXY / 2; mobj_setbitmap(&fs->obj,&bitmap_dumple); } static void dumple_tickship(FSHIP *fst) { mobj_limitxy(&fst->obj); wander(fst,.3,.6); } static void dumple_drawship(FSHIP *s) { fbmobj(033,&s->obj); } static void dumple_behit(FSHIP *s, int how) { if ((how == BH_SHOT) && (rnd() & 7)) return; die_behit(s,how); } static FSOPS fsops_dumple = { &dumple_initnew, &nocleanup_cleanup, &dumple_tickship, &noshoot_tickshot, &dumple_drawship, &noshoot_drawshot, &dumple_behit, 80 }; static void trimpet_initnew(FSHIP *fs) { TRIMPET *t; t = malloc(sizeof(*t)); fs->private = t; t->ship = fs; t->hibernating = 0; t->hibfuse = 0; if (rnd() & 1) { fs->obj.x = MARGIN; fs->obj.vx = 1; } else { fs->obj.x = MAXX - 1 - MARGIN; fs->obj.vx = -1; } fs->obj.y = MAXY / 2; mobj_setbitmap(&fs->obj,&bitmap_trimpet); } static void trimpet_cleanup(FSHIP *fs) { TRIMPET *t; t = fs->private; if (t->hibernating) cancel_fuse(t->hibfuse); free(t); } static void trimpet_setv(FSHIP *fs) { switch (rnd() & 3) { case 0: fs->obj.vx = 0; fs->obj.vy = 1; break; case 1: fs->obj.vx = 1; fs->obj.vy = 0; break; case 2: fs->obj.vx = 0; fs->obj.vy = -1; break; case 3: fs->obj.vx = -1; fs->obj.vy = 0; break; } } static void trimpet_tickship(FSHIP *fs) { TRIMPET *t; t = fs->private; if (t->hibernating) return; mobj_bouncexy(&fs->obj); if ((rnd() & 511) == 0) trimpet_setv(fs); } static void trimpet_drawship(FSHIP *fs) { fbmobj(011,&fs->obj); } static void trimpet_wakeup(void *tv) { TRIMPET *t; t = tv; t->hibfuse = 0; t->hibernating = 0; trimpet_setv(t->ship); mobj_setbitmap(&t->ship->obj,&bitmap_trimpet); } static void trimpet_behit(FSHIP *fs, int how __attribute__((__unused__))) { TRIMPET *t; t = fs->private; if (t->hibernating) { cancel_fuse(t->hibfuse); } else { t->hibernating = 1; mobj_setbitmap(&fs->obj,&bitmap_trimpethib); } fs->obj.vx = 0; fs->obj.vy = 0; t->hibfuse = add_fuse(300,trimpet_wakeup,t); } static FSOPS fsops_trimpet = { &trimpet_initnew, &trimpet_cleanup, &trimpet_tickship, &noshoot_tickshot, &trimpet_drawship, &noshoot_drawshot, &trimpet_behit, 0 }; static void parasite_initnew(FSHIP *fs) { if (rnd() & 1) { fs->obj.x = MARGIN; } else { fs->obj.x = MAXX - 1 - MARGIN; } fs->obj.y = MAXY / 2; mobj_setbitmap(&fs->obj,&bitmap_parasite); } static void parasite_setv(FSHIP *fs) { float a; a = atan2f(player->obj.y-fs->obj.y,player->obj.x-fs->obj.x); fs->obj.vx = cosf(a); fs->obj.vy = sinf(a); } static void parasite_tickship(FSHIP *fs) { mobj_limitxy(&fs->obj); parasite_setv(fs); } static void parasite_drawship(FSHIP *fs) { fbmobj(011,&fs->obj); } static FSOPS fsops_parasite = { ¶site_initnew, &nocleanup_cleanup, ¶site_tickship, &noshoot_tickshot, ¶site_drawship, &noshoot_drawshot, &die_behit, 40 }; static void shrapwarden_setv(FSHIP *fs) { float a; a = ((rnd() & 4095) * M_PI) / 4096; fs->obj.vx = cosf(a); fs->obj.vy = sinf(a); } static void shrapwarden_initnew(FSHIP *fs) { fship_setmaxnshots(fs,16); if (rnd() & 1) { fs->obj.x = MARGIN; } else { fs->obj.x = MAXX - 1 - MARGIN; } fs->obj.y = MAXY / 2; shrapwarden_setv(fs); mobj_setbitmap(&fs->obj,&bitmap_shrapwarden); } static void shrapwarden_tickship(FSHIP *fs) { mobj_bouncexy(&fs->obj); if ((rnd() & 255) == 0) shrapwarden_setv(fs); } static void shrapwarden_drawship(FSHIP *fs) { fbmobj(011,&fs->obj); } static void shrapwarden_drawshot(MOBJ *s) { fbmobj(011,s); } static void shrapwarden_behit(FSHIP *fs, int how) { int i; MOBJ *s; float a; a = ((rnd() & 4095) * M_PI) / 32768; for (i=0;i<16;i++) { s = fship_getshot(fs); s->vx = 4 * cosf(a+((i*M_PI)/8)); s->vy = 4 * sinf(a+((i*M_PI)/8)); mobj_setbitmap(s,&bitmap_shrapwarden_frag); } die_behit(fs,how); } static FSOPS fsops_shrapwarden = { &shrapwarden_initnew, &nocleanup_cleanup, &shrapwarden_tickship, &destroy_tickshot, &shrapwarden_drawship, &shrapwarden_drawshot, &shrapwarden_behit, 400 }; static void zarklephaser_initnew(FSHIP *fs) { fship_setmaxnshots(fs,8); if (rnd() & 1) { fs->obj.x = MARGIN; fs->obj.vx = .6; } else { fs->obj.x = MAXX - 1 - MARGIN; fs->obj.vx = -.6; } fs->obj.y = MAXY / 2; fs->obj.vy = 0; mobj_setbitmap(&fs->obj,&bitmap_zarklephaser); } static void zarklephaser_tickship(FSHIP *fst) { int tm; mobj_bouncexy(&fst->obj); tm = 255; if (((fst->obj.x-player->obj.x)*fst->obj.vx) < 0) tm >>= 1; if (((fst->obj.y-player->obj.y)*fst->obj.vy) < 0) tm >>= 1; if ((rnd() & tm) == 0) { float a; float v; a = atan2f(fst->obj.vy,fst->obj.vx); a = ((rnd() & 4095) * M_PI) / 2048; v = .2 + (((rnd() & 4095) * .4) / 4095); fst->obj.vx = cosf(a) * v; fst->obj.vy = sinf(a) * v; if (player->obj.x > fst->obj.x) fst->obj.vx -= .02; else fst->obj.vx += .02; if (player->obj.y > fst->obj.y) fst->obj.vy -= .02; else fst->obj.vy += .02; } if ((rnd() & 31) == 0) { MOBJ *s; s = fship_getshot(fst); if (s) { float a; float v; int i; a = atan2f(player->obj.y-fst->obj.y,player->obj.x-fst->obj.x); for (i=0;i<3;i++) { a += (((rnd() & 4095) - (rnd() & 4095)) * M_PI) / 8192; } v = .5 + ((rnd() & 4095) / 2048.0); s->vx = cosf(a) * v; s->vy = sinf(a) * v; mobj_setbitmap(s,&bitmap_zarklephaser_shot); } } } static void zarklephaser_drawship(FSHIP *s) { fbmobj(022,&s->obj); } static void zarklephaser_drawshot(MOBJ *o) { fbmobj(022,o); } static FSOPS fsops_zarklephaser = { &zarklephaser_initnew, &nocleanup_cleanup, &zarklephaser_tickship, &destroy_tickshot, &zarklephaser_drawship, &zarklephaser_drawshot, &die_behit, 6 }; static void husket_initnew(FSHIP *fs) { fship_setmaxnshots(fs,5); if (rnd() & 1) { fs->obj.x = MARGIN; fs->obj.vx = .6; } else { fs->obj.x = MAXX - 1 - MARGIN; fs->obj.vx = -.6; } fs->obj.y = MAXY / 2; fs->obj.vy = 0; mobj_setbitmap(&fs->obj,&bitmap_husket); } static void husket_tickship(FSHIP *fst) { int tm; mobj_bouncexy(&fst->obj); tm = 255; if (((fst->obj.x-player->obj.x)*fst->obj.vx) < 0) tm >>= 1; if (((fst->obj.y-player->obj.y)*fst->obj.vy) < 0) tm >>= 1; if ((rnd() & tm) == 0) { float a; float v; a = atan2f(fst->obj.vy,fst->obj.vx); a = ((rnd() & 4095) * M_PI) / 2048; v = ((rnd() & 4095) * .4) / 4095; fst->obj.vx = cosf(a) * v; fst->obj.vy = sinf(a) * v; if (player->obj.x > fst->obj.x) fst->obj.vx += .02; else fst->obj.vx -= .02; if (player->obj.y > fst->obj.y) fst->obj.vy += .02; else fst->obj.vy -= .02; } if ((rnd() & 127) == 0) { MOBJ *s; s = fship_getshot(fst); if (s) { float a; float v; int i; a = atan2f(player->obj.y-fst->obj.y,player->obj.x-fst->obj.x); for (i=0;i<3;i++) { a += (((rnd() & 4095) - (rnd() & 4095)) * M_PI) / 8192; } v = .5 + ((rnd() & 4095) / 3072.0); s->vx = cosf(a) * v; s->vy = sinf(a) * v; mobj_setbitmap(s,&bitmap_husket_shot); } } } static void husket_drawship(FSHIP *s) { fbmobj(022,&s->obj); } static void husket_drawshot(MOBJ *o) { fbmobj(022,o); } static FSOPS fsops_husket = { &husket_initnew, &nocleanup_cleanup, &husket_tickship, &destroy_tickshot, &husket_drawship, &husket_drawshot, &die_behit, 8 }; static void bane_initnew(FSHIP *fs) { fship_setmaxnshots(fs,16); if (rnd() & 1) { fs->obj.x = MARGIN; fs->obj.vx = .6; } else { fs->obj.x = MAXX - 1 - MARGIN; fs->obj.vx = -.6; } fs->obj.y = MAXY / 2; fs->obj.vy = ((int)(rnd() & 4095) - (int)(rnd() & 4095)) / 4095.0; mobj_setbitmap(&fs->obj,&bitmap_bane); } static void bane_tickship(FSHIP *fst) { mobj_bouncexy(&fst->obj); if ((rnd() & 127) == 0) { float a; float v; a = atan2f(fst->obj.vy,fst->obj.vx); a = ((rnd() & 4095) * M_PI) / 2048; v = .2 + (((rnd() & 4095) * .4) / 4095); fst->obj.vx = cosf(a) * v; fst->obj.vy = sinf(a) * v; } if ((rnd() & 127) == 0) { MOBJ *s; s = fship_getshot(fst); if (s) { float a; a = ((rnd() & 4095) * M_PI) / 2048; s->vx = 2 * cosf(a); s->vy = 2 * sinf(a); mobj_setbitmap(s,&bitmap_bane_shot); } } } static void bane_tickshot(MOBJ *s) { mobj_bouncexy(s); } static void bane_drawship(FSHIP *s) { fbmobj(033,&s->obj); } static void bane_drawshot(MOBJ *o) { fbmobj(033,o); } static FSOPS fsops_bane = { &bane_initnew, &nocleanup_cleanup, &bane_tickship, &bane_tickshot, &bane_drawship, &bane_drawshot, &die_behit, 12 }; static void menace_initnew(FSHIP *fs) { MENACE *m; m = malloc(sizeof(*m)); fs->private = m; m->ship = fs; m->laser = ML_NO; if (rnd() & 1) { fs->obj.x = MARGIN; fs->obj.vx = 1; } else { fs->obj.x = MAXX - 1 - MARGIN; fs->obj.vx = -1; } fs->obj.y = MAXY / 2; fs->obj.vy = 0; mobj_setbitmap(&fs->obj,&bitmap_menace); } static void menace_setv(FSHIP *fs) { switch (rnd() & 3) { case 0: fs->obj.vx = 0; fs->obj.vy = 1; break; case 1: fs->obj.vx = 1; fs->obj.vy = 0; break; case 2: fs->obj.vx = 0; fs->obj.vy = -1; break; case 3: fs->obj.vx = -1; fs->obj.vy = 0; break; } } static void menace_shoot(MENACE *m) { m->ship->obj.vx = 0; m->ship->obj.vy = 0; m->llen = 0; if (rnd() & 1) { m->laser = ML_ACTIVE | ML_OUT | ML_H; m->lmax = rnd() % MAXX; } else { m->laser = ML_ACTIVE | ML_OUT | ML_V; m->lmax = rnd() % MAXY; } } static void menace_tickship(FSHIP *fs) { MENACE *m; m = fs->private; if (m->laser & ML_ACTIVE) { switch (m->laser & ML_IO) { case ML_OUT: m->llen ++; if (m->llen >= m->lmax) m->laser = (m->laser & ~ML_IO) | ML_IN; break; case ML_IN: m->llen --; if (m->llen < 1) { m->laser = ML_NO; menace_setv(fs); } break; } } else { mobj_bouncexy(&fs->obj); if ((rnd() & 511) == 0) menace_setv(fs); if ((rnd() & 1023) == 0) menace_shoot(m); } } static void menace_drawship(FSHIP *s) { MENACE *m; fbmobj(033,&s->obj); m = s->private; if ((m->laser & ML_ACTIVE) && (m->llen > 0)) { int x; int y; int l; x = s->obj.x; y = s->obj.y; switch (m->laser & ML_HV) { case ML_H: drain(); l = MAXX - (x+8); if (l > m->llen) l = m->llen; fbrect(rnd()&077,x+8,y-1,x+7+l,y); l = x-8; if (l > m->llen) l = m->llen; fbrect(rnd()&077,x-8-l,y-1,x-9,y); drain(); break; case ML_V: drain(); l = MAXY - (y+8); if (l > m->llen) l = m->llen; fbrect(rnd()&077,x-1,y+8,x,y+7+l); l = y-8; if (l > m->llen) l = m->llen; fbrect(rnd()&077,x-1,y-8-l,x,y-9); drain(); break; } } } static int menace_shootcheck(FSHIP *mfs, FSHIP *s) { MENACE *m; m = mfs->private; if ((m->laser & ML_ACTIVE) && (m->llen > 0)) { int x; int y; int l; x = mfs->obj.x; y = mfs->obj.y; switch (m->laser & ML_HV) { case ML_H: l = MAXX - (x+8); if (l > m->llen) l = m->llen; if (touchesrect(&s->obj,x+8,y-1,x+7+l,y)) return(1); l = x-8; if (l > m->llen) l = m->llen; if (touchesrect(&s->obj,x-8-l,y-1,x-9,y)) return(1); break; case ML_V: l = MAXY - (y+8); if (l > m->llen) l = m->llen; if (touchesrect(&s->obj,x-1,y+8,x,y+7+l)) return(1); l = y-8; if (l > m->llen) l = m->llen; if (touchesrect(&s->obj,x-1,y-8-l,x,y-9)) return(1); break; } if (debugk) debugk--; } return(0); } static FSOPS fsops_menace = { &menace_initnew, &justfree_cleanup, &menace_tickship, &noshoot_tickshot, &menace_drawship, &noshoot_drawshot, &die_behit, 10 }; static void tentawarble_initnew(FSHIP *fs) { TENTAWARBLE *w; w = malloc(sizeof(*w)); fs->private = w; w->ship = fs; w->stillfor = 0; w->diving = 0; if (rnd() & 1) { fs->obj.x = MARGIN; fs->obj.vx = 1; } else { fs->obj.x = MAXX - 1 - MARGIN; fs->obj.vx = -1; } fs->obj.y = MAXY / 2; fs->obj.vy = ((int)(rnd() & 4095) - (int)(rnd() & 4095)) / 4095.0; mobj_setbitmap(&fs->obj,&bitmap_tentawarble); } static void tentawarble_tickship(FSHIP *fs) { TENTAWARBLE *w; w = fs->private; mobj_bouncexy(&fs->obj); if (w->diving) { w->divetime --; if (w->divetime < 1) { fs->obj.vx = 0; fs->obj.vy = 0; w->diving = 0; w->stillfor = 0; } return; } if (player_still) { w->stillfor ++; if (w->stillfor > 50) { float d; float a; int n; a = atan2f(player->obj.y-fs->obj.y,player->obj.x-fs->obj.x); d = hypotf(player->obj.y-fs->obj.y,player->obj.x-fs->obj.x); n = d / 5; if (n < 1) n = 1; w->diving = 1; w->divetime = n + 2; fs->obj.vx = (d/n) * cosf(a); fs->obj.vy = (d/n) * sinf(a); return; } } else { w->stillfor = 0; } wander(fs,.3,.5); } static void tentawarble_drawship(FSHIP *s) { fbmobj(033,&s->obj); } static FSOPS fsops_tentawarble = { &tentawarble_initnew, &justfree_cleanup, &tentawarble_tickship, &noshoot_tickshot, &tentawarble_drawship, &noshoot_drawshot, &die_behit, 8 }; static void fuse_bubble_down(void) { unsigned int x; unsigned int l; unsigned int r; FUSE *fx; FUSE *fl; FUSE *fr; x = 0; fx = fuses[0]; while (x < fuses_n) { l = x + x + 1; r = l + 1; fl = (l < fuses_n) ? fuses[l] : 0; fr = (r < fuses_n) ? fuses[r] : 0; if (fl && (fl->when < fx->when)) { if (fr && (fr->when < fx->when)) { if (fl->when < fr->when) { fuses[x] = fl; x = l; } else { fuses[x] = fr; x = r; } } else { fuses[x] = fl; x = l; } } else { if (fr && (fr->when < fx->when)) { fuses[x] = fr; x = r; } else { break; } } } fuses[x] = fx; } static void initgame(void) { state = STATE_NASCENT; fships = 0; player = makefship(&fsops_player); player->refs ++; /* never free */ player->obj.flags |= FSF_HIT_P; emitter_l_obj.x = 0; emitter_l_obj.y = MAXY / 2; emitter_l_obj.vx = 0; emitter_l_obj.vy = 0; emitter_l_obj.flags = 0; mobj_setbitmap(&emitter_l_obj,&bitmap_emitter_l); emitter_r_obj.x = MAXX - 1; emitter_r_obj.y = MAXY / 2; emitter_r_obj.vx = 0; emitter_r_obj.vy = 0; emitter_r_obj.flags = 0; mobj_setbitmap(&emitter_r_obj,&bitmap_emitter_r); exit_l_obj.y = MAXY - 1; exit_l_obj.vx = 0; exit_l_obj.vy = 0; exit_l_obj.flags = 0; mobj_setbitmap(&exit_l_obj,&bitmap_exit_l); exit_r_obj.y = MAXY - 1; exit_r_obj.vx = 0; exit_r_obj.vy = 0; exit_r_obj.flags = 0; mobj_setbitmap(&exit_r_obj,&bitmap_exit_r); exit_closure_obj.y = MAXY - 1; exit_closure_obj.vx = 0; exit_closure_obj.vy = 0; exit_closure_obj.flags = 0; mobj_setbitmap(&exit_closure_obj,&bitmap_empty); paused = 0; expls = 0; ncrystals = 0; acrystals = 0; crystals = 0; nmines = 0; amines = 0; mines = 0; nbombs = 0; abombs = 0; bombs = 0; score = 0; lives = 2; smartbombs = 2; nextlife = 600; level = 1; add_fuse(1,schedule_emit,0); setup_level(); } static void queue_event(int ev) { if (((evqh-evqt) & (EVQ_SIZE-1)) == (EVQ_SIZE-1)) { write(1,"evq full\n",9); } else { evq[evqh] = ev; evqh = (evqh+1) & (EVQ_SIZE-1); } } static int checkkb(void) { struct firm_event ev; int r; r = read(kbfd,&ev,sizeof(ev)); if (r == sizeof(ev)) { rnd_tweak(&ev,sizeof(ev)); queue_event(EVT_KBD|(ev.id&0x7f)|((ev.value==VKEY_UP)?EVK_UP:EVK_DN)); return(1); } return(0); } static int checkms(void) { struct firm_event ev; int r; int v; r = read(msfd,&ev,sizeof(ev)); if (r == sizeof(ev)) { rnd_tweak(&ev,sizeof(ev)); switch (ev.id) { case MS_LEFT: v = EVM_BUT_L; if (0) { case MS_MIDDLE: v = EVM_BUT_M; } if (0) { case MS_RIGHT: v = EVM_BUT_R; } queue_event(EVT_MOUSE|EVM_T_BUT|v|((ev.value==VKEY_UP)?EVM_UP:EVM_DN)); break; case LOC_X_DELTA: v = EVM_T_DX; if (0) { case LOC_Y_DELTA: v = EVM_T_DY; } if (ev.value < 0) { queue_event(EVT_MOUSE|v|EVM_S_NEG|(EVM_DELTA&-ev.value)); } else { queue_event(EVT_MOUSE|v|EVM_S_POS|(EVM_DELTA&ev.value)); } break; default: printf("id %#x ignored from mouse\n",ev.id); break; } return(1); } return(0); } static int checktick(void) { int ht; ht = hardtime; if (softtime != ht) { queue_event(EVT_TICK); softtime ++; if ((softtime == ht) && (softtime >= drawtime)) queue_event(EVT_DRAW); return(1); } return(0); } static int dequeue_event(void) { int ev; if (evqh == evqt) return(EVT_NIL); ev = evq[evqt]; evqt = (evqt+1) & (EVQ_SIZE-1); return(ev); } static void render_fships(void) { int i; FSHIP *fs; for (fs=fships;fs;fs=fs->link) { if (! (fs->obj.flags & FSF_DEAD)) (*fs->ops->drawship)(fs); for (i=fs->nshots-1;i>=0;i--) { MOBJ *s; s = &fs->shots[i]; if (! (s->flags & FSF_DEAD)) (*fs->ops->drawshot)(s); } } } static void old_explosion(void *ev) { ((EXPL *)ev)->obj.flags |= FSF_DEAD; } static void new_explosion(int x, int y) { EXPL *e; e = malloc(sizeof(EXPL)); e->link = expls; expls = e; mobj_init(&e->obj); e->obj.x = x; e->obj.y = y; mobj_setbitmap(&e->obj,&bitmap_explosion); add_fuse(bitmap_explosion.chg*5,old_explosion,e); } static void tick_fships(void) { FSHIP *fs; FSHIP **fsp; MOBJ *s; int i; fsp = &fships; while ((fs = *fsp)) { if ((fs->obj.flags & (FSF_DEAD|FSF_EXPL)) == FSF_DEAD) { new_explosion(fs->obj.x,fs->obj.y); fs->obj.flags |= FSF_EXPL; } if ((fs->obj.flags & FSF_DEAD) && (fs->refs < 1)) { *fsp = fs->link; mobj_cleanup(&fs->obj); (*fs->ops->cleanup)(fs); free(fs->shots); free(fs); } else { for (i=fs->nshots-1;i>=0;i--) { s = fs->shots + i; if (s->flags & FSF_DEAD) { fs->nshots --; fs->refs --; mobj_cleanup(s); if (i < fs->nshots) fs->shots[i] = fs->shots[fs->nshots]; continue; } mobj_move(fs->shots+i); } for (i=fs->nshots-1;i>=0;i--) (*fs->ops->tickshot)(&fs->shots[i]); if (! (fs->obj.flags & FSF_DEAD)) { mobj_move(&fs->obj); (*fs->ops->tickship)(fs); } fsp = &fs->link; } } } static void player_fire(void) { MOBJ *s; if ( (fabsf(player->obj.vx) > .01) || (fabsf(player->obj.vy) > .01) ) { s = fship_getshot(player); if (s) { s->vx = player->obj.vx * 2.5; s->vy = player->obj.vy * 2.5; mobj_setbitmap(s,&bitmap_player_shot); } } } static void check_fuses(void) { FUSE *f; while ((fuses_n > 0) && (fuses[0]->when <= queuetime)) { f = fuses[0]; if (f->fn) (*f->fn)(f->arg); free(f); fuses_n --; if (fuses_n > 0) { fuses[0] = fuses[fuses_n]; fuse_bubble_down(); } } } static void check_hits(void) { FSHIP *f1; FSHIP *f2; int i; MOBJ *s; if ( touches(&player->obj,&emitter_l_obj) || touches(&player->obj,&emitter_r_obj) || touches(&player->obj,&exit_l_obj) || touches(&player->obj,&exit_r_obj) || touches(&player->obj,&exit_closure_obj) ) { player->obj.flags |= FSF_DEAD; return; } for (i=ncrystals-1;i>=0;i--) { if (touches(&player->obj,crystals[i])) { destroy_crystal(i); incscore(8); set_exit_bitmap(); } } for (i=nbombs-1;i>=0;i--) { if (touches(&player->obj,bombs[i])) { destroy_bomb(i); smartbombs ++; } } for (i=nmines-1;i>=0;i--) { if (touches(&player->obj,&mines[i]->obj)) { destroymine(i); player->obj.flags |= FSF_DEAD; return; } } for (f1=fships;f1;f1=f1->link) { if (f1->obj.flags & FSF_DEAD) continue; if ((f1 == player) && exiting) continue; for (f2=fships;f2;f2=f2->link) { if (f1->obj.flags & f2->obj.flags & FSF_HIT) continue; if (!(f2->obj.flags & FSF_DEAD) && touches(&f1->obj,&f2->obj)) { (*f1->ops->behit)(f1,BH_SHIP); (*f2->ops->behit)(f2,BH_SHIP); goto nextf1; /* continue 2 */ } if ( (f1 == player) && (f2->ops == &fsops_menace) && menace_shootcheck(f2,f1) ) { (*f1->ops->behit)(f1,BH_SHOT); goto nextf1; /* continue 2 */ } for (i=f2->nshots-1;i>=0;i--) { s = f2->shots + i; if (s->flags & FSF_DEAD) continue; if (touches(&f1->obj,s)) { (*f1->ops->behit)(f1,BH_SHOT); s->flags |= FSF_DEAD; goto nextf1; /* continue 3 */ } } } nextf1:; } } static void end_exploded(void *arg __attribute__((__unused__))) { if (lives > 0) { lives --; reset_playbegin(); enterstate(STATE_WAITBEGIN); } else { printf("Final score: %d\n",score); exit(0); } } static void advance_exploded(void *arg __attribute__((__unused__))) { if (state == STATE_EXPLODED) { exploded++; add_fuse(5,advance_exploded,0); } } static void dec_flash(void *arg __attribute__((__unused__))) { flash --; if (flash > 0) add_fuse(4,dec_flash,0); } static void togglepause(void) { paused ^= PAUSE_TABKEY; } static void toggleinvul(void) { invul = ! invul; } static void tick_exit(void) { exit_x += exit_vx; if (exit_x > MAXX-48) { exit_x = MAXX-48; exit_vx = - exit_vx; } else if (exit_x < 48) { exit_x = 48; exit_vx = - exit_vx; } changed_exit_x(); } static void startflash(int n) { if (flash == 0) add_fuse(4,dec_flash,0); flash += n; } static void smartbomb(void) { if (smartbombs < 1) return; smartbombs --; startflash(5); clear_enemies(1); } static void process_evq(void) { int ev; while (1) { ev = dequeue_event(); switch (ev & EV_TYPE) { case EVT_NIL: return; break; case EVT_KBD: switch (ev & (EVK_CODE|EVK_UPDN)) { case 1|EVK_DN: debugk++; break; case 1|EVK_UP: break; case 5|EVK_DN: toggleinvul(); break; case 5|EVK_UP: break; case 29|EVK_DN: exit(0); break; case 29|EVK_UP: break; case 53|EVK_DN: togglepause(); break; case 53|EVK_UP: break; case 121|EVK_DN: smartbomb(); break; case 121|EVK_UP: break; default: printf("kbd: %d ",ev&EVK_CODE); switch (ev & EVK_UPDN) { case EVK_UP: printf("up\n"); break; case EVK_DN: printf("dn\n"); break; } break; } break; case EVT_TICK: queuetime ++; if (state == STATE_PLAYING) { tick_fships(); tick_exit(); check_hits(); wavetime ++; } check_fuses(); if (player->obj.flags & FSF_DEAD) { player->obj.flags &= ~FSF_DEAD; if (! invul) { exploded = 1; enterstate(STATE_EXPLODED); startflash(7); add_fuse(5,advance_exploded,0); add_fuse(200,end_exploded,0); } } break; case EVT_DRAW: wantrender = 1; break; case EVT_MOUSE: if (state == STATE_WAITBEGIN) enterstate(STATE_PLAYING); switch (ev & EVM_TYPE) { case EVM_T_DX: switch (ev & EVM_SIGN) { case EVM_S_NEG: player->obj.vx -= (ev & EVM_DELTA) * MOVE_SCALE; break; case EVM_S_POS: player->obj.vx += (ev & EVM_DELTA) * MOVE_SCALE; break; } break; case EVM_T_DY: switch (ev & EVM_SIGN) { case EVM_S_NEG: player->obj.vy += (ev & EVM_DELTA) * MOVE_SCALE; break; case EVM_S_POS: player->obj.vy -= (ev & EVM_DELTA) * MOVE_SCALE; break; } break; case EVM_T_BUT: if ((ev & EVM_UPDN) == EVM_DN) player_fire(); break; } break; } } } static void render_fixtures(void) { fbmobj(055,&emitter_l_obj); fbmobj(055,&emitter_r_obj); fbmobj(066,&exit_l_obj); fbmobj(066,&exit_r_obj); fbmobj(066,&exit_closure_obj); } static void render_explosions(void) { EXPL *e; EXPL **ep; ep = &expls; while ((e = *ep)) { if (e->obj.flags & FSF_DEAD) { *ep = e->link; mobj_cleanup(&e->obj); free(e); } else { fbmobj(rnd()&077,&e->obj); ep = &e->link; } } } static void render_crystals(void) { int i; for (i=ncrystals-1;i>=0;i--) fbmobj(077,crystals[i]); } static void render_mines(void) { int i; for (i=nmines-1;i>=0;i--) fbmobj(044,&mines[i]->obj); } static void render_bombs(void) { int i; for (i=nbombs-1;i>=0;i--) fbmobj(rnd()&077,bombs[i]); } static void render_scoreline(void) { char s[64]; int i; sprintf(&s[0],"Score: %9d",score); render_fontstring(077,0,-5,&s[0]); if (lives < 5) { for (i=0;i= 360000) { sprintf(&s[0],"Wave: %2d Time: %d:%02d:%02d",level,wavetime/360000,(wavetime/6000)%60,(wavetime/100)%60); } else { sprintf(&s[0],"Wave: %2d Time: %02d:%02d",level,(wavetime/6000)%60,(wavetime/100)%60); } render_fontstring(077,275,-5,&s[0]); } static void render_bonusscreen(void) { char s[128]; sprintf(&s[0],"Wave %d complete",level); render_fontstring(077,150,75,&s[0]); if (bonustime >= 360000) { sprintf(&s[0],"Time: %d:%02d:%02d.%02d",bonustime/360000,(bonustime/6000)%60,(bonustime/100)%60,bonustime%100); } else { sprintf(&s[0],"Time: %02d:%02d.%02d",(bonustime/6000)%60,(bonustime/100)%60,bonustime%100); } render_fontstring(077,175,125,&s[0]); switch (bonusphase) { case 1: break; case 2: render_fontstring(077,150,175,"Sorry, no time bonus"); break; case 3: sprintf(&s[0],"Bonus: %d",bonus_score); render_fontstring(077,150,175,&s[0]); break; } } static void mayberender(void) { if (! wantrender) return; wantrender = 0; drain(); fb->alu = 0xff00; fb->mode = 0x20000; fbrect_(0,0,0,1151,899); drain(); fb->alu = 0xee22; fb->mode = 0x40000; switch (state) { case STATE_WAITBEGIN: case STATE_PLAYING: case STATE_EXPLODED: render_explosions(); render_crystals(); render_mines(); render_bombs(); render_fixtures(); render_fships(); render_scoreline(); break; case STATE_BONUS: render_scoreline(); render_bonusscreen(); break; } drain(); setcmap(drawbit); drawbit = ! drawbit; fb->pm = drawbit ? 070 : 007; } static void step(void) { struct pollfd pfd[2]; process_evq(); mayberender(); if (checkkb()) return; if (checkms()) return; if (checktick()) return; pfd[0].fd = kbfd; pfd[0].events = POLLIN | POLLRDNORM; pfd[1].fd = msfd; pfd[1].events = POLLIN | POLLRDNORM; if (poll(&pfd[0],2,INFTIM) < 0) { if (errno == EINTR) return; fprintf(stderr,"%s: poll: %s\n",__progname,strerror(errno)); exit(1); } } static void initfuses(void) { fuses = 0; fuses_n = 0; fuses_a = 0; } static void initbitmaps(void) { int i; BITMAP *b; int nv; int v; int r0; int r; unsigned long int s; for (i=0;(b=allbitmaps[i]);i++) { nv = b->nvariants; if (nv < 1) nv = 1; s = 0; for (v=nv-1;v>=0;v--) { r0 = (b->vmap ? b->vmap[v] : v) * b->h; for (r=b->h-1;r>=0;r--) s |= b->bits[r+r0]; } b->shadow = s; } } int main(void); int main(void) { setupfb(); setupkb(); setupms(); setuptimer(); initevq(); initfuses(); initrandom(); initbitmaps(); initgame(); wantrender = 1; while (1) step(); exit(0); }