#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "cg6.h" static const char *fbpath = "/dev/cgsix0"; static const char *kbpath = "/dev/kbd1"; #define FRICTION .99 #define THRUST .05 #define TURNRATE .05 #define SHOTVEL 4 #define SHOTSIZE 5 #define INNERRAD 125 #define RADGROW .5 #define RADSTEP 35 #define SHIPSIZE 35 #define CASTLESIZE 85 #define FBALLVEL 6 #define FBALLSIZE 30 #define BOMBSIZE 6 #define BOMBTURN .03 #define BOMBVEL 1 #define BOMBACC .99 /* * BOUCERAD is the distance from the castle at which the ship bounces. * One could argue it should use innerrad rather than INNERRAD. * * RINGDIP is 1-hypot((cos(M_PI/6)+1)/2,sin(M_PI/6)/2), that is, the * proportion by which the straight lines of the rings dip within the * circumscribed circle. * * CASTLE_ROT_SPEEDUP is the factor by which maxcrv is multiplied each * tick. This value is the 30000th root of 100*pi; that is, after * 30000 ticks (five minutes), the initial value of BASE_CASTLE_ROT * will have turned into pi/10 and the castle can turn anywhere in no * more than a tenth of a second. To calculate: pi=const(pi) cmp=pi*100 r10=solve(pow(x,10)-cmp,x,1,2) giving r10=1.777112768463957753413567363606813057675584937712775446074193847 r100=solve(pow(x,10)-r10,x,1,1.1) giving r100=1.059184212230882529609404950333490828709524784812073937588807094 r1000=solve(pow(x,10)-r100,x,1.005,1.006) giving r1000=1.00576646247609577094712455529555032046948664410033781766606117 r10000=solve(pow(x,10)-r1000,x,1.00057,1.00058) giving r10000=1.000575155345625728573733905651520945895527191861185134910171764 rapp=solve(pow(x,3)-r10000,x,1.0001,1.0002) giving rapp=1.00019168170431855726858047962180470791110289018153364516423637 r=solve(pow(x,30000)-cmp,x,rapp-1@-20,rapp+1@-20) giving r=1.000191681704318557268582941293658942648214145708076308738455581 */ #define BOUNCERAD (INNERRAD+(2*RADSTEP)+SHIPSIZE) #define RINGDIP .0340742 #define BASE_CASTLE_ROT .001 #define CASTLE_ROT_SPEEDUP 1.000191681704318557268582941293658942648214145708076308738455581; static const unsigned char cmap[16][3] = { { 0, 0, 0 }, #define CM_BLACK 0 { 255, 255, 255 }, #define CM_WHITE 1 { 0, 150, 0 }, #define CM_RING_DIM 2 { 0, 255, 0 }, #define CM_RING_LIT 3 { 255, 0, 0 }, #define CM_CASTLE 4 { 175, 175, 175 }, #define CM_SHIP 5 { 255, 255, 255 }, #define CM_SHOT 6 { 255, 175, 0 }, #define CM_FBALL 7 { 255, 175, 0 }, #define CM_BOMB 8 }; typedef struct fuse FUSE; typedef struct xy XY; typedef struct bomb BOMB; typedef struct locvel LOCVEL; typedef struct lvr LVR; typedef struct shot SHOT; typedef struct ring RING; struct ring { int inx; int serial; char segs[12]; int nsegs; int nsegs1; int nsegs2; float rr; float rot; float rv; } ; struct xy { float x; float y; } ; struct locvel { XY loc; XY vel; } ; struct lvr { LOCVEL lv; float r; } ; struct shot { LOCVEL lv; int age; int lastring; } ; struct bomb { int state; #define BS_HOPPING 1 #define BS_ONRING 2 #define BS_FREE 3 union { struct { XY loc; RING *ring; int ringser; int seg; int ticks; } hopping; struct { RING *ring; int ringser; int seg; int wanthop; FUSE *hopfuse; } onring; struct { LOCVEL lv; float va; float vs; } free; } u; } ; struct fuse { unsigned long int when; void (*fn)(void *); void *arg; } ; static int fbfd; static int kbfd; static volatile struct cg6fb *fb; static volatile struct brooktree *bt; static volatile unsigned char *vram; #define MIDX (1152/2) #define MIDY (900/2) #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; static int fuses_n; static int fuses_a; static FUSE **fuses; #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 static int paused; static int singlestep; static int wantrender; static int drawbit; static int flash; static int state; #define STATE_PLAYING 1 static int score; static int lives; static int kbit; #define BIT_L 0x00000001 #define BIT_R 0x00000002 #define BIT_T 0x00000004 static const float ringrv[3] = { .01, -.0094, .003 }; static RING ringarr[3]; static RING *rings[3]; static float innerrad; static int canbomb; static FUSE *canbomb_fuse; static int nbombs; static BOMB bombv[3]; static BOMB *bombs[3]; static int nfball; static LOCVEL fball; static int nshots; static SHOT shots[3]; static LVR castle; static double maxcrv; static LVR ship; static const XY drawcastle[] = { { .9, .15 }, { 1, -.07 }, { -.15, -.15, }, { -.5, -.85 }, { .5, -.75 }, { .65, -.4 }, { -.8, -.6 }, { -.5, 0 }, { -.8, .6 }, { .65, .4 }, { .5, .75 }, { -.5, .85 }, { -.15, .15 }, { 1, .07 }, { .9, -.15 } }; #define CASTLEPTS (sizeof(drawcastle)/sizeof(drawcastle[0])) static const XY drawship[] = { { .95, .2 }, { -.85, .2 }, { .6, .07 }, { -.75, .7 }, { -.4, 0 }, { -.75, -.7 }, { .6, -.07 }, { -.85, -.2 }, { .95, -.2 } }; #define SHIPPTS (sizeof(drawship)/sizeof(drawship[0])) static float angreduce_p(float) __attribute__((__const__)); static float angreduce_p(float a) { while (a >= 2*M_PI) a -= 2*M_PI; while (a < 0) a += 2*M_PI; return(a); } static float angreduce_s(float) __attribute__((__const__)); static float angreduce_s(float a) { while (a <= -M_PI) a += 2*M_PI; while (a > M_PI) a -= 2*M_PI; return(a); } static void setcmap(int bit) { int i; int v; bt->addr = 0; for (i=0;i<=255;i++) { v = (bit ? (i >> 4) : i) & 15; if ((flash & 1) & (v == CM_BLACK)) v = CM_WHITE; 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 fbline(int fg, int x0, int y0, int x1, int y1) { fb->fg = 0x11 * fg; fb->aliney = 899 - y0; fb->alinex = x0; fb->aliney = 899 - y1; fb->alinex = x1; draw(); } static void fbrline(int fg, int x0, int y0, int x1, int y1) { fb->fg = 0x11 * fg; fb->aliney = 899 - y0; fb->alinex = x0; fb->rliney = - y1; fb->rlinex = x1; draw(); } static void fblinev(int fg, int n, int *xv, int *yv) { fb->fg = 0x11 * fg; fb->aliney = 899 - *yv++; fb->alinex = *xv++; for (;n>0;n--) { fb->aliney = 899 - *yv++; fb->alinex = *xv++; draw(); } } static void fbrect(int fg, int x1, int y1, int x2, int y2) { fb->fg = 0x11 * fg; fb->arecty = y1; fb->arectx = x1; fb->arecty = y2; fb->arectx = x2; draw(); } static void setupfb(void) { void *mrv; struct fbgattr a; 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; 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; fb->pm = ~0; drain(); fb->clipminx = 0; fb->clipminy = 0; fb->clipmaxx = 1151; fb->clipmaxy = 899; fb->pm = 15; } 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 handle_sigalrm(int sig __attribute__((__unused__))) { if (paused) { if (! singlestep) return; singlestep --; } 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 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 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 setcanbomb(void *arg __attribute__((__unused__))) { canbomb = 1; canbomb_fuse = 0; } static void setup_new(void) { int r; int s; int i; innerrad = INNERRAD; for (r=0;r<3;r++) { rings[r] = &ringarr[r]; ringarr[r].inx = r; ringarr[r].serial = 0; for (s=0;s<12;s++) ringarr[r].segs[s] = 2; ringarr[r].nsegs = 12; ringarr[r].nsegs1 = 0; ringarr[r].nsegs2 = 12; ringarr[r].rr = r * RADSTEP; ringarr[r].rot = ((rnd() & 4095) * M_PI) / 2048; ringarr[r].rv = ringrv[r]; } canbomb = 0; canbomb_fuse = add_fuse(200,setcanbomb,0); nbombs = 0; for (i=0;i<3;i++) bombs[i] = &bombv[i]; nfball = 0; nshots = 0; castle.lv.loc.x = MIDX; castle.lv.loc.y = MIDY; castle.lv.vel.x = 0; castle.lv.vel.y = 0; castle.r = M_PI / 2; ship.lv.loc.x = MIDX; ship.lv.loc.y = 50; ship.lv.vel.x = 0; ship.lv.vel.y = 0; ship.r = M_PI / 2; } static void initgame(void) { state = STATE_PLAYING; paused = 0; singlestep = 0; score = 0; lives = 2; maxcrv = BASE_CASTLE_ROT; setup_new(); } 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 checktick(void) { int ht; ht = hardtime; if (softtime != ht) { queue_event(EVT_TICK); softtime ++; if ((softtime == ht) && (paused || (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 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 dec_flash(void *arg __attribute__((__unused__))) { flash --; if (flash > 0) add_fuse(4,dec_flash,0); } #if 0 static void startflash(int n) { if (flash == 0) add_fuse(4,dec_flash,0); flash += n; } #endif static void togglepause(void) { paused = ! paused; } static void sstep(void) { singlestep ++; } static void move_rings(void) { int i; for (i=0;i<3;i++) rings[i]->rot = angreduce_s(rings[i]->rot+rings[i]->rv); if (innerrad < INNERRAD) { innerrad += RADGROW; } else if (rings[2]->nsegs == 0) { RING *t; innerrad -= RADSTEP; t = rings[2]; rings[2] = rings[1]; rings[1] = rings[0]; rings[0] = t; t->serial ++; for (i=0;i<12;i++) t->segs[i] = 2; t->nsegs = 12; t->nsegs1 = 0; t->nsegs2 = 12; for (i=0;i<3;i++) { rings[i]->inx = i; rings[i]->rr = i * RADSTEP; } } } static void wraparound(XY *lp) { if (lp->x < 25) lp->x += 1100; else if (lp->x > 1125) lp->x -= 1100; if (lp->y < 25) lp->y += 850; else if (lp->y > 875) lp->y -= 850; } static void move_lv_wrap(LOCVEL *lv) { lv->loc.x += lv->vel.x; lv->loc.y += lv->vel.y; wraparound(&lv->loc); } static int move_lv_off(LOCVEL *lv) { lv->loc.x += lv->vel.x; lv->loc.y += lv->vel.y; return( (lv->loc.x < 25) || (lv->loc.y < 25) || (lv->loc.x > 1125) || (lv->loc.y > 875) ); } static void move_ship(void) { move_lv_wrap(&ship.lv); if ( (abs((int)ship.lv.loc.x-(int)castle.lv.loc.x) < BOUNCERAD+1) && (abs((int)ship.lv.loc.y-(int)castle.lv.loc.y) < BOUNCERAD+1) && (hypotf(ship.lv.loc.x-castle.lv.loc.x,ship.lv.loc.y-castle.lv.loc.y) < BOUNCERAD) ) { XY r; float a; float rc; a = atan2f(ship.lv.loc.y-castle.lv.loc.y,ship.lv.loc.x-castle.lv.loc.x); r.x = cosf(a); r.y = sinf(a); rc = (ship.lv.vel.x * r.x) + (ship.lv.vel.y * r.y); ship.lv.vel.x -= 2 * rc * r.x; ship.lv.vel.y -= 2 * rc * r.y; ship.lv.loc.x = castle.lv.loc.x + ((BOUNCERAD+.001) * cosf(a)); ship.lv.loc.y = castle.lv.loc.y + ((BOUNCERAD+.001) * sinf(a)); } ship.lv.vel.x *= FRICTION; ship.lv.vel.y *= FRICTION; switch (kbit & (BIT_L|BIT_R)) { case BIT_L: ship.r = angreduce_s(ship.r+TURNRATE); break; case BIT_R: ship.r = angreduce_s(ship.r-TURNRATE); break; } if (kbit & BIT_T) { ship.lv.vel.x += cosf(ship.r) * THRUST; ship.lv.vel.y += sinf(ship.r) * THRUST; } } static void fire(void) { SHOT *s; if (nshots > 2) return; s = &shots[nshots++]; s->lv = ship.lv; s->lv.vel.x += SHOTVEL * cosf(ship.r); s->lv.vel.y += SHOTVEL * sinf(ship.r); s->age = 0; s->lastring = -1; } static void deadshot(int inx) { nshots --; if (inx < nshots) shots[inx] = shots[nshots]; } static void deadbomb(int inx) { BOMB *b; b = bombs[inx]; switch (b->state) { case BS_ONRING: if (! b->u.onring.wanthop) cancel_fuse(b->u.onring.hopfuse); break; } nbombs --; if (inx < nbombs) { b = bombs[inx]; bombs[inx] = bombs[nbombs]; bombs[nbombs] = b; } } static void move_shots(void) { int i; SHOT *s; for (i=nshots-1;i>=0;i--) { s = &shots[i]; move_lv_wrap(&s->lv); if (s->age++ > 300) deadshot(i); } } static int within(XY l1, XY l2, float dist) { return ( (fabsf(l1.x-l2.x) <= dist) && (fabsf(l1.y-l2.y) <= dist) && (hypotf(l1.x-l2.x,l1.y-l2.y) <= dist) ); } static void check_hits(void) { int sno; SHOT *s; float rad; int rno; RING *r; int bno; BOMB *b; int seg; for (sno=nshots-1;sno>=0;sno--) { s = &shots[sno]; rad = hypotf(castle.lv.loc.y-s->lv.loc.y,castle.lv.loc.x-s->lv.loc.x); for (rno=0;rno<3;rno++) { r = rings[rno]; if ( (rad < innerrad+r->rr+SHOTSIZE) && (rad > ((innerrad+r->rr)*(1-RINGDIP))-SHOTSIZE) ) { seg = 6 * angreduce_p(atan2f(s->lv.loc.y-castle.lv.loc.y,s->lv.loc.x-castle.lv.loc.x)-r->rot) / M_PI; if (seg > 11) seg -= 12; if (r->segs[seg]) { switch (--r->segs[seg]) { case 0: r->nsegs --; r->nsegs1 --; break; case 1: r->nsegs2 --; r->nsegs1 ++; break; } deadshot(sno); goto nextshot; /* continue 2 */ } } } for (bno=nbombs-1;bno>=0;bno--) { XY loc; b = bombs[bno]; switch (b->state) { case BS_HOPPING: loc = b->u.hopping.loc; break; case BS_ONRING: { float a; float r; a = b->u.onring.ring->rot + ((b->u.onring.seg * M_PI) / 6) + (M_PI / 12); r = (innerrad + b->u.onring.ring->rr) * (1-RINGDIP); loc.x = castle.lv.loc.x + (r * cosf(a)); loc.y = castle.lv.loc.y + (r * sinf(a)); } break; case BS_FREE: loc = b->u.free.lv.loc; break; } if (within(s->lv.loc,loc,BOMBSIZE+SHOTSIZE)) { deadshot(sno); deadbomb(bno); goto nextshot; /* continue 2 */ } } nextshot:; } } static void tick_castle(void) { float a; float da; float maxd; int i; RING *r; int s; maxcrv *= CASTLE_ROT_SPEEDUP; if (maxcrv > M_PI) maxcrv = M_PI; maxd = maxcrv; a = atan2f(ship.lv.loc.y-castle.lv.loc.y,ship.lv.loc.x-castle.lv.loc.x); da = angreduce_s(a-castle.r); if (fabsf(da) < maxd) { castle.r = a; if (! nfball) { for (i=0;i<3;i++) { r = rings[i]; s = 6 * angreduce_p(castle.r-r->rot-((((INNERRAD+r->rr)-CASTLESIZE)/FBALLVEL)*r->rv)) / M_PI; if (s > 11) s -= 12; if (r->segs[s]) break; } if (i >= 3) { nfball = 1; fball = castle.lv; fball.loc.x += CASTLESIZE * cosf(castle.r); fball.loc.y += CASTLESIZE * sinf(castle.r); fball.vel.x = FBALLVEL * cosf(castle.r); fball.vel.y = FBALLVEL * sinf(castle.r); } } } else { if (da > 0) { castle.r += maxd; } else { castle.r -= maxd; } castle.r = angreduce_s(castle.r); } if (!nfball && (nbombs < 3) && canbomb) { for (i=0;(i<3)&&((r=rings[i])->nsegs==0);i++) ; if (i < 3) { s = 6 * angreduce_p(castle.r-r->rot) / M_PI; if ((r->segs[s] == 2) || ((r->nsegs2 == 0) && (r->segs[s] == 1))) { BOMB *b; b = bombs[nbombs++]; b->state = BS_HOPPING; b->u.hopping.loc.x = castle.lv.loc.x + (CASTLESIZE * cosf(castle.r)); b->u.hopping.loc.y = castle.lv.loc.y + (CASTLESIZE * sinf(castle.r)); b->u.hopping.ring = r; b->u.hopping.ringser = r->serial; b->u.hopping.seg = s; b->u.hopping.ticks = 25; canbomb = 0; canbomb_fuse = add_fuse(500,setcanbomb,0); } } } } static void move_fball(void) { if (move_lv_off(&fball)) nfball = 0; } static void bomb_hop(void *bv) { BOMB *b; b = bv; if (b->state != BS_ONRING) abort(); b->u.onring.wanthop = 1; b->u.onring.hopfuse = 0; } static void tick_bombs(void) { #define H b->u.hopping #define O b->u.onring #define F b->u.free int bno; BOMB *b; for (bno=nbombs-1;bno>=0;bno--) { b = bombs[bno]; switch (b->state) { case BS_HOPPING: if ((H.ring->serial != H.ringser) || (H.ring->segs[H.seg] == 0)) { XY t; t = H.loc; b->state = BS_FREE; F.lv.loc = t; F.lv.vel.x = 0; F.lv.vel.y = 0; F.va = atan2f(ship.lv.loc.y-t.y,ship.lv.loc.x-t.x); F.vs = 0; } else if (--H.ticks < 1) { RING *r; int s; r = H.ring; s = H.seg; b->state = BS_ONRING; O.ring = r; O.ringser = r->serial; O.seg = s; O.wanthop = 0; O.hopfuse = add_fuse(250,bomb_hop,b); } else { float a; float r; XY dst; a = H.ring->rot + ((H.seg * M_PI) / 6) + (M_PI / 12); r = (innerrad + H.ring->rr) * (1-RINGDIP); dst.x = castle.lv.loc.x + (r * cosf(a)); dst.y = castle.lv.loc.y + (r * sinf(a)); H.loc.x = dst.x + (((H.loc.x - dst.x) * H.ticks) / (H.ticks + 1)); H.loc.y = dst.y + (((H.loc.y - dst.y) * H.ticks) / (H.ticks + 1)); } break; case BS_ONRING: { float a; float r; XY at; static void setat(void) { a = O.ring->rot + ((O.seg * M_PI) / 6) + (M_PI / 12); r = (innerrad + O.ring->rr) * (1-RINGDIP); at.x = castle.lv.loc.x + (r * cosf(a)); at.y = castle.lv.loc.y + (r * sinf(a)); } if ((O.ring->serial != O.ringser) || (O.ring->segs[O.seg] == 0)) { if (! O.wanthop) cancel_fuse(O.hopfuse); setat(); b->state = BS_FREE; F.lv.loc = at; F.lv.vel.x = 0; F.lv.vel.y = 0; F.va = atan2f(ship.lv.loc.y-at.y,ship.lv.loc.x-at.x); F.vs = 0; } else if (O.wanthop) { if (O.ring->inx == 2) { float rv; setat(); rv = O.ring->rv; b->state = BS_FREE; F.lv.loc = at; F.lv.vel.x = - r * sinf(a) * rv; F.lv.vel.y = r * cosf(a) * rv; F.va = a + ((rv > 0) ? (M_PI / 2) : -(M_PI / 2)); F.vs = hypot(F.lv.vel.x,F.lv.vel.y); } else { int i; RING *r; for (i=O.ring->inx+1;(i<3)&&((r=rings[i])->nsegs==0);i++) ; if (i < 3) { int s; s = 6 * angreduce_p(O.ring->rot+((O.seg*M_PI)/6)+(M_PI/12)-r->rot) / M_PI; if ((r->segs[s] == 2) || ((r->nsegs2 == 0) && (r->segs[s] == 1))) { setat(); b->state = BS_HOPPING; H.loc = at; H.ring = r; H.ringser = r->serial; H.seg = s; H.ticks = 25; } } } } } break; case BS_FREE: { float sa; float da; move_lv_off(&F.lv); sa = atan2f(ship.lv.loc.y-F.lv.loc.y,ship.lv.loc.x-F.lv.loc.x); da = angreduce_s(sa-F.va); if (fabsf(da) < BOMBTURN) { F.va = sa; } else if (da > 0) { F.va += BOMBTURN; } else { F.va -= BOMBTURN; } F.vs = BOMBVEL + ((F.vs - BOMBVEL) * BOMBACC); F.lv.vel.x = F.vs * cosf(F.va); F.lv.vel.y = F.vs * sinf(F.va); } break; default: abort(); break; } } #undef H #undef O #undef F } 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 5|EVK_DN: sstep(); break; case 5|EVK_UP: break; case 19|EVK_DN: fire(); break; case 19|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 119|EVK_DN: kbit |= BIT_L; break; case 119|EVK_UP: kbit &=~BIT_L; break; case 120|EVK_DN: kbit |= BIT_R; break; case 120|EVK_UP: kbit &=~BIT_R; break; case 122|EVK_DN: kbit |= BIT_T; break; case 122|EVK_UP: kbit &=~BIT_T; 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) { move_rings(); tick_castle(); move_ship(); move_shots(); tick_bombs(); move_fball(); check_hits(); } check_fuses(); break; case EVT_DRAW: wantrender = 1; break; } } } static void render_rings(void) { int i; RING *r; int s; int rad; int fg; float a1; float a2; for (i=0;i<3;i++) { r = rings[i]; rad = innerrad + r->rr; for (s=0;s<12;s++) { switch (r->segs[s]) { case 0: continue; break; case 1: fg = CM_RING_LIT; break; case 2: fg = CM_RING_DIM; break; } a1 = r->rot + ((s * M_PI) / 6); a2 = r->rot + (((s+1) * M_PI) / 6); fbline(fg, MIDX+(rad*cosf(a1)), MIDY+(rad*sinf(a1)), MIDX+(rad*cosf(a2)), MIDY+(rad*sinf(a2)) ); } } } static void render_lvr(int fg, LVR *lvr, float scale, const XY *pts, int npts) { int xv[npts]; int yv[npts]; int i; float s; float c; s = sinf(lvr->r) * scale; c = cosf(lvr->r) * scale; for (i=0;ilv.loc.x + (pts[i].x * c) - (pts[i].y * s); yv[i] = lvr->lv.loc.y + (pts[i].x * s) + (pts[i].y * c); } fblinev(fg,npts-1,&xv[0],&yv[0]); } static void render_castle(void) { render_lvr(CM_CASTLE,&castle,CASTLESIZE,&drawcastle[0],CASTLEPTS); } static void render_ship(void) { render_lvr(CM_SHIP,&ship,SHIPSIZE,&drawship[0],SHIPPTS); } static void render_shots(void) { int i; SHOT *s; int j; float a; for (i=0;ilv.loc.x,s->lv.loc.y,SHOTSIZE*cosf(a),SHOTSIZE*sinf(a)); } } } static void render_bombs(void) { int i; BOMB *b; int j; float a; float r; int x; int y; for (i=0;istate) { case BS_HOPPING: x = b->u.hopping.loc.x; y = b->u.hopping.loc.y; break; case BS_ONRING: a = b->u.onring.ring->rot + ((b->u.onring.seg * M_PI) / 6) + (M_PI / 12); r = (innerrad + b->u.onring.ring->rr) * (1-RINGDIP); x = castle.lv.loc.x + (r * cosf(a)); y = castle.lv.loc.y + (r * sinf(a)); break; case BS_FREE: x = b->u.free.lv.loc.x; y = b->u.free.lv.loc.y; break; default: abort(); break; } for (j=0;j<5;j++) { a = ((rnd() & 4095) * M_PI) / 2048; fbrline(CM_BOMB,x,y,BOMBSIZE*cosf(a),BOMBSIZE*sinf(a)); } } } static void render_fball(void) { int j; float a; if (! nfball) return; for (j=0;j<6;j++) { a = ((rnd() & 4095) * M_PI) / 2048; switch (rnd() & 3) { case 0: fbrline(CM_FBALL,fball.loc.x,fball.loc.y,FBALLSIZE*cosf(a),FBALLSIZE*sinf(a)); break; case 1: case 2: { float r; float a2; int xv[3]; int yv[3]; r = (((rnd() & 4095) + 2048) * FBALLSIZE) / 8192.0; a2 = ((2048 - (int)(rnd()&4095)) * M_PI) / 8190; xv[0] = fball.loc.x; yv[0] = fball.loc.y; xv[1] = fball.loc.x + (r * cosf(a)); yv[1] = fball.loc.y + (r * sinf(a)); xv[2] = fball.loc.x + (FBALLSIZE * cosf(a+a2)); yv[2] = fball.loc.y + (FBALLSIZE * sinf(a+a2)); fblinev(CM_FBALL,2,&xv[0],&yv[0]); } break; case 3: { float r; float a2; float a3; int xv[3]; int yv[3]; r = (((rnd() & 4095) + 2048) * FBALLSIZE) / 8192.0; a2 = ((2500 - (int)(rnd()&4095)) * M_PI) / 7000; a3 = (((int)(rnd()&4095) - 2500) * M_PI) / 7000; xv[0] = fball.loc.x; yv[0] = fball.loc.y; xv[1] = fball.loc.x + (r * cosf(a)); yv[1] = fball.loc.y + (r * sinf(a)); xv[2] = fball.loc.x + (FBALLSIZE * cosf(a+a2)); yv[2] = fball.loc.y + (FBALLSIZE * sinf(a+a2)); fblinev(CM_FBALL,2,&xv[0],&yv[0]); xv[2] = fball.loc.x + (FBALLSIZE * cosf(a+a3)); yv[2] = fball.loc.y + (FBALLSIZE * sinf(a+a3)); fblinev(CM_FBALL,1,&xv[1],&yv[1]); } break; } } } static void mayberender(void) { if (! wantrender) return; wantrender = 0; drain(); fbrect(CM_BLACK,0,0,1151,899); switch (state) { case STATE_PLAYING: render_rings(); render_castle(); render_ship(); render_shots(); render_fball(); render_bombs(); break; } drain(); setcmap(drawbit); drawbit = ! drawbit; fb->pm = drawbit ? 0xf0 : 0x0f; } static void step(void) { struct pollfd pfd; process_evq(); mayberender(); if (checkkb()) return; if (checktick()) return; pfd.fd = kbfd; pfd.events = POLLIN | POLLRDNORM; if (poll(&pfd,1,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; } int main(void); int main(void) { setupfb(); setupkb(); setuptimer(); initevq(); initfuses(); initrandom(); initgame(); wantrender = 1; while (1) step(); exit(0); }