shithub: battleship

Download patch

ref: 1540dd58e53cbda25f8cd920e007aba3f9399da7
parent: dbf2640b573990cf3377dc65824fc6870a5ee3ff
author: rodri <[email protected]>
date: Thu Nov 7 16:23:15 EST 2024

bts: use buttons to select the game mode (removes -a option).

now pressing q in a screen other than the main one (Waiting0)
forces a reconnection, effectively resetting the game.

--- a/bts.c
+++ b/bts.c
@@ -136,6 +136,7 @@
 char winspec[32];
 char uid[8+1], oid[8+1];
 Channel *drawchan;
+Channel *reconnc;
 Channel *ingress, *egress;
 Mousectl *mctl; /* only used to update the cursor */
 RFrame worldrf;
@@ -163,6 +164,27 @@
 } conclusion;
 
 
+static void
+PvP_handler(Button *)
+{
+	if(game.state != Waiting0)
+		return;
+	chanprint(egress, "play %d\n", GMPvP);
+}
+
+static void
+PvAI_handler(Button *)
+{
+	if(game.state != Waiting0)
+		return;
+	chanprint(egress, "play %d\n", GMPvAI);
+}
+
+Button mainbtns[] = {
+	{ .label = "PvP", .handler = PvP_handler },
+	{ .label = "PvAI", .handler = PvAI_handler },
+};
+
 Point
 fromworld(Point2 p)
 {
@@ -339,9 +361,13 @@
 void
 drawgameoptions(Image *dst)
 {
-	static char s[] = "press p to play, w to watch";
+	Button *b;
 
-	string(dst, Pt(SCRW/2 - stringwidth(font, s)/2, 10*font->height+5), pal[PCWhite], ZP, font, s);
+	for(b = mainbtns; b < mainbtns+nelem(mainbtns); b++){
+		draw(dst, b->r, pal[b->status? PCBlack: PCWhite], nil, ZP);
+		border(dst, b->r, Btnborder, pal[PCBrown], ZP);
+		string(dst, addpt(b->r.min, Pt(Dx(b->r)/2 - stringwidth(font, b->label)/2, Btnpadding + Btnborder)), pal[b->status? PCWhite: PCBlack], ZP, font, b->label);
+	}
 }
 
 void
@@ -453,7 +479,6 @@
 		sysfatal("resize failed");
 	unlockdisplay(display);
 
-	/* ignore move events */
 	if(Dx(screen->r) != SCRW || Dy(screen->r) != SCRH){
 		fd = open("/dev/wctl", OWRITE);
 		if(fd >= 0){
@@ -521,6 +546,25 @@
 }
 
 void
+initmainbtns(void)
+{
+	Button *b;
+	Point btnsize;
+
+	btnsize.x = Btnborder + Btnpadding + 100 + Btnpadding + Btnborder;
+	btnsize.y = Btnborder + Btnpadding + font->height + Btnpadding + Btnborder;
+
+	for(b = mainbtns; b < mainbtns+nelem(mainbtns); b++){
+		b->status = BRest;
+		if(b == mainbtns)
+			b->r.min = Pt(SCRW/2 - Btnpadding - 100/2, 8*font->height);
+		else
+			b->r.min = addpt(b[-1].r.min, Pt(0, btnsize.y+4));
+		b->r.max = addpt(b->r.min, btnsize);
+	}
+}
+
+void
 initboards(void)
 {
 	memset(alienboard.map, Twater, MAPW*MAPH);
@@ -588,7 +632,6 @@
 int
 confirmdone(Mousectl *mc)
 {
-	/* thanks sigrid! */
 	Cursor anchor = {
 		{0, 0},
 		{ 0x00, 0x00, 0x00, 0x1e, 0x01, 0x92, 0x30, 0xd2,
@@ -624,6 +667,7 @@
 lmb(Mousectl *mc)
 {
 	Point2 cell;
+	Button *b;
 	char buf[3+1];
 
 	if(conclusion.s != nil)
@@ -630,6 +674,14 @@
 		return;
 
 	switch(game.state){
+	case Waiting0:
+		for(b = mainbtns; b < mainbtns+nelem(mainbtns); b++){
+			if(ptinrect(mc->xy, b->r)){
+				b->handler(b);
+				break;
+			}
+		}
+		break;
 	case Outlaying:
 		if(!ptinrect(mc->xy, localboard.bbox))
 			break;
@@ -747,17 +799,23 @@
 void
 mouse(Mousectl *mc)
 {
-	Rectangle newbbox;
 	static Mouse oldm;
+	Rectangle newbbox;
+	Button *b;
 	int selmatch;
 
 	mc->xy = subpt(mc->xy, screen->r.min);
 
-	if(game.state == Waiting0)
+	if(game.state == Waiting0){
+		for(b = mainbtns; b < mainbtns+nelem(mainbtns); b++)
+			b->status = ptinrect(mc->xy, b->r)? BHover: BRest;
+		nbsend(drawchan, nil);
+
 		if((selmatch = matches->update(matches, mc, drawchan)) >= 0){
 			if(debug) fprint(2, "selected match id %d title %s\n", matches->entries[selmatch].id, matches->entries[selmatch].title);
 			chanprint(egress, "watch %d\n", matches->entries[selmatch].id);
 		}
+	}
 
 	if(game.state == Outlaying && curship != nil){
 		newbbox = mkshipbbox(toboard(&localboard, mc->xy), curship->orient, curship->ncells);
@@ -807,12 +865,11 @@
 
 	switch(r){
 	case Kdel:
-	case 'q':
 		threadexitsall(nil);
-	case 'p':
-		if(game.state != Waiting0)
-			break;
-		chanprint(egress, "play %d\n", game.mode);
+	case 'q':
+		if(game.state == Waiting0)
+			threadexitsall(nil);
+		nbsend(reconnc, nil);
 		break;
 	case 'w':
 		if(game.state != Waiting0)
@@ -1039,7 +1096,7 @@
 			tot = 0;
 	}
 	closeioproc(io);
-	threadexitsall("connection lost");
+	threadexits(nil);
 }
 
 void
@@ -1057,13 +1114,13 @@
 			fprint(2, "sent '%s'\n", s);
 		free(s);
 	}
-	threadexitsall("connection lost");
+	threadexits(nil);
 }
 
 void
 usage(void)
 {
-	fprint(2, "usage: %s [-dsa] addr\n", argv0);
+	fprint(2, "usage: %s [-ds] addr\n", argv0);
 	threadexitsall("usage");
 }
 
@@ -1073,7 +1130,7 @@
 	char aux[64];
 	char *addr;
 	char *user;
-	int fd;
+	int fd, cfd;
 	Mousectl *mc;
 	Keyboardctl *kc;
 	Rune r;
@@ -1086,9 +1143,6 @@
 	case 's':
 		silent++;
 		break;
-	case 'a':
-		game.mode = GMPvAI;
-		break;
 	default: usage();
 	}ARGEND
 	if(argc != 1)
@@ -1125,6 +1179,7 @@
 
 	initpalette();
 	inittiles();
+	initmainbtns();
 	initboards();
 	initarmada();
 	matches = newmenulist(14*font->height, "ongoing matches");
@@ -1139,7 +1194,7 @@
 	if(debug)
 		fprint(2, "connecting to %s\n", addr);
 
-	fd = dial(addr, nil, nil, nil);
+	fd = dial(addr, nil, nil, &cfd);
 	if(fd < 0)
 		sysfatal("dial: %r");
 	else if(debug)
@@ -1146,6 +1201,7 @@
 		fprint(2, "line established\n");
 
 	drawchan = chancreate(sizeof(void*), 1);
+	reconnc = chancreate(sizeof(void*), 1);
 	ingress = chancreate(sizeof(char*), 1);
 	egress = chancreate(sizeof(char*), 1);
 	threadcreate(netrecvthread, &fd, mainstacksize);
@@ -1152,12 +1208,13 @@
 	threadcreate(netsendthread, &fd, mainstacksize);
 	nbsend(drawchan, nil);
 
-	enum { MOUSE, RESIZE, KEYS, DRAW, NONE };
+	enum { MOUSE, RESIZE, KEYS, DRAW, RECONN, NONE };
 	Alt a[] = {
 	 [MOUSE]	{mc->c, &mc->Mouse, CHANRCV},
 	 [RESIZE]	{mc->resizec, nil, CHANRCV},
 	 [KEYS]		{kc->c, &r, CHANRCV},
 	 [DRAW]		{drawchan, nil, CHANRCV},
+	 [RECONN]	{reconnc, nil, CHANRCV},
 	 [NONE]		{nil, nil, CHANEND}
 	};
 	for(;;)
@@ -1173,6 +1230,26 @@
 			break;
 		case DRAW:
 			redraw();
+			break;
+		case RECONN:
+			if(debug)
+				fprint(2, "reconnecting to %s\n", addr);
+
+			write(cfd, "close", 5);
+			close(cfd);
+			close(fd);
+
+			fd = dial(addr, nil, nil, &cfd);
+			if(fd < 0)
+				sysfatal("dial: %r");
+			else if(debug)
+				fprint(2, "line established\n");
+
+			threadcreate(netrecvthread, &fd, mainstacksize);
+			threadcreate(netsendthread, &fd, mainstacksize);
+
+			resetgame();
+			nbsend(drawchan, nil);
 			break;
 		default:
 			sysfatal("input thread interrupted");
--- a/dat.h
+++ b/dat.h
@@ -32,7 +32,12 @@
 	ACalibrating,
 	ABombing,
 
+	BRest = 0,
+	BHover,
+
 	Boardmargin = 50,
+	Btnborder = 4,
+	Btnpadding = 2,
 	TW = 16,
 	TH = TW,
 	MAPW = 17,
@@ -161,6 +166,16 @@
 	} pl[2];
 	Board *bl[2];
 	char conclusion[16];
+};
+
+typedef struct Button Button;
+
+struct Button
+{
+	char *label;
+	Rectangle r;
+	int status;
+	void (*handler)(Button*);
 };
 
 typedef struct Mentry Mentry;