shithub: battleship

Download patch

ref: 423e5319e3d3501637fffb11bb5c0acd48c2f424
parent: 2d2fda85ac4907d53a26b9afebdfadf9364a6436
author: rodri <rgl@antares-labs.eu>
date: Fri Nov 15 11:38:47 EST 2024

bts: add vfx. use stringn instead of string.

diff: cannot open b/assets/vfx//null: file does not exist: 'b/assets/vfx//null'
binary files /dev/null b/assets/vfx/hit.png differ
binary files /dev/null b/assets/vfx/miss.png differ
--- a/bts.c
+++ b/bts.c
@@ -135,6 +135,8 @@
 Font *titlefont;
 char winspec[32];
 char uid[8+1], oid[8+1];
+Sprite *spritetab[NVFX];
+Vfx vfxqueue;
 Channel *drawchan;
 Channel *reconnc;
 Channel *ingress, *egress;
@@ -277,12 +279,8 @@
 Point
 vstring(Image *dst, Point p, Image *src, Point sp, Font *f, char *s)
 {
-	char buf[2];
-
-	buf[1] = 0;
 	while(*s){
-		buf[0] = *s++;
-		string(dst, p, src, sp, f, buf);
+		stringn(dst, p, src, sp, f, s++, 1);
 		p.y += font->height;
 	}
 	return p;
@@ -408,6 +406,9 @@
 			snprint(aux, sizeof aux, "%s (%d)", shipname(curship-armada), curship->ncells);
 			p = Pt(SCRW/2 - stringwidth(font, aux)/2, SCRH-Boardmargin);
 			string(dst, p, pal[PCYellow], ZP, font, aux);
+			s = "MMB to rotate the ship";
+			p = Pt(SCRW/2 - stringwidth(font, s)/2, SCRH-Boardmargin+font->height);
+			string(dst, p, pal[PCYellow], ZP, font, s);
 		}else{
 			s = "done with the layout?";
 			p = Pt(SCRW/2 - stringwidth(font, s)/2, SCRH-Boardmargin);
@@ -452,6 +453,8 @@
 void
 redraw(void)
 {
+	Vfx *vfx;
+
 	lockdisplay(display);
 
 	draw(screenb, screenb->r, pal[PCBlack], nil, ZP);
@@ -468,6 +471,8 @@
 		drawinfo(screenb);
 		break;
 	}
+	for(vfx = vfxqueue.next; vfx != &vfxqueue; vfx = vfx->next)
+		vfx->draw(vfx, screenb);
 	drawconclusion(screenb);
 
 	draw(screen, screen->r, screenb, nil, ZP);
@@ -603,6 +608,18 @@
 }
 
 void
+initvfx(void)
+{
+	char aux[64];
+
+	snprint(aux, sizeof aux, "%s/%s", assetdir, "vfx/hit.png");
+	spritetab[VFXHit] = readpngsprite(aux, ZP, Rect(0, 0, 32, 32), 12, 100);
+	snprint(aux, sizeof aux, "%s/%s", assetdir, "vfx/miss.png");
+	spritetab[VFXMiss] = readpngsprite(aux, ZP, Rect(0, 0, 32, 32), 7, 150);
+	initvfxq(&vfxqueue);
+}
+
+void
 initsfx(void)
 {
 	struct {
@@ -763,11 +780,11 @@
 		if(!rectinrect(curship->bbox, localboard.bbox)){
 			switch(curship->orient){
 			case OH:
-				curship->bbox.min.x -= curship->bbox.max.x-localboard.bbox.max.x;
+				curship->bbox.min.x -= curship->bbox.max.x - localboard.bbox.max.x;
 				curship->bbox.max.x = localboard.bbox.max.x;
 				break;
 			case OV:
-				curship->bbox.min.y -= curship->bbox.max.y-localboard.bbox.max.y;
+				curship->bbox.min.y -= curship->bbox.max.y - localboard.bbox.max.y;
 				curship->bbox.max.y = localboard.bbox.max.y;
 				break;
 			}
@@ -878,7 +895,7 @@
 void
 keelhaul(void)
 {
-	static char s[] = "…YOU LOST";
+	static char s[] = "...YOU LOST";
 
 	conclusion.c = pal[PCRed];
 	conclusion.s = s;
@@ -1009,11 +1026,17 @@
 			idx = strtoul(cb->f[1], nil, 10);
 			cell = coords2cell(cb->f[2]);
 			settile(match.bl[idx^1], cell, Thit);
+			addvfx(&vfxqueue,
+				newvfx(spritetab[VFXHit]->clone(spritetab[VFXHit]),
+					addpt(fromboard(match.bl[idx^1], cell), Pt(TW/2, TH/2)), 1));
 			break;
 		case CMplayermiss:
 			idx = strtoul(cb->f[1], nil, 10);
 			cell = coords2cell(cb->f[2]);
 			settile(match.bl[idx^1], cell, Tmiss);
+			addvfx(&vfxqueue,
+				newvfx(spritetab[VFXMiss]->clone(spritetab[VFXMiss]),
+					addpt(fromboard(match.bl[idx^1], cell), Pt(TW/2, TH/2)), 1));
 			break;
 		case CMplayerplays:
 			idx = strtoul(cb->f[1], nil, 10);
@@ -1045,6 +1068,9 @@
 			break;
 		case CMwehit:
 			settile(&alienboard, lastshot, Thit);
+			addvfx(&vfxqueue,
+				newvfx(spritetab[VFXHit]->clone(spritetab[VFXHit]),
+					addpt(fromboard(&alienboard, lastshot), Pt(TW/2, TH/2)), 1));
 			break;
 		case CMwemiss:
 			if(!silent)
@@ -1051,6 +1077,9 @@
 				playaudio(playlist[SWATER]);
 
 			settile(&alienboard, lastshot, Tmiss);
+			addvfx(&vfxqueue,
+				newvfx(spritetab[VFXMiss]->clone(spritetab[VFXMiss]),
+					addpt(fromboard(&alienboard, lastshot), Pt(TW/2, TH/2)), 1));
 			break;
 		}
 		break;
@@ -1064,6 +1093,9 @@
 			cell = coords2cell(cb->f[1]);
 			for(i = 0; i < nelem(armada); i++)
 				if(ptinrect(fromboard(&localboard, cell), armada[i].bbox)){
+					addvfx(&vfxqueue,
+						newvfx(spritetab[VFXHit]->clone(spritetab[VFXHit]),
+							addpt(fromboard(&localboard, cell), Pt(TW/2, TH/2)), 1));
 					cell = subpt2(cell, armada[i].p);
 					armada[i].hit[(int)vec2len(cell)] = 1;
 					break;
@@ -1072,6 +1104,9 @@
 		case CMtheymiss:
 			cell = coords2cell(cb->f[1]);
 			settile(&localboard, cell, Tmiss);
+			addvfx(&vfxqueue,
+				newvfx(spritetab[VFXMiss]->clone(spritetab[VFXMiss]),
+					addpt(fromboard(&localboard, cell), Pt(TW/2, TH/2)), 1));
 			break;
 		}
 		break;
@@ -1101,7 +1136,9 @@
 void
 timerproc(void *)
 {
-	uvlong t0, Δt, acc;
+	Vfx *vfx;
+	uvlong t0, Δt, φt, acc;
+	int refresh;
 
 	threadsetname("timer");
 
@@ -1109,8 +1146,17 @@
 	acc = 0;
 	for(;;){
 		Δt = nsec() - t0;
+		φt = Δt/1000000ULL;
 		acc += Δt;
 
+		refresh = 0;
+		for(vfx = vfxqueue.next; vfx != &vfxqueue; vfx = vfx->next){
+			vfx->step(vfx, φt);
+			refresh = 1;
+		}
+		if(refresh)
+			nbsend(drawchan, nil);
+
 		if(gamestate == Waiting0 && acc >= 5*SEC){
 			chanprint(egress, "watch\n");
 			acc = 0;
@@ -1117,7 +1163,7 @@
 		}
 
 		t0 += Δt;
-		sleep(HZ2MS(10));
+		sleep(HZ2MS(20));
 	}
 }
 
@@ -1232,6 +1278,7 @@
 	initmainbtns();
 	initboards();
 	initarmada();
+	initvfx();
 	matches = newmenulist(14*font->height, "ongoing matches");
 	gamestate = Waiting0;
 
--- a/dat.h
+++ b/dat.h
@@ -7,7 +7,7 @@
 	Tmiss,
 	NTILES,
 
-	TBITS = 2, /* ceil(log(NTILES)/log(2)) */
+	TBITS = 2,	/* ceil(log(NTILES)/log(2)) */
 	TMASK = (1<<TBITS) - 1,
 
 	Scarrier = 0,
@@ -17,9 +17,13 @@
 	Sdestroyer,
 	NSHIPS,
 
-	OH, /* horizontal */
-	OV, /* vertical */
+	VFXHit = 0,
+	VFXMiss,
+	NVFX,
 
+	OH = 0,		/* horizontal */
+	OV,		/* vertical */
+
 	GMPvP = 0,
 	GMPvAI,
 
@@ -178,6 +182,35 @@
 	Rectangle r;
 	int status;
 	void (*handler)(Button*);
+};
+
+typedef struct Sprite Sprite;
+typedef struct Vfx Vfx;
+
+struct Sprite
+{
+	Image *sheet;
+	Point sp;
+	Rectangle r;
+	int nframes;
+	int curframe;
+	ulong period;
+	ulong elapsed;
+
+	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*);
 };
 
 typedef struct Mentry Mentry;
--- a/fns.h
+++ b/fns.h
@@ -36,6 +36,22 @@
 void delmenulist(Menulist*);
 
 /*
+ * sprite
+ */
+Sprite *newsprite(Image*, Point, Rectangle, int, ulong);
+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 initvfxq(Vfx*);
+
+/*
  * parse
  */
 Cmdbuf *parsecmd(char*, int);
--- a/mkfile
+++ b/mkfile
@@ -13,6 +13,8 @@
 	andy.$O\
 	menulist.$O\
 	mixer.$O\
+	sprite.$O\
+	vfx.$O\
 
 HFILES=\
 	dat.h\