diff options
author | rodri <rgl@antares-labs.eu> | 2020-02-20 21:56:37 +0000 |
---|---|---|
committer | rodri <rgl@antares-labs.eu> | 2020-02-20 21:56:37 +0000 |
commit | e1cb9df9355a6ae67aac76dbcde209b8f70796ee (patch) | |
tree | 2a5a01e89fb4b1f9bd497a318fb59e0e34622d60 | |
download | asteroids-master.tar.gz asteroids-master.tar.bz2 asteroids-master.zip |
-rw-r--r-- | dat.h | 73 | ||||
-rw-r--r-- | fns.h | 18 | ||||
-rw-r--r-- | main.c | 509 | ||||
-rw-r--r-- | mkfile | 13 | ||||
-rw-r--r-- | notes | 3 | ||||
-rw-r--r-- | triangle.c | 58 | ||||
-rw-r--r-- | util.c | 40 | ||||
-rw-r--r-- | vector.c | 54 |
8 files changed, 768 insertions, 0 deletions
@@ -0,0 +1,73 @@ +#define DEG 0.01745329251994330 + +enum { + STACKSZ = 8192, + SEC = 1000, + THRUST = 50, + BSPEED = 300, + FPS = 60, + Maxbisect = 4 +}; + +enum { + K↑, + K←, + K→, + Kfire, + Knav, + Kquit, + Ke +}; + +enum { + Casteroid, + Cthrust, + Cbullet, + Cprov, + Cretrov, + Cend +}; + +enum { + Sscore, + Sshield, + Sammo, + Se +}; + +typedef struct Vector Vector; +typedef struct Triangle Triangle; +typedef struct Particle Particle; +typedef struct Asteroid Asteroid; +typedef struct Bullet Bullet; +typedef struct Spacecraft Spacecraft; + +struct Vector { + double x, y; +}; + +struct Triangle { + Point p0, p1, p2; +}; + +struct Particle { + Vector p, v; + double yaw; +}; + +struct Asteroid { + Particle; + int stillin, bisectno; + Asteroid *prev, *next; +}; + +struct Bullet { + Particle; + int fired; +}; + +struct Spacecraft { + Particle; + int shields; + Bullet ammo[5]; +}; @@ -0,0 +1,18 @@ +Triangle Trian(int, int, int, int, int, int); +Triangle Trianpt(Point, Point, Point); +Point centroid(Triangle); +void triangle(Image *, Triangle, int, Image *, Point); +void filltriangle(Image *, Triangle, Image *, Point); +Triangle rotatriangle(Triangle, double, Point); +Vector Vec(double, double); +Vector Vpt(Point); +Vector addvec(Vector, Vector); +Vector subvec(Vector, Vector); +Vector mulvec(Vector, double); +double dotvec(Vector, Vector); +Vector normvec(Vector); +double round(double); +Point rotatept(Point, double, Point); +int ptincircle(Point, Point, double); +int triangleXcircle(Triangle, Point, double); +void *emalloc(ulong); @@ -0,0 +1,509 @@ +#include <u.h> +#include <libc.h> +#include <thread.h> +#include <draw.h> +#include <mouse.h> +#include <keyboard.h> +#include "dat.h" +#include "fns.h" + +Rune keys[Ke] = { + [K↑] Kup, + [K←] Kleft, + [K→] Kright, + [Kfire] ' ', + [Knav] 'n', + [Kquit] 'q' +}; + +Mousectl *mctl; +int kdown; +Channel *scrsync; +Point orig; +Vector basis; +Spacecraft ship; +Asteroid asteroids; +int nasteroid; +Triangle shipmdl, thrustmdl; +Point asteroidmdl[Maxbisect][16]; +Image *colors[Cend]; +double t0, Δt; +int shownav, showthrust; +QLock pauselk; +int paused; +int ∞flag; + +char stats[Se][32]; +int score; + +void * +emalloc(ulong n) +{ + void *p; + + p = malloc(n); + if(p == nil) + sysfatal("malloc: %r"); + memset(p, 0, n); + setmalloctag(p, getcallerpc(&n)); + return p; +} + +void +addasteroid(Asteroid *a) +{ + asteroids.prev->next = a; + a->prev = asteroids.prev; + a->next = &asteroids; + asteroids.prev = a; +} + +Asteroid * +delasteroid(Asteroid *a) +{ + Asteroid *an; + + an = a->next; + a->prev->next = an; + an->prev = a->prev; + free(a); + return an; +} + +void +splitasteroid(Asteroid *a) +{ + Asteroid *newa; + + newa = emalloc(sizeof(Asteroid)); + newa->p = a->p; + newa->v.x = a->v.x*cos(PI/2) - a->v.y*sin(PI/2); + newa->v.y = a->v.x*sin(PI/2) + a->v.y*cos(PI/2); + newa->bisectno = a->bisectno; + addasteroid(newa); + newa = emalloc(sizeof(Asteroid)); + newa->p = a->p; + newa->v.x = a->v.x*cos(-PI/2) - a->v.y*sin(-PI/2); + newa->v.y = a->v.x*sin(-PI/2) + a->v.y*cos(-PI/2); + newa->bisectno = a->bisectno; + addasteroid(newa); + nasteroid += 2; +} + +Point +vectopt(Vector v) +{ + return Pt(v.x, v.y); +} + +Point +toscreen(Vector p) +{ + return addpt(orig, Pt(p.x*basis.x, p.y*basis.y)); +} + +Vector +fromscreen(Point p) +{ + return Vec(p.x-screen->r.min.x, screen->r.max.y-p.y); +} + +void +wrapcoord(Vector *p) +{ + if(p->x > Dx(screen->r)) + p->x = 0; + if(p->y > Dy(screen->r)) + p->y = 0; + if(p->x < 0) + p->x = Dx(screen->r); + if(p->y < 0) + p->y = Dy(screen->r); +} + +void +drawstats(void) +{ + int i, nfired; + + snprint(stats[Sscore], sizeof(stats[Sscore]), "SCORE %d", score); + if(∞flag) + snprint(stats[Sshield], sizeof(stats[Sshield]), "SHIELDS ∞"); + else + snprint(stats[Sshield], sizeof(stats[Sshield]), "SHIELDS %d", ship.shields); + for(i = nfired = 0; i < nelem(ship.ammo); i++) + if(ship.ammo[i].fired) + nfired++; + snprint(stats[Sammo], sizeof(stats[Sammo]), "AMMO %d/%d", nelem(ship.ammo)-nfired, nelem(ship.ammo)); + for(i = 0; i < Se; i++) + stringn(screen, addpt(screen->r.min, Pt(10, 10 + i*font->height)), display->white, ZP, font, stats[i], strlen(stats[i])); +} + +void +drawship(void) +{ + Triangle shipmdltrans, thrustmdltrans; + int i; + + shipmdltrans = rotatriangle(shipmdl, ship.yaw, Pt(0, 0)); + triangle(screen, Trianpt( + toscreen(addvec(ship.p, Vpt(shipmdltrans.p0))), + toscreen(addvec(ship.p, Vpt(shipmdltrans.p1))), + toscreen(addvec(ship.p, Vpt(shipmdltrans.p2))) + ), 0, display->white, ZP); + if(showthrust){ + thrustmdltrans = rotatriangle(thrustmdl, ship.yaw, Pt(0, 0)); + filltriangle(screen, Trianpt( + toscreen(addvec(ship.p, Vpt(thrustmdltrans.p0))), + toscreen(addvec(ship.p, Vpt(thrustmdltrans.p1))), + toscreen(addvec(ship.p, Vpt(thrustmdltrans.p2))) + ), colors[Cthrust], ZP); + } + for(i = 0; i < nelem(ship.ammo); i++) + if(ship.ammo[i].fired) + line(screen, toscreen(ship.ammo[i].p), toscreen(subvec(ship.ammo[i].p, Vec(cos(ship.ammo[i].yaw)*8, sin(ship.ammo[i].yaw)*8))), 0, 0, 0, colors[Cbullet], ZP); +} + +void +drawasteroids(void) +{ + Asteroid *ap; + Point asteroidmdltrans[16]; + int i; + + for(ap = asteroids.next; ap != &asteroids; ap = ap->next){ + for(i = 0; i < nelem(asteroidmdl[ap->bisectno]); i++) + asteroidmdltrans[i] = toscreen(addvec(ap->p, Vpt(asteroidmdl[ap->bisectno][i]))); + poly(screen, asteroidmdltrans, nelem(asteroidmdltrans), 0, 0, 0, colors[Casteroid], ZP); + } +} + +void +redraw(void) +{ + + lockdisplay(display); + draw(screen, screen->r, display->black, nil, ZP); + if(shownav){ + line(screen, toscreen(ship.p), toscreen(addvec(ship.p, ship.v)), 0, 0, 0, colors[Cprov], ZP); + line(screen, toscreen(ship.p), toscreen(addvec(ship.p, mulvec(ship.v, -1))), 0, 0, 0, colors[Cretrov], ZP); + } + drawship(); + drawasteroids(); + drawstats(); + if(paused) + stringn(screen, addpt(screen->r.min, Pt(Dx(screen->r)/2-3*font->width, Dy(screen->r)/2)), display->white, ZP, font, "PAUSED", 6); + flushimage(display, 1); + unlockdisplay(display); +} + +void +congrats(void) +{ + char *s[] = { + "CONGRATS!", + "YOU DID IT!" + }; + int i; + + lockdisplay(display); + draw(screen, screen->r, display->black, nil, ZP); + for(i = 0; i < nelem(s); i++) + stringn(screen, addpt(divpt(addpt(screen->r.min, screen->r.max), 2), Pt(-strlen(s[i])/2*font->width, i*font->height)), colors[Cprov], ZP, font, s[i], strlen(s[i])); + flushimage(display, 1); + unlockdisplay(display); + sleep(5*SEC); + threadexitsall(nil); +} + +void +gameover(void) +{ + char s[] = "GAME OVER"; + char ss[48]; + + snprint(ss, sizeof ss, "FINAL SCORE %d", score); + lockdisplay(display); + draw(screen, screen->r, display->black, nil, ZP); + stringn(screen, addpt(divpt(addpt(screen->r.min, screen->r.max), 2), Pt(-strlen(s)/2*font->width, 0)), colors[Cretrov], ZP, font, s, strlen(s)); + stringn(screen, addpt(divpt(addpt(screen->r.min, screen->r.max), 2), Pt(-strlen(ss)/2*font->width, font->height)), colors[Cretrov], ZP, font, ss, strlen(ss)); + flushimage(display, 1); + unlockdisplay(display); + sleep(5*SEC); + threadexitsall(nil); +} + +void +fire(void) +{ + int i; + + for(i = 0; i < nelem(ship.ammo); i++) + if(!ship.ammo[i].fired){ + ship.ammo[i].p = ship.p; + ship.ammo[i].v = addvec(ship.v, Vec(cos(ship.yaw)*BSPEED, sin(ship.yaw)*BSPEED)); + ship.ammo[i].yaw = ship.yaw; + ship.ammo[i].fired++; + break; + } +} + +void +scrsynproc(void *) +{ + for(;;){ + send(scrsync, nil); + sleep(SEC/FPS); + } +} + +void +mouse(void) +{ + Vector p; + + Δt = (nsec()-t0)/1e9; + kdown &= ~(1<<K↑); + if(mctl->buttons & 1){ + p = subvec(fromscreen(mctl->xy), ship.p); + ship.yaw = atan2(p.y, p.x); + kdown |= 1<<K↑; + } +} + +void +kbdproc(void *) +{ + Rune r, *a; + char buf[128], *s; + int fd, n; + + threadsetname("kbdproc"); + if((fd = open("/dev/kbd", OREAD)) < 0) + sysfatal("kbdproc: %r"); + memset(buf, 0, sizeof buf); + for(;;){ + if(buf[0] != 0){ + n = strlen(buf)+1; + memmove(buf, buf+n, sizeof(buf)-n); + } + if(buf[0] == 0){ + if((n = read(fd, buf, sizeof(buf)-1)) <= 0) + break; + buf[n-1] = 0; + buf[n] = 0; + } + if(buf[0] == 'c'){ + if(utfrune(buf, Kdel)){ + close(fd); + threadexitsall(nil); + } + if(utfrune(buf, Kesc)){ + if(paused) + qunlock(&pauselk); + else + qlock(&pauselk); + paused = !paused; + redraw(); + } + } + if(buf[0] != 'k' && buf[0] != 'K') + continue; + s = buf+1; + kdown = 0; + while(*s){ + s += chartorune(&r, s); + for(a = keys; a < keys+Ke; a++) + if(r == *a){ + kdown |= 1 << a-keys; + break; + } + } + } +} + +void +handlekeys(void) +{ + showthrust = 0; + if(kdown & 1<<Kquit) + threadexitsall(nil); + if(kdown & 1<<K←) + ship.yaw += PI * Δt; + if(kdown & 1<<K→) + ship.yaw -= PI * Δt; + if(kdown & 1<<K↑){ + showthrust = 1; + ship.v = addvec(ship.v, Vec( + cos(ship.yaw) * THRUST*Δt, + sin(ship.yaw) * THRUST*Δt)); + } + if(kdown & 1<<Kfire) + fire(), kdown &= ~(1<<Kfire); + if(kdown & 1<<Knav) + shownav = !shownav, kdown &= ~(1<<Knav); +} + +void +resized(void) +{ + lockdisplay(display); + if(getwindow(display, Refnone) < 0) + fprint(2, "can't reattach to window\n"); + unlockdisplay(display); + orig = Pt(screen->r.min.x, screen->r.max.y); + redraw(); +} + +Image * +rgb(u32int c) +{ + Image *i; + + if((i = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, c)) == nil) + sysfatal("allocimage: %r"); + return i; +} + +void +initcolors(void) +{ + colors[Casteroid] = rgb(0xcc8844ff); + colors[Cthrust] = rgb(DYellow); + colors[Cbullet] = rgb(DRed); + colors[Cprov] = rgb(DGreen); + colors[Cretrov] = rgb(DYellow); +} + +void +usage(void) +{ + fprint(2, "usage: %s\n", argv0); + threadexitsall("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + Asteroid *ap, *newa; + char *s; + double elev, avgelev[Maxbisect]; + int i, j, waspaused; + + nasteroid = 4; + ARGBEGIN{ + case 'n': + nasteroid = strtol(EARGF(usage()), &s, 0); + if(s != nil) + usage(); + break; + case L'∞': ∞flag++; break; + default: usage(); + }ARGEND; + + if(initdraw(nil, nil, "asteroids") < 0) + sysfatal("initdraw: %r"); + if((mctl = initmouse(nil, screen)) == nil) + sysfatal("initmouse: %r"); + initcolors(); + srand(time(0)); + orig = Pt(screen->r.min.x, screen->r.max.y); + basis = Vec(1, -1); + memset(avgelev, 0, sizeof avgelev); + for(i = 0; i < Maxbisect; i++){ + for(j = 0; j < nelem(asteroidmdl[i])-1; j++){ + elev = frand()*(80-16*i) + (75-16*i); + asteroidmdl[i][j] = Pt( + elev*cos(((double)j/nelem(asteroidmdl[i])) * 2*PI), + elev*sin(((double)j/nelem(asteroidmdl[i])) * 2*PI) + ); + avgelev[i] += elev; + } + asteroidmdl[i][j] = asteroidmdl[i][0]; + avgelev[i] /= j; + } + shipmdl = Trian( + 8, 0, + -4, 4, + -4, -4 + ); + thrustmdl = Trian( + -12, 0, + -4, 2, + -4, -2 + ); + ship.p = Vec(Dx(screen->r)/2, Dy(screen->r)/2); + ship.yaw = 90*DEG; + ship.shields = 5; + asteroids.prev = asteroids.next = &asteroids; + for(i = 0; i < nasteroid; i++){ + srand(nsec()); + newa = emalloc(sizeof(Asteroid)); + newa->p = Vec( + ntruerand(Dx(screen->r)), + ntruerand(Dy(screen->r)) + ); + newa->v = Vec(cos(frand() * 2*PI)*50+10, sin(frand() * 2*PI)*50+10); + addasteroid(newa); + } + display->locking = 1; + unlockdisplay(display); + scrsync = chancreate(1, 0); + proccreate(scrsynproc, nil, STACKSZ); + proccreate(kbdproc, nil, STACKSZ); + t0 = nsec(); + for(;;){ + enum{MOUSE, RESIZE, SCRSYN}; + Alt a[] = { + {mctl->c, &mctl->Mouse, CHANRCV}, + {mctl->resizec, nil, CHANRCV}, + {scrsync, nil, CHANRCV}, + {nil, nil, CHANEND} + }; + switch(alt(a)){ + case MOUSE: mouse(); break; + case RESIZE: resized(); break; + case SCRSYN: redraw(); break; + } + waspaused = paused; + qlock(&pauselk); + if(waspaused) + t0 = nsec(); + Δt = (nsec()-t0)/1e9; + handlekeys(); + ship.p = addvec(ship.p, mulvec(ship.v, Δt)); + wrapcoord(&ship.p); + for(i = 0; i < nelem(ship.ammo); i++) + if(ship.ammo[i].fired){ + ship.ammo[i].p = addvec(ship.ammo[i].p, mulvec(ship.ammo[i].v, Δt)); + if(!ptinrect(vectopt(ship.ammo[i].p), Rect(0, 0, Dx(screen->r), Dy(screen->r)))) + ship.ammo[i].fired--; + } + for(ap = asteroids.next; ap != &asteroids; ap = ap->next){ + ap->p = addvec(ap->p, mulvec(ap->v, Δt)); + wrapcoord(&ap->p); + if(!∞flag && triangleXcircle(Trianpt( + addpt(vectopt(ship.p), shipmdl.p0), + addpt(vectopt(ship.p), shipmdl.p1), + addpt(vectopt(ship.p), shipmdl.p2) + ), vectopt(ap->p), avgelev[ap->bisectno])){ + if(!ap->stillin && --ship.shields == 0) + gameover(); + ap->stillin = 1; + }else + ap->stillin = 0; + for(i = 0; i < nelem(ship.ammo); i++) + if(ship.ammo[i].fired && ptincircle(vectopt(ship.ammo[i].p), vectopt(ap->p), avgelev[ap->bisectno])){ + ship.ammo[i].fired--; + score += 50*ap->bisectno; + if(++ap->bisectno < Maxbisect) + splitasteroid(ap); + ap = delasteroid(ap); + if(--nasteroid == 0) + congrats(); + } + } + t0 += Δt*1e9; + qunlock(&pauselk); + } +} @@ -0,0 +1,13 @@ +</$objtype/mkfile + +BIN=/$objtype/bin/ +TARG=asteroids +OFILES=\ + main.$O\ + util.$O\ + vector.$O\ + triangle.$O + +HFILES=dat.h fns.h + +</sys/src/cmd/mkone @@ -0,0 +1,3 @@ +• replace 'vlong t0, t;' by 'double t0, Δt;' so we only update t0 at the +main loop and Δt every time we do dynamics. +• implement bullets and sheer destruction. diff --git a/triangle.c b/triangle.c new file mode 100644 index 0000000..5301956 --- /dev/null +++ b/triangle.c @@ -0,0 +1,58 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include "dat.h" +#include "fns.h" + +Triangle +Trian(int x0, int y0, int x1, int y1, int x2, int y2) +{ + return (Triangle){Pt(x0, y0), Pt(x1, y1), Pt(x2, y2)}; +} + +Triangle +Trianpt(Point p0, Point p1, Point p2) +{ + return (Triangle){p0, p1, p2}; +}; + +Point +centroid(Triangle t) +{ + return divpt(addpt(t.p0, addpt(t.p1, t.p2)), 3); +} + +void +triangle(Image *dst, Triangle t, int thick, Image *src, Point sp) +{ + Point pl[4]; + + pl[0] = t.p0; + pl[1] = t.p1; + pl[2] = t.p2; + pl[3] = pl[0]; + + poly(dst, pl, nelem(pl), 0, 0, thick, src, sp); +} + +void +filltriangle(Image *dst, Triangle t, Image *src, Point sp) +{ + Point pl[4]; + + pl[0] = t.p0; + pl[1] = t.p1; + pl[2] = t.p2; + pl[3] = pl[0]; + + fillpoly(dst, pl, nelem(pl), 0, src, sp); +} + +Triangle +rotatriangle(Triangle t, double θ, Point c) +{ + t.p0 = rotatept(t.p0, θ, c); + t.p1 = rotatept(t.p1, θ, c); + t.p2 = rotatept(t.p2, θ, c); + return t; +} @@ -0,0 +1,40 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include "dat.h" +#include "fns.h" + +double +round(double n) +{ + return floor(n + 0.5); +} + +Point +rotatept(Point p, double θ, Point c) +{ + Point r; + + p = subpt(p, c); + r.x = round(p.x*cos(θ) - p.y*sin(θ)); + r.y = round(p.x*sin(θ) + p.y*cos(θ)); + r = addpt(r, c); + return r; +} + +int +ptincircle(Point p, Point c, double r) +{ + Point d; + + d = subpt(c, p); + return hypot(d.x, d.y) < r; +} + +int +triangleXcircle(Triangle t, Point c, double r) +{ + return ptincircle(t.p0, c, r) || + ptincircle(t.p1, c, r) || + ptincircle(t.p2, c, r); +} diff --git a/vector.c b/vector.c new file mode 100644 index 0000000..21a3711 --- /dev/null +++ b/vector.c @@ -0,0 +1,54 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include "dat.h" +#include "fns.h" + +Vector +Vec(double x, double y) +{ + return (Vector){x, y}; +} + +Vector +Vpt(Point p) +{ + return (Vector){p.x, p.y}; +} + +Vector +addvec(Vector v, Vector u) +{ + return (Vector){v.x+u.x, v.y+u.y}; +} + +Vector +subvec(Vector v, Vector u) +{ + return (Vector){v.x-u.x, v.y-u.y}; +} + +Vector +mulvec(Vector v, double s) +{ + return (Vector){v.x*s, v.y*s}; +} + +double +dotvec(Vector v, Vector u) +{ + return v.x*u.x + v.y*u.y; +} + +Vector +normvec(Vector v) +{ + double len; + + len = hypot(v.x, v.y); + if(len == 0) + return (Vector){0, 0}; + v.x /= len; + v.y /= len; + return v; +} |