ref: e095f65376894dcdef067b63103f2e17e55a1f14
parent: 1baca76aaf266a2d071af4a173aee5b85efeffd6
author: rodri <rgl@antares-labs.eu>
date: Mon May 22 05:00:42 EDT 2023
improvements to show a bullet's explosion upon ttl consumption. implemented a vfx subsystem to handle localized, async animations.
binary files /dev/null b/assets/vfx/bullet.explosion.png differ
--- a/dat.h
+++ b/dat.h
@@ -59,6 +59,7 @@
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,8 +102,20 @@
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
{
Rune key;
@@ -133,7 +146,6 @@
struct Ship
{
Particle;
- Kind kind;
int fuel;
Bullet rounds[10];
VModel *mdl;
--- a/fns.h
+++ b/fns.h
@@ -50,6 +50,14 @@
void delsprite(Sprite*);
/*
+ * vfx
+ */
+Vfx *newvfx(Sprite*, Point, int);
+void delvfx(Vfx*);
+void addvfx(Vfx*, Vfx*);
+void initvfx(Vfx*);
+
+/*
* net
*/
void dhgenpg(ulong*, ulong*);
--- a/mkfile
+++ b/mkfile
@@ -14,6 +14,7 @@
party.$O\
universe.$O\
sprite.$O\
+ vfx.$O\
net.$O\
fmt.$O\
--- a/musw.c
+++ b/musw.c
@@ -20,6 +20,11 @@
NGAMESTATES
};
+enum {
+ VFX_BULLET_EXPLOSION,
+ NVFX
+};
+
Keymap kmap[] = {
{.key = Kup, .op = K↑},
{.key = Kleft, .op = K↺},
@@ -35,11 +40,13 @@
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 @@
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 @@
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 @@
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 @@
&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 @@
void
redraw(void)
{
+ Vfx *vfx;
+
lockdisplay(display);
if(doghosting)
@@ -581,6 +608,9 @@
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 @@
double frametime;
char *server;
int fd;
+ Vfx *vfx;
Mousectl *mc;
Ioproc *io;
@@ -704,6 +735,7 @@
sysfatal("dial: %r");
universe = newuniverse();
+ olduniverse = newuniverse();
needlemdl = readvmodel("assets/mdl/needle.vmdl");
if(needlemdl == nil)
sysfatal("readvmodel: %r");
@@ -710,12 +742,15 @@
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 @@
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)
--- a/muswd.c
+++ b/muswd.c
@@ -418,8 +418,8 @@
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);
}
--- a/sprite.c
+++ b/sprite.c
@@ -35,6 +35,12 @@
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 @@
spr->elapsed = 0;
spr->step = sprite_step;
spr->draw = sprite_draw;
+ spr->clone = sprite_clone;
return spr;
}
@@ -111,6 +118,6 @@
void
delsprite(Sprite *spr)
{
- freeimage(spr->sheet);
+ //freeimage(spr->sheet);
free(spr);
}
--- a/todo
+++ b/todo
@@ -1,6 +1,6 @@
[ ] 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
@@ -7,11 +7,14 @@
[ ] 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
--- a/universe.c
+++ b/universe.c
@@ -103,12 +103,15 @@
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 @@
}
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;
--- /dev/null
+++ b/vfx.c
@@ -1,0 +1,69 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include <mp.h>
+#include <libsec.h>
+#include <thread.h>
+#include <draw.h>
+#include <geometry.h>
+#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;
+}