ref: c2782417e222be0e61fd910bae5c3d127bdda90b
dir: /bts.c/
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <geometry.h>
#include "dat.h"
#include "fns.h"
int debug;
char deffont[] = "/lib/font/bit/pelm/unicode.9.font";
char winspec[32];
Channel *drawchan;
Channel *ingress, *egress;
RFrame worldrf;
Image *screenb;
Image *tiletab[NTILES];
Board alienboard;
Board localboard;
Ship *carrier;
struct {
int state;
} game;
Point
fromworld(Point2 p)
{
p = invrframexform(p, worldrf);
return Pt(p.x,p.y);
}
Point2
toworld(Point p)
{
return rframexform(Pt2(p.x,p.y,1), worldrf);
}
Point
fromboard(Board *b, Point2 p)
{
p = invrframexform(invrframexform(p, *b), worldrf);
return Pt(p.x,p.y);
}
Point2
toboard(Board *b, Point p)
{
Point2 np;
np = rframexform(rframexform(Pt2(p.x,p.y,1), worldrf), *b);
np.x = (int)np.x;
np.y = (int)np.y;
return np;
}
Image *
gettileimage(int type)
{
if(type < 0 || type > nelem(tiletab))
return nil;
return tiletab[type];
}
void
settile(Board *b, Point2 cell, int type)
{
Point p;
p.x = cell.x;
p.y = cell.y;
b->map[p.x][p.y] = type;
}
void
drawtile(Image *dst, Board *b, Point2 cell, int type)
{
Point p;
Image *ti;
p = fromboard(b, cell);
ti = gettileimage(type);
if(ti == nil)
return;
draw(dst, Rpt(p, addpt(p, Pt(TW,TH))), ti, nil, ZP);
}
void
drawship(Image *dst, Ship *s)
{
Point2 p, sv;
int i;
p = s->p;
switch(s->orient){
case OH: sv = Vec2(1,0); break;
case OV: sv = Vec2(0,1); break;
default: return;
}
for(i = 0; i < s->ncells; i++){
drawtile(dst, &localboard, p, s->hit[i]? Thit: Tship);
p = addpt2(p, sv);
}
}
void
drawboard(Image *dst, Board *b)
{
int i, j;
for(i = 0; i < MAPW; i++)
for(j = 0; j < MAPH; j++)
drawtile(dst, b, Pt2(i,j,1), b->map[i][j]);
}
void
redraw(void)
{
lockdisplay(display);
draw(screenb, screenb->r, display->black, nil, ZP);
drawboard(screenb, &alienboard);
drawboard(screenb, &localboard);
drawship(screenb, carrier);
draw(screen, screen->r, screenb, nil, ZP);
flushimage(display, 1);
unlockdisplay(display);
}
void
resize(void)
{
lockdisplay(display);
if(getwindow(display, Refnone) < 0)
sysfatal("resize failed");
unlockdisplay(display);
freeimage(screenb);
screenb = eallocimage(display, rectsubpt(screen->r, screen->r.min), screen->chan, 0, DNofill);
send(drawchan, nil);
}
void
inittiles(void)
{
Image *brush;
int i, x, y;
Point pts[2];
for(i = 0; i < nelem(tiletab); i++){
tiletab[i] = eallocimage(display, Rect(0,0,TW,TH), screen->chan, 0, DNofill);
switch(i){
case Twater:
brush = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreyblue);
draw(tiletab[i], tiletab[i]->r, brush, nil, ZP);
freeimage(brush);
brush = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalebluegreen);
for(pts[0] = ZP, x = 0; x < TW; x++){
y = sin(x)*TH/2;
pts[1] = Pt(x,y+TH/2);
line(tiletab[i], pts[0], pts[1], Endsquare, Endsquare, 0, brush, ZP);
pts[0] = pts[1];
}
freeimage(brush);
break;
case Tship:
brush = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x333333FF);
draw(tiletab[i], tiletab[i]->r, brush, nil, ZP);
freeimage(brush);
brush = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, DBlack);
fillellipse(tiletab[i], Pt(TW/2,TH/2), 2, 2, brush, ZP);
freeimage(brush);
break;
case Thit:
brush = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, DRed);
draw(tiletab[i], tiletab[i]->r, brush, nil, ZP);
freeimage(brush);
brush = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, DBlack);
pts[0] = Pt(TW/2-TW/4,TH/2-TH/4);
pts[1] = Pt(TW/2+TW/4,TH/2+TH/4);
line(tiletab[i], pts[0], pts[1], Endsquare, Endsquare, 1, brush, ZP);
pts[0].y += TH/2;
pts[1].y -= TH/2;
line(tiletab[i], pts[0], pts[1], Endsquare, Endsquare, 1, brush, ZP);
freeimage(brush);
break;
case Tmiss:
draw(tiletab[i], tiletab[i]->r, tiletab[Twater], nil, ZP);
brush = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, DWhite);
ellipse(tiletab[i], Pt(TW/2,TH/2), 6, 6, 1, brush, ZP);
freeimage(brush);
break;
}
}
}
void
initboards(void)
{
memset(alienboard.map, Twater, MAPW*MAPH);
alienboard.p = Pt2(Boardmargin,Boardmargin,1);
memset(localboard.map, Twater, MAPW*MAPH);
localboard.p = addpt2(alienboard.p, Vec2(0,MAPH*TH+TH));
alienboard.bx = Vec2(TW,0);
localboard.bx = Vec2(TW,0);
alienboard.by = Vec2(0,TH);
localboard.by = Vec2(0,TH);
alienboard.bbox = Rpt(fromworld(alienboard.p), fromworld(addpt2(alienboard.p, Pt2(TW*MAPW,TH*MAPH,1))));
localboard.bbox = Rpt(fromworld(localboard.p), fromworld(addpt2(localboard.p, Pt2(TW*MAPW,TH*MAPH,1))));
}
void
initships(void)
{
Point2 sv;
carrier = emalloc(sizeof *carrier);
carrier->p = Pt2(2,4,1);
carrier->orient = OV;
carrier->ncells = 5;
carrier->hit = emalloc(carrier->ncells*sizeof(int));
memset(carrier->hit, 0, carrier->ncells*sizeof(int));
carrier->sunk = 0;
switch(carrier->orient){
case OH: sv = Vec2(1,0); break;
case OV: sv = Vec2(0,1); break;
default: sysfatal("initships: wrong ship orientation");
}
carrier->bbox = Rpt(
fromboard(&localboard, carrier->p),
fromboard(&localboard, addpt2(addpt2(carrier->p, mulpt2(sv, carrier->ncells)), Vec2(sv.y,sv.x)))
);
}
void
placeship(Mousectl *mc, Ship *s)
{
for(;;){
if(readmouse(mc) < 0)
break;
mc->xy = subpt(mc->xy, screen->r.min);
if(ptinrect(mc->xy, localboard.bbox) && mc->buttons == 1){
s->p = toboard(&localboard, mc->xy);
send(drawchan, nil);
break;
}
}
}
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)
return;
cell = toboard(b, mc->xy);
switch(game.state){
case Outlaying:
settile(b, cell, Tship);
break;
case Playing:
settile(b, cell, Tmiss);
chanprint(egress, "shoot %d-%d", (int)cell.x, (int)cell.y);
break;
}
send(drawchan, nil);
}
void
rmb(Mousectl *mc)
{
enum {
PLACESHIP,
};
static char *items[] = {
[PLACESHIP] "place ship",
nil
};
static Menu menu = { .item = items };
switch(menuhit(3, mc, &menu, _screen)){
case PLACESHIP:
placeship(mc, carrier);
break;
}
}
void
mouse(Mousectl *mc)
{
mc->xy = subpt(mc->xy, screen->r.min);
switch(mc->buttons){
case 1:
lmb(mc);
break;
case 2:
// mmb(mc);
break;
case 4:
rmb(mc);
break;
}
}
void
key(Rune r)
{
switch(r){
case Kdel:
case 'q':
threadexitsall(nil);
}
}
void
showproc(void *)
{
threadsetname("showproc");
while(recv(drawchan, nil) > 0)
redraw();
sysfatal("showproc died");
}
void
inputthread(void *arg)
{
Input *in;
Rune r;
Alt a[4];
in = arg;
a[0].c = in->mc->c; a[0].v = &in->mc->Mouse; a[0].op = CHANRCV;
a[1].c = in->mc->resizec; a[1].v = nil; a[1].op = CHANRCV;
a[2].c = in->kc->c; a[2].v = &r; a[2].op = CHANRCV;
a[3].op = CHANEND;
for(;;)
switch(alt(a)){
case -1:
sysfatal("input thread interrupted");
case 0:
mouse(in->mc);
break;
case 1:
resize();
break;
case 2:
key(r);
break;
}
}
void
netrecvthread(void *arg)
{
Ioproc *io;
char buf[256];
int n, fd;
threadsetname("netrecvthread");
fd = *(int*)arg;
io = ioproc();
while((n = ioread(io, fd, buf, sizeof(buf)-1)) > 0){
buf[n] = 0;
if(debug)
fprint(2, "rcvd '%s'\n", buf);
if(strcmp(buf, "wait") == 0)
game.state = Waiting1;
else if(strcmp(buf, "play") == 0)
game.state = Playing;
// chanprint(ingress, "%s", buf);
}
closeioproc(io);
threadexitsall("connection lost");
}
void
netsendthread(void *arg)
{
char *s;
int fd;
threadsetname("netsendthread");
fd = *(int*)arg;
while(recv(egress, &s) > 0){
if(write(fd, s, strlen(s)) != strlen(s))
break;
if(debug)
fprint(2, "sent '%s'\n", s);
}
threadexitsall("connection lost");
}
void
usage(void)
{
fprint(2, "usage: %s [-d] addr\n", argv0);
threadexitsall("usage");
}
void
threadmain(int argc, char *argv[])
{
char *addr;
int fd;
Input in;
GEOMfmtinstall();
ARGBEGIN{
case 'd':
debug++;
break;
default: usage();
}ARGEND
if(argc != 1)
usage();
addr = netmkaddr(argv[0], "tcp", "3047");
if(debug)
fprint(2, "connecting to %s\n", addr);
fd = dial(addr, nil, nil, nil);
if(fd < 0)
sysfatal("dial: %r");
else if(debug)
fprint(2, "line established\n");
if(write(fd, "join", 4) != 4)
sysfatal("whoops: %r");
snprint(winspec, sizeof winspec, "-dx %d -dy %d", SCRW, SCRH);
if(newwindow(winspec) < 0)
sysfatal("newwindow: %r");
if(initdraw(nil, deffont, "bts") < 0)
sysfatal("initdraw: %r");
if((in.mc = initmouse(nil, screen)) == nil)
sysfatal("initmouse: %r");
if((in.kc = initkeyboard(nil)) == nil)
sysfatal("initkeyboard: %r");
display->locking = 1;
unlockdisplay(display);
screenb = eallocimage(display, rectsubpt(screen->r, screen->r.min), screen->chan, 0, DNofill);
worldrf.p = Pt2(0,0,1);
worldrf.bx = Vec2(1,0);
worldrf.by = Vec2(0,1);
inittiles();
initboards();
initships();
game.state = Waiting0;
drawchan = chancreate(sizeof(void*), 0);
ingress = chancreate(sizeof(char*), 0);
egress = chancreate(sizeof(char*), 0);
proccreate(showproc, nil, mainstacksize);
threadcreate(inputthread, &in, mainstacksize);
threadcreate(netrecvthread, &fd, mainstacksize);
threadcreate(netsendthread, &fd, mainstacksize);
send(drawchan, nil);
yield();
}