shithub: battleship

Download patch

ref: 2293ed78636e5f66b5e2884bebd193803cb6939f
parent: 497acdd8860d629ba24ed9976fd47eeb743b74ab
author: rodri <[email protected]>
date: Fri Sep 22 11:32:36 EDT 2023

add parsecmd to the client. finish the menulist of matches.

the menulist now supports scrolling.
also changed some cursors and got to tidy up /^lmb\(.

--- a/bts.c
+++ b/bts.c
@@ -9,6 +9,41 @@
 #include "dat.h"
 #include "fns.h"
 
+enum {
+	CMid,
+	CMqueued,
+	CMlayout,
+	CMoid,
+	CMwait,
+	CMplay,
+	CMwehit,
+	CMwemiss,
+	CMtheyhit,
+	CMtheymiss,
+	CMmatchesb,	/* list header */
+	CMmatch,	/* list entry */
+	CMmatchese,	/* list tail */
+	CMwin,
+	CMlose,
+};
+Cmdtab svcmd[] = {
+	CMid,		"id",		1,
+	CMqueued,	"queued",	1,
+	CMlayout, 	"layout",	1,
+	CMoid, 		"oid",		2,
+	CMwait, 	"wait",		1,
+	CMplay,		"play",		1,
+	CMwehit,	"hit",		1,
+	CMwemiss,	"miss",		1,
+	CMtheyhit, 	"hit",		2,
+	CMtheymiss,	"miss",		2,
+	CMmatchesb,	"matches",	1,
+	CMmatch,	"m",		4,
+	CMmatchese,	"end",		1,
+	CMwin,		"win",		1,
+	CMlose,		"lose",		1,
+};
+
 int debug;
 
 Cursor patrolcursor = {
@@ -174,7 +209,7 @@
 	oid[0] = 0;
 	game.state = Waiting0;
 	conclusion.s = nil;
-	csetcursor(mctl, &patrolcursor);
+	csetcursor(mctl, nil);
 }
 
 Point
@@ -498,36 +533,33 @@
 void
 lmb(Mousectl *mc)
 {
-	Board *b;
 	Point2 cell;
 
-	b = nil;
-	if(ptinrect(mc->xy, alienboard.bbox))
-		b = &alienboard;
-	else if(ptinrect(mc->xy, localboard.bbox))
-		b = &localboard;
-
-	if(b == nil || conclusion.s != nil)
+	if(conclusion.s != nil)
 		return;
 
-	cell = toboard(b, mc->xy);
 	switch(game.state){
 	case Outlaying:
-		if(b == &localboard)
-			if(curship != nil && rectinrect(curship->bbox, localboard.bbox))
-				if(++curship-armada >= nelem(armada))
-					curship = nil;
-				else if(curship != &armada[0])
-					curship->orient = (curship-1)->orient;
+		if(!ptinrect(mc->xy, localboard.bbox))
+			break;
+
+		if(curship != nil && rectinrect(curship->bbox, localboard.bbox)){
+			if(++curship-armada >= nelem(armada))
+				curship = nil;
+			else if(curship != &armada[0])
+				curship->orient = (curship-1)->orient;
+			nbsend(drawchan, nil);
+		}
 		break;
 	case Playing:
-		if(b == &alienboard){
-			chanprint(egress, "shoot %s\n", cell2coords(cell));
-			lastshot = cell;
-		}
+		if(!ptinrect(mc->xy, alienboard.bbox))
+			break;
+
+		cell = toboard(&alienboard, mc->xy);
+		chanprint(egress, "shoot %s\n", cell2coords(cell));
+		lastshot = cell;
 		break;
 	}
-	nbsend(drawchan, nil);
 }
 
 void
@@ -619,9 +651,14 @@
 {
 	Rectangle newbbox;
 	static Mouse oldm;
+	int selmatch;
 
 	mc->xy = subpt(mc->xy, screen->r.min);
 
+	if(game.state == Waiting0)
+		if((selmatch = matches->update(matches, mc, drawchan)) >= 0)
+			if(debug) fprint(2, "selected match id %d title %s\n", selmatch, matches->entries[selmatch].title);
+
 	if(game.state == Outlaying && curship != nil){
 		newbbox = mkshipbbox(toboard(&localboard, mc->xy), curship->orient, curship->ncells);
 
@@ -712,42 +749,47 @@
 void
 processcmd(char *cmd)
 {
+	Cmdbuf *cb;
+	Cmdtab *ct;
 	Point2 cell;
-	char *f[3];
-	int i, nf;
+	int i;
 
 	if(debug)
 		fprint(2, "rcvd '%s'\n", cmd);
 
-	nf = tokenize(cmd, f, nelem(f));
-	if(nf < 1)
+	cb = parsecmd(cmd, strlen(cmd));
+	ct = lookupcmd(cb, svcmd, nelem(svcmd));
+	if(ct == nil){
+		free(cb);
 		return;
+	}
 
-	if(nf == 1 && strcmp(f[0], "win") == 0)
+	if(ct->index == CMwin)
 		celebrate();
-	else if(nf == 1 && strcmp(f[0], "lose") == 0)
+	else if(ct->index == CMlose)
 		keelhaul();
 
 	switch(game.state){
 	case Waiting0:
-		if(nf == 1 && strcmp(f[0], "id") == 0)
+		if(ct->index == CMid)
 			chanprint(egress, "id %s\n", uid);
-		else if(nf == 1 && strcmp(f[0], "queued") == 0)
+		else if(ct->index == CMqueued){
 			game.state = Ready;
-		else if(!matches->filling && nf == 1 && strcmp(f[0], "matches") == 0){
+			csetcursor(mctl, &patrolcursor);
+		}else if(!matches->filling && ct->index == CMmatchesb){
 			matches->clear(matches);
 			matches->filling = 1;
-		}else if(matches->filling && nf == 3)
-			matches->add(matches, strtoul(f[0], nil, 10), smprint("%s vs %s", f[1], f[2]));
-		else if(matches->filling && nf == 1 && strcmp(f[0], "end") == 0)
+		}else if(matches->filling && ct->index == CMmatch)
+			matches->add(matches, strtoul(cb->f[1], nil, 10), smprint("%s vs %s", cb->f[2], cb->f[3]));
+		else if(matches->filling && ct->index == CMmatchese)
 			matches->filling = 0;
 		break;
 	case Ready:
-		if(nf == 1 && strcmp(f[0], "layout") == 0){
+		if(ct->index == CMlayout){
 			game.state = Outlaying;
 			curship = &armada[0];
-		}else if(nf == 2 && strcmp(f[0], "oid") == 0)
-			snprint(oid, sizeof oid, "%s", f[1]);
+		}else if(ct->index == CMoid)
+			snprint(oid, sizeof oid, "%s", cb->f[1]);
 		break;
 	case Watching:
 		/* <idx?> <uid> (hit|missed) <coord> */
@@ -756,27 +798,27 @@
 		 */
 		break;
 	case Outlaying:
-		if(nf == 1 && strcmp(f[0], "wait") == 0){
+		if(ct->index == CMwait){
 			game.state = Waiting;
 			csetcursor(mctl, &waitcursor);
-		}else if(nf == 1 && strcmp(f[0], "play") == 0)
+		}else if(ct->index == CMplay)
 			game.state = Playing;
 		break;
 	case Playing:
-		if(nf == 1 && strcmp(f[0], "wait") == 0){
+		if(ct->index == CMwait){
 			game.state = Waiting;
 			csetcursor(mctl, &waitcursor);
-		}else if(nf == 1 && strcmp(f[0], "hit") == 0)
+		}else if(ct->index == CMwehit)
 			settile(&alienboard, lastshot, Thit);
-		else if(nf == 1 && strcmp(f[0], "miss") == 0)
+		else if(ct->index == CMwemiss)
 			settile(&alienboard, lastshot, Tmiss);
 		break;
 	case Waiting:
-		if(nf == 1 && strcmp(f[0], "play") == 0){
+		if(ct->index == CMplay){
 			game.state = Playing;
 			csetcursor(mctl, nil);
-		}else if(nf == 2 && strcmp(f[0], "hit") == 0){
-			cell = coords2cell(f[1]);
+		}else if(ct->index == CMtheyhit){
+			cell = coords2cell(cb->f[1]);
 			for(i = 0; i < nelem(armada); i++)
 				if(ptinrect(fromboard(&localboard, cell), armada[i].bbox)){
 					cell = subpt2(cell, armada[i].p);
@@ -783,12 +825,13 @@
 					armada[i].hit[(int)vec2len(cell)] = 1;
 					break;
 				}
-		}else if(nf == 2 && strcmp(f[0], "miss") == 0){
-			cell = coords2cell(f[1]);
+		}else if(ct->index == CMtheymiss){
+			cell = coords2cell(cb->f[1]);
 			settile(&localboard, cell, Tmiss);
 		}
 		break;
 	}
+	free(cb);
 	nbsend(drawchan, nil);
 }
 
@@ -906,7 +949,6 @@
 	initarmada();
 	matches = newmenulist(14*font->height, "ongoing matches");
 	game.state = Waiting0;
-	csetcursor(mctl, &patrolcursor);
 
 	drawchan = chancreate(sizeof(void*), 1);
 	ingress = chancreate(sizeof(char*), 1);
--- a/menulist.c
+++ b/menulist.c
@@ -13,7 +13,7 @@
 	Menuborder = 2,
 	Vspace = 2,
 	Scrollwidth = 10,
-	Maxvisitems = 5,
+	Maxvisitems = 8,
 };
 static char none[] = "none";
 
@@ -31,7 +31,11 @@
 		ml->r.min.x = SCRW/2 - ew/2;
 		ml->r.max.x = ml->r.min.x + ew;
 	}
-	if(ml->nentries > 1)
+
+	if(ml->nentries > Maxvisitems){
+		ml->sr.min = subpt(ml->r.min, Pt(Scrollwidth+2*Menuborder,0));
+		ml->sr.max = Pt(ml->r.min.x-2*Menuborder, ml->r.max.y);
+	}else if(ml->nentries > 1)
 		ml->r.max.y += font->height+Vspace;
 }
 
@@ -55,16 +59,45 @@
 	ml->r.max = addpt(ml->r.min, Pt(w, font->height+Vspace));
 	ml->sr = ZR;
 	ml->high = -1;
+	ml->off = 0;
 }
 
-static void
-menulist_update(Menulist *ml, Mousectl *mc)
+static int
+menulist_update(Menulist *ml, Mousectl *mc, Channel *drawchan)
 {
-	if(ptinrect(mc->xy, ml->r)){
-		/* item highlighting and selection */
-	}else if(ptinrect(mc->xy, ml->sr)){
-		/* scrolling */
+	/* redundant from bts.c:/^mouse\(, but it's necessary to avoid overdrawing */
+	static Mouse oldm;
+	static ulong lastlmbms;
+	int selected;
+
+	selected = -1;
+	if(ptinrect(mc->xy, Rpt(ml->sr.min, ml->r.max))){
+		if(ptinrect(mc->xy, ml->r)){
+			/* item highlighting and selection */
+			ml->high = ml->off + (mc->xy.y - ml->r.min.y)/(font->height+Vspace);
+			if(oldm.buttons != mc->buttons && mc->buttons == 1){
+				if(mc->msec-lastlmbms < 500)
+					selected = ml->high;
+				else
+					lastlmbms = mc->msec;
+			}
+		}
+		if(mc->buttons != oldm.buttons && ml->nentries > Maxvisitems)
+			/* scrolling */
+			switch(mc->buttons){
+			case 1: if(!ptinrect(mc->xy, ml->sr)) break;
+			case 8:
+				ml->off = max(0, ml->off - (mc->xy.y - ml->sr.min.y)/(font->height+Vspace));
+				break;
+			case 4: if(!ptinrect(mc->xy, ml->sr)) break;
+			case 16:
+				ml->off = min(ml->off + (mc->xy.y - ml->sr.min.y)/(font->height+Vspace), ml->nentries-Maxvisitems);
+				break;
+			}
 	}
+	nbsendp(drawchan, nil);
+	oldm = mc->Mouse;
+	return selected;
 }
 
 static void
@@ -72,7 +105,7 @@
 {
 	static Image *bc;
 	Rectangle tr, er; /* title and per-entry */
-	int i;
+	int i, width;
 
 	if(ml->filling)
 		return;
@@ -81,18 +114,20 @@
 		bc = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
 
 	/* draw title */
+	width = stringwidth(font, ml->title);
 	tr.min = subpt(ml->r.min, Pt(0,Menuborder + font->height+Vspace));
 	tr.max = Pt(ml->r.max.x, ml->r.min.y - Menuborder);
 	draw(dst, tr, display->black, nil, ZP);
-	string(dst, tr.min, display->white, ZP, font, ml->title);
+	string(dst, addpt(tr.min, Pt(Dx(tr)/2 - width/2,0)), display->white, ZP, font, ml->title);
 
 	/* draw content */
 	border(dst, ml->r, -Menuborder, bc, ZP);
 	er.min = ml->r.min;
 	er.max = Pt(ml->r.max.x, er.min.y + font->height+Vspace);
-	for(i = 0; i < ml->nentries; i++){
-		draw(dst, er, display->white, nil, ZP);
-		string(dst, er.min, display->black, ZP, font, ml->entries[i].title);
+	for(i = ml->off; i < ml->nentries && er.min.y < ml->r.max.y; i++){
+		width = stringwidth(font, ml->entries[i].title);
+		draw(dst, er, i == ml->high? display->black: display->white, nil, ZP);
+		string(dst, addpt(er.min, Pt(Dx(er)/2 - width/2,0)), i == ml->high? display->white: display->black, ZP, font, ml->entries[i].title);
 		er.min.y += font->height+Vspace;
 		er.max.y = er.min.y + font->height+Vspace;
 	}
@@ -99,6 +134,13 @@
 	if(i == 0){
 		draw(dst, er, display->white, nil, ZP);
 		string(dst, er.min, display->black, ZP, font, none);
+	}
+
+	/* draw scroll */
+	if(ml->nentries > Maxvisitems){
+		border(dst, ml->sr, -Menuborder, bc, ZP);
+		draw(dst, ml->sr, display->black, nil, ZP);
+		draw(dst, Rpt(addpt(ml->sr.min, Pt(0,ml->off*Dy(ml->sr)/ml->nentries)), Pt(ml->sr.max.x,ml->sr.min.y + (ml->off+Maxvisitems)*Dy(ml->sr)/ml->nentries)), display->white, nil, ZP);
 	}
 }