shithub: musw

Download patch

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.

diff: cannot open b/assets/vfx//null: file does not exist: 'b/assets/vfx//null'
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;
+}