From e095f65376894dcdef067b63103f2e17e55a1f14 Mon Sep 17 00:00:00 2001 From: rodri Date: Mon, 22 May 2023 09:00:42 +0000 Subject: improvements to show a bullet's explosion upon ttl consumption. implemented a vfx subsystem to handle localized, async animations. --- assets/vfx/bullet.explosion.png | Bin 0 -> 10209 bytes dat.h | 14 +++++++- fns.h | 8 +++++ mkfile | 1 + musw.c | 49 ++++++++++++++++++++++++---- muswd.c | 4 +-- sprite.c | 9 +++++- todo | 5 ++- universe.c | 15 +++++---- vfx.c | 69 ++++++++++++++++++++++++++++++++++++++++ 10 files changed, 156 insertions(+), 18 deletions(-) create mode 100644 assets/vfx/bullet.explosion.png create mode 100644 vfx.c diff --git a/assets/vfx/bullet.explosion.png b/assets/vfx/bullet.explosion.png new file mode 100644 index 0000000..ba6bfa8 Binary files /dev/null and b/assets/vfx/bullet.explosion.png differ diff --git a/dat.h b/dat.h index 4c9e019..024c1c9 100644 --- a/dat.h +++ b/dat.h @@ -59,6 +59,7 @@ enum { typedef struct VModel VModel; typedef struct Sprite Sprite; +typedef struct Vfx Vfx; typedef struct Keymap Keymap; typedef struct State State; typedef struct Particle Particle; @@ -101,6 +102,18 @@ struct Sprite void (*step)(Sprite*, ulong); void (*draw)(Sprite*, Image*, Point); + Sprite *(*clone)(Sprite*); +}; + +struct Vfx +{ + Sprite *a; /* animation */ + Point p; + int times; /* to repeat. -1 loops forever */ + Vfx *prev, *next; + + void (*step)(Vfx*, ulong); + void (*draw)(Vfx*, Image*); }; struct Keymap @@ -133,7 +146,6 @@ struct Bullet struct Ship { Particle; - Kind kind; int fuel; Bullet rounds[10]; VModel *mdl; diff --git a/fns.h b/fns.h index 039081d..20a58f6 100644 --- a/fns.h +++ b/fns.h @@ -49,6 +49,14 @@ Sprite *readsprite(char*, Point, Rectangle, int, ulong); Sprite *readpngsprite(char*, Point, Rectangle, int, ulong); void delsprite(Sprite*); +/* + * vfx + */ +Vfx *newvfx(Sprite*, Point, int); +void delvfx(Vfx*); +void addvfx(Vfx*, Vfx*); +void initvfx(Vfx*); + /* * net */ diff --git a/mkfile b/mkfile index 123c050..f30d877 100644 --- a/mkfile +++ b/mkfile @@ -14,6 +14,7 @@ OFILES=\ party.$O\ universe.$O\ sprite.$O\ + vfx.$O\ net.$O\ fmt.$O\ diff --git a/musw.c b/musw.c index 836a56c..ef5d331 100644 --- a/musw.c +++ b/musw.c @@ -20,6 +20,11 @@ enum { NGAMESTATES }; +enum { + VFX_BULLET_EXPLOSION, + NVFX +}; + Keymap kmap[] = { {.key = Kup, .op = K↑}, {.key = Kleft, .op = K↺}, @@ -35,11 +40,13 @@ Keymap kmap[] = { ulong kdown; RFrame screenrf; -Universe *universe; +Universe *universe, *olduniverse; VModel *needlemdl, *wedgemdl; Image *screenb; Image *skymap; Sprite *intro; +Sprite *vfxtab[NVFX]; +Vfx vfxqueue; State gamestates[NGAMESTATES]; State *gamestate; Channel *ingress; @@ -65,6 +72,16 @@ fromscreen(Point p) return rframexform(Pt2(p.x,p.y,1), screenrf); } +void +swapuniverses(void) +{ + Universe *u; + + u = universe; + universe = olduniverse; + olduniverse = u; +} + /* * readvmodel and drawship are testing routines * that will later be implemented as VModel methods. @@ -166,10 +183,11 @@ drawbullets(Ship *ship, Image *dst) Point2 v; for(i = 0; i < nelem(ship->rounds); i++){ - if(!ship->rounds[i].fired) + b = &ship->rounds[i]; + + if(!b->fired) continue; - b = &ship->rounds[i]; v = Vec2(-1,0); /* it's pointing backwards to paint the tail */ Matrix R = { cos(b->θ), -sin(b->θ), 0, @@ -406,6 +424,8 @@ threadnetppu(void *) case NSsimstate: weplaying = 1; + swapuniverses(); + bufp = frame->data; bufp += unpack(bufp, frame->len, "PdPdP", &universe->ships[0].p, &universe->ships[0].θ, @@ -431,6 +451,11 @@ threadnetppu(void *) &universe->ships[i].rounds[bi].p, &universe->ships[i].rounds[bi].θ); universe->ships[i].rounds[bi].fired++; } + + for(i = 0; i < nelem(universe->ships); i++) + for(j = 0; j < nelem(universe->ships[i].rounds); j++) + if(!universe->ships[i].rounds[j].fired && olduniverse->ships[i].rounds[j].fired) + addvfx(&vfxqueue, newvfx(vfxtab[VFX_BULLET_EXPLOSION]->clone(vfxtab[VFX_BULLET_EXPLOSION]), toscreen(universe->ships[i].rounds[j].p), 1)); break; case NSnudge: newf = newframe(nil, NCnudge, frame->seq+1, frame->seq, 0, nil); @@ -557,6 +582,8 @@ drawprogressing(char *s) void redraw(void) { + Vfx *vfx; + lockdisplay(display); if(doghosting) @@ -581,6 +608,9 @@ redraw(void) break; } + for(vfx = vfxqueue.next; vfx != &vfxqueue; vfx = vfx->next) + vfx->draw(vfx, screenb); + draw(screen, screen->r, screenb, nil, ZP); flushimage(display, 1); @@ -659,6 +689,7 @@ threadmain(int argc, char *argv[]) double frametime; char *server; int fd; + Vfx *vfx; Mousectl *mc; Ioproc *io; @@ -704,18 +735,22 @@ threadmain(int argc, char *argv[]) sysfatal("dial: %r"); universe = newuniverse(); + olduniverse = newuniverse(); needlemdl = readvmodel("assets/mdl/needle.vmdl"); if(needlemdl == nil) sysfatal("readvmodel: %r"); wedgemdl = readvmodel("assets/mdl/wedge.vmdl"); if(wedgemdl == nil) sysfatal("readvmodel: %r"); - universe->ships[0].mdl = needlemdl; - universe->ships[1].mdl = wedgemdl; - universe->star.spr = readpngsprite("assets/spr/pulsar.png", ZP, Rect(0,0,64,64), 9, 50); + olduniverse->ships[0].mdl = universe->ships[0].mdl = needlemdl; + olduniverse->ships[1].mdl = universe->ships[1].mdl = wedgemdl; + olduniverse->star.spr = universe->star.spr = readpngsprite("assets/spr/pulsar.png", ZP, Rect(0,0,64,64), 9, 50); intro = readpngsprite("assets/spr/intro.png", ZP, Rect(0,0,640,480), 28, 100); + vfxtab[VFX_BULLET_EXPLOSION] = readpngsprite("assets/vfx/bullet.explosion.png", ZP, Rect(0, 0, 32, 32), 12, 100); + initvfx(&vfxqueue); + gamestates[GSIntro].δ = intro_δ; gamestates[GSConnecting].δ = connecting_δ; gamestates[GSMatching].δ = matching_δ; @@ -740,6 +775,8 @@ threadmain(int argc, char *argv[]) switch(gamestate-gamestates){ case GSPlaying: universe->star.spr->step(universe->star.spr, frametime/1e6); + for(vfx = vfxqueue.next; vfx != &vfxqueue; vfx = vfx->next) + vfx->step(vfx, frametime/1e6); /* fallthrough */ default: if(netconn.state == NCSConnecting) diff --git a/muswd.c b/muswd.c index 65c1af0..4f871d8 100644 --- a/muswd.c +++ b/muswd.c @@ -418,8 +418,8 @@ fprintstates(int fd) for(p = theparty.next; p != &theparty; p = p->next, i++){ for(s = &p->u->ships[0]; s-p->u->ships < nelem(p->u->ships); s++){ - fprint(fd, "%ld s%lld k%d p %v v %v θ %g ω %g m %g f %d\n", - i, s-p->u->ships, s->kind, s->p, s->v, s->θ, s->ω, s->mass, s->fuel); + fprint(fd, "%ld s%lld p %v v %v θ %g ω %g m %g f %d\n", + i, s-p->u->ships, s->p, s->v, s->θ, s->ω, s->mass, s->fuel); } fprint(fd, "%ld S p %v m %g\n", i, p->u->star.p, p->u->star.mass); } diff --git a/sprite.c b/sprite.c index d000768..4af7c54 100644 --- a/sprite.c +++ b/sprite.c @@ -35,6 +35,12 @@ sprite_draw(Sprite *spr, Image *dst, Point dp) draw(dst, rectaddpt(spr->r, dp), spr->sheet, nil, sp); } +static Sprite * +sprite_clone(Sprite *spr) +{ + return newsprite(spr->sheet, spr->sp, spr->r, spr->nframes, spr->period); +} + Sprite * newsprite(Image *sheet, Point sp, Rectangle r, int nframes, ulong period) { @@ -50,6 +56,7 @@ newsprite(Image *sheet, Point sp, Rectangle r, int nframes, ulong period) spr->elapsed = 0; spr->step = sprite_step; spr->draw = sprite_draw; + spr->clone = sprite_clone; return spr; } @@ -111,6 +118,6 @@ readpngsprite(char *sheetfile, Point sp, Rectangle r, int nframes, ulong period) void delsprite(Sprite *spr) { - freeimage(spr->sheet); + //freeimage(spr->sheet); free(spr); } diff --git a/todo b/todo index 093309a..a2cd2b9 100644 --- a/todo +++ b/todo @@ -1,17 +1,20 @@ [ ] collision detection [✓] toroidal warping -[✓] respect bullets's ttl +[ ] respect bullets's ttl [✓] communicate this event to the clients (δ: fired 1 → 0) [ ] explode when the time comes [ ] fuel consumption [ ] hyperjump [ ] minskytron effect [ ] engine damage on every jump +[ ] nozzle exhaust plume rendering [✓] different screens for each game state [✓] intro [✓] connecting to server [✓] waiting for a player [✓] main game +[ ] keep a score +[ ] manage health [ ] reduce the amount of data sent on every NSsimstate packet [✓] only send the fired bullets [ ] bit packing diff --git a/universe.c b/universe.c index ceb3f1d..0278493 100644 --- a/universe.c +++ b/universe.c @@ -103,12 +103,15 @@ universe_collide(Universe *u) Bullet *b; for(s = u->ships; s < u->ships+nelem(u->ships); s++){ - for(b = s->rounds; b < s->rounds+nelem(s->rounds); b++){ - if(b->fired && b->ttl <= 0) - b->fired = 0; - if(b->fired) + for(b = s->rounds; b < s->rounds+nelem(s->rounds); b++) + if(b->fired){ + if(b->ttl <= 0){ + b->fired = 0; + continue; + } warp(b); - } + + } warp(s); } } @@ -152,11 +155,9 @@ inituniverse(Universe *u) } u->ships[0].mass = 10e3; /* 10 tons */ - u->ships[0].kind = NEEDLE; u->ships[0].fuel = 100; u->ships[1].mass = 40e3; /* 40 tons */ - u->ships[1].kind = WEDGE; u->ships[1].fuel = 200; u->ships[0].forward = u->ships[1].forward = ship_forward; diff --git a/vfx.c b/vfx.c new file mode 100644 index 0000000..69f292f --- /dev/null +++ b/vfx.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" + +static void +vfx_step(Vfx *v, ulong Δt) +{ + if(v->times == 0 && v->a->curframe == 0){ + delvfx(v); + return; + } + + v->a->step(v->a, Δt); + + if(v->times > 0 && v->a->curframe == v->a->nframes-1) + v->times--; +} + +static void +vfx_draw(Vfx *v, Image *dst) +{ + v->a->draw(v->a, dst, v->p); +} + +Vfx * +newvfx(Sprite *spr, Point dp, int repeat) +{ + Vfx *v; + + v = emalloc(sizeof(Vfx)); + v->a = spr; + v->p = dp; + v->times = repeat; + v->step = vfx_step; + v->draw = vfx_draw; + + return v; +} + +void +delvfx(Vfx *v) +{ + v->next->prev = v->prev; + v->prev->next = v->next; + delsprite(v->a); + free(v); +} + +void +addvfx(Vfx *v, Vfx *nv) +{ + nv->prev = v->prev; + nv->next = v; + v->prev->next = nv; + v->prev = nv; +} + +void +initvfx(Vfx *v) +{ + v->next = v->prev = v; +} -- cgit v1.2.3