3873eb06d9
It seems like on amd64, we're overflowing the stack. Let's not do that.
892 lines
17 KiB
C
892 lines
17 KiB
C
#include "xs.h"
|
|
|
|
/*
|
|
* engine for 4s, 5s, etc
|
|
*/
|
|
|
|
Cursor whitearrow = {
|
|
{0, 0},
|
|
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
|
|
0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC,
|
|
0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
|
|
0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, },
|
|
{0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C,
|
|
0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C,
|
|
0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C,
|
|
0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }
|
|
};
|
|
|
|
enum
|
|
{
|
|
CNone = 0,
|
|
CBounds = 1,
|
|
CPiece = 2,
|
|
NX = 10,
|
|
NY = 20,
|
|
};
|
|
|
|
enum{
|
|
TIMER,
|
|
MOUSE,
|
|
RESHAPE,
|
|
KBD,
|
|
SUSPEND,
|
|
NALT
|
|
};
|
|
|
|
char board[NY][NX];
|
|
Rectangle rboard;
|
|
Point pscore;
|
|
Point scoresz;
|
|
int pcsz = 32;
|
|
Point pos;
|
|
Image *bb, *bbmask, *bb2, *bb2mask;
|
|
Image *whitemask;
|
|
Rectangle br, br2;
|
|
long points;
|
|
int dt;
|
|
int DY;
|
|
int DMOUSE;
|
|
int lastmx;
|
|
Mouse mouse;
|
|
int newscreen;
|
|
Channel *timerc;
|
|
Channel *suspc;
|
|
Channel *mousec;
|
|
Channel *kbdc;
|
|
Mousectl *mousectl;
|
|
Keyboardctl *kbdctl;
|
|
int suspended;
|
|
|
|
void redraw(int);
|
|
|
|
int tsleep;
|
|
|
|
Piece *piece;
|
|
|
|
#define NCOL 10
|
|
|
|
uchar txbits[NCOL][32]={
|
|
{0xDD,0xDD,0xFF,0xFF,0x77,0x77,0xFF,0xFF,
|
|
0xDD,0xDD,0xFF,0xFF,0x77,0x77,0xFF,0xFF,
|
|
0xDD,0xDD,0xFF,0xFF,0x77,0x77,0xFF,0xFF,
|
|
0xDD,0xDD,0xFF,0xFF,0x77,0x77,0xFF,0xFF},
|
|
{0xDD,0xDD,0x77,0x77,0xDD,0xDD,0x77,0x77,
|
|
0xDD,0xDD,0x77,0x77,0xDD,0xDD,0x77,0x77,
|
|
0xDD,0xDD,0x77,0x77,0xDD,0xDD,0x77,0x77,
|
|
0xDD,0xDD,0x77,0x77,0xDD,0xDD,0x77,0x77},
|
|
{0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
|
|
0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
|
|
0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
|
|
0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55},
|
|
{0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
|
|
0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
|
|
0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
|
|
0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55},
|
|
{0x22,0x22,0x88,0x88,0x22,0x22,0x88,0x88,
|
|
0x22,0x22,0x88,0x88,0x22,0x22,0x88,0x88,
|
|
0x22,0x22,0x88,0x88,0x22,0x22,0x88,0x88,
|
|
0x22,0x22,0x88,0x88,0x22,0x22,0x88,0x88},
|
|
{0x22,0x22,0x00,0x00,0x88,0x88,0x00,0x00,
|
|
0x22,0x22,0x00,0x00,0x88,0x88,0x00,0x00,
|
|
0x22,0x22,0x00,0x00,0x88,0x88,0x00,0x00,
|
|
0x22,0x22,0x00,0x00,0x88,0x88,0x00,0x00},
|
|
{0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
|
|
0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
|
|
0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
|
|
0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00},
|
|
{0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
|
|
0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
|
|
0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
|
|
0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00},
|
|
{0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,
|
|
0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,
|
|
0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,
|
|
0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC},
|
|
{0xCC,0xCC,0xCC,0xCC,0x33,0x33,0x33,0x33,
|
|
0xCC,0xCC,0xCC,0xCC,0x33,0x33,0x33,0x33,
|
|
0xCC,0xCC,0xCC,0xCC,0x33,0x33,0x33,0x33,
|
|
0xCC,0xCC,0xCC,0xCC,0x33,0x33,0x33,0x33},
|
|
};
|
|
|
|
int txpix[NCOL] = {
|
|
DYellow, /* yellow */
|
|
DCyan, /* cyan */
|
|
DGreen, /* lime green */
|
|
DGreyblue, /* slate */
|
|
DRed, /* red */
|
|
DGreygreen, /* olive green */
|
|
DBlue, /* blue */
|
|
0xFF55AAFF, /* pink */
|
|
0xFFAAFFFF, /* lavender */
|
|
0xBB005DFF, /* maroon */
|
|
};
|
|
|
|
Image *tx[NCOL];
|
|
|
|
int
|
|
movemouse(void)
|
|
{
|
|
mouse.xy = Pt(rboard.min.x + Dx(rboard)/2, rboard.min.y +Dy(rboard)/2);
|
|
moveto(mousectl, mouse.xy);
|
|
return mouse.xy.x;
|
|
}
|
|
|
|
int
|
|
warp(Point p, int x)
|
|
{
|
|
if (!suspended && piece != nil) {
|
|
x = pos.x + piece->sz.x*pcsz/2;
|
|
if (p.y < rboard.min.y)
|
|
p.y = rboard.min.y;
|
|
if (p.y >= rboard.max.y)
|
|
p.y = rboard.max.y - 1;
|
|
moveto(mousectl, Pt(x, p.y));
|
|
}
|
|
return x;
|
|
}
|
|
|
|
Piece *
|
|
rotr(Piece *p)
|
|
{
|
|
if(p->rot == 3)
|
|
return p-3;
|
|
return p+1;
|
|
}
|
|
|
|
Piece *
|
|
rotl(Piece *p)
|
|
{
|
|
if(p->rot == 0)
|
|
return p+3;
|
|
return p-1;
|
|
}
|
|
|
|
int
|
|
collide(Point pt, Piece *p)
|
|
{
|
|
int i;
|
|
int c = CNone;
|
|
|
|
pt.x = (pt.x - rboard.min.x) / pcsz;
|
|
pt.y = (pt.y - rboard.min.y) / pcsz;
|
|
for(i=0; i<N; i++){
|
|
pt.x += p->d[i].x;
|
|
pt.y += p->d[i].y;
|
|
if(pt.x<0 || pt.x>=NX || pt.y<0 || pt.y>=NY)
|
|
c |= CBounds;
|
|
if(board[pt.y][pt.x])
|
|
c |= CPiece;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
int
|
|
collider(Point pt, Point pmax)
|
|
{
|
|
int i, j, pi, pj, n, m;
|
|
|
|
pi = (pt.x - rboard.min.x) / pcsz;
|
|
pj = (pt.y - rboard.min.y) / pcsz;
|
|
n = pmax.x / pcsz;
|
|
m = pmax.y / pcsz + 1;
|
|
for(i = pi; i < pi+n && i < NX; i++)
|
|
for(j = pj; j < pj+m && j < NY; j++)
|
|
if(board[j][i])
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
setpiece(Piece *p){
|
|
int i;
|
|
Rectangle r, r2;
|
|
Point op, delta;
|
|
|
|
draw(bb, bb->r, display->white, nil, ZP);
|
|
draw(bbmask, bbmask->r, display->transparent, nil, ZP);
|
|
br = Rect(0, 0, 0, 0);
|
|
br2 = br;
|
|
piece = p;
|
|
if(p == 0)
|
|
return;
|
|
r.min = bb->r.min;
|
|
for(i=0; i<N; i++){
|
|
r.min.x += p->d[i].x*pcsz;
|
|
r.min.y += p->d[i].y*pcsz;
|
|
r.max.x = r.min.x + pcsz;
|
|
r.max.y = r.min.y + pcsz;
|
|
if(i == 0){
|
|
draw(bb, r, display->black, nil, ZP);
|
|
draw(bb, insetrect(r, 1), tx[piece->tx], nil, ZP);
|
|
draw(bbmask, r, display->opaque, nil, ZP);
|
|
op = r.min;
|
|
}else{
|
|
draw(bb, r, bb, nil, op);
|
|
draw(bbmask, r, bbmask, nil, op);
|
|
}
|
|
if(br.max.x < r.max.x)
|
|
br.max.x = r.max.x;
|
|
if(br.max.y < r.max.y)
|
|
br.max.y = r.max.y;
|
|
}
|
|
br.max = subpt(br.max, bb->r.min);
|
|
delta = Pt(0,DY);
|
|
br2.max = addpt(br.max, delta);
|
|
r = rectaddpt(br, bb2->r.min);
|
|
r2 = rectaddpt(br2, bb2->r.min);
|
|
draw(bb2, r2, display->white, nil, ZP);
|
|
draw(bb2, rectaddpt(r,delta), bb, nil, bb->r.min);
|
|
draw(bb2mask, r2, display->transparent, nil, ZP);
|
|
draw(bb2mask, r, display->opaque, bbmask, bb->r.min);
|
|
draw(bb2mask, rectaddpt(r,delta), display->opaque, bbmask, bb->r.min);
|
|
}
|
|
|
|
void
|
|
drawpiece(void){
|
|
draw(screen, rectaddpt(br, pos), bb, bbmask, bb->r.min);
|
|
if (suspended)
|
|
draw(screen, rectaddpt(br, pos), display->white, whitemask, ZP);
|
|
}
|
|
|
|
void
|
|
undrawpiece(void)
|
|
{
|
|
Image *mask = nil;
|
|
if(collider(pos, br.max))
|
|
mask = bbmask;
|
|
draw(screen, rectaddpt(br, pos), display->white, mask, bb->r.min);
|
|
}
|
|
|
|
void
|
|
rest(void)
|
|
{
|
|
int i;
|
|
Point pt;
|
|
|
|
pt = divpt(subpt(pos, rboard.min), pcsz);
|
|
for(i=0; i<N; i++){
|
|
pt.x += piece->d[i].x;
|
|
pt.y += piece->d[i].y;
|
|
board[pt.y][pt.x] = piece->tx+16;
|
|
}
|
|
}
|
|
|
|
int
|
|
canfit(Piece *p)
|
|
{
|
|
static int dx[]={0, -1, 1, -2, 2, -3, 3, 4, -4};
|
|
int i, j;
|
|
Point z;
|
|
|
|
j = N + 1;
|
|
if(j >= 4){
|
|
j = p->sz.x;
|
|
if(j<p->sz.y)
|
|
j = p->sz.y;
|
|
j = 2*j-1;
|
|
}
|
|
for(i=0; i<j; i++){
|
|
z.x = pos.x + dx[i]*pcsz;
|
|
z.y = pos.y;
|
|
if(!collide(z, p)){
|
|
z.y = pos.y + pcsz-1;
|
|
if(!collide(z, p)){
|
|
undrawpiece();
|
|
pos.x = z.x;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
score(int p)
|
|
{
|
|
char buf[128];
|
|
|
|
points += p;
|
|
snprint(buf, sizeof(buf), "%.6ld", points);
|
|
draw(screen, Rpt(pscore, addpt(pscore, scoresz)), display->white, nil, ZP);
|
|
string(screen, pscore, display->black, ZP, font, buf);
|
|
}
|
|
|
|
void
|
|
drawsq(Image *b, Point p, int ptx){
|
|
Rectangle r;
|
|
|
|
r.min = p;
|
|
r.max.x = r.min.x+pcsz;
|
|
r.max.y = r.min.y+pcsz;
|
|
draw(b, r, display->black, nil, ZP);
|
|
draw(b, insetrect(r, 1), tx[ptx], nil, ZP);
|
|
}
|
|
|
|
void
|
|
drawboard(void)
|
|
{
|
|
int i, j;
|
|
|
|
border(screen, insetrect(rboard, -2), 2, display->black, ZP);
|
|
draw(screen, Rect(rboard.min.x, rboard.min.y-2, rboard.max.x, rboard.min.y),
|
|
display->white, nil, ZP);
|
|
for(i=0; i<NY; i++)
|
|
for(j=0; j<NX; j++)
|
|
if(board[i][j])
|
|
drawsq(screen, Pt(rboard.min.x+j*pcsz, rboard.min.y+i*pcsz), board[i][j]-16);
|
|
score(0);
|
|
if (suspended)
|
|
draw(screen, screen->r, display->white, whitemask, ZP);
|
|
}
|
|
|
|
void
|
|
choosepiece(void)
|
|
{
|
|
int i;
|
|
|
|
do{
|
|
i = nrand(NP);
|
|
setpiece(&pieces[i]);
|
|
pos = rboard.min;
|
|
pos.x += nrand(NX)*pcsz;
|
|
}while(collide(Pt(pos.x, pos.y+pcsz-DY), piece));
|
|
drawpiece();
|
|
flushimage(display, 1);
|
|
}
|
|
|
|
int
|
|
movepiece(void)
|
|
{
|
|
Image *mask = nil;
|
|
|
|
if(collide(Pt(pos.x, pos.y+pcsz), piece))
|
|
return 0;
|
|
if(collider(pos, br2.max))
|
|
mask = bb2mask;
|
|
draw(screen, rectaddpt(br2, pos), bb2, mask, bb2->r.min);
|
|
pos.y += DY;
|
|
flushimage(display, 1);
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
suspend(int s)
|
|
{
|
|
suspended = s;
|
|
if (suspended)
|
|
setcursor(mousectl, &whitearrow);
|
|
else
|
|
setcursor(mousectl, nil);
|
|
if (!suspended)
|
|
drawpiece();
|
|
drawboard();
|
|
flushimage(display, 1);
|
|
}
|
|
|
|
void
|
|
pause(int t)
|
|
{
|
|
int s;
|
|
Alt alts[NALT+1];
|
|
|
|
alts[TIMER].c = timerc;
|
|
alts[TIMER].v = nil;
|
|
alts[TIMER].op = CHANRCV;
|
|
alts[SUSPEND].c = suspc;
|
|
alts[SUSPEND].v = &s;
|
|
alts[SUSPEND].op = CHANRCV;
|
|
alts[RESHAPE].c = mousectl->resizec;
|
|
alts[RESHAPE].v = nil;
|
|
alts[RESHAPE].op = CHANRCV;
|
|
// avoid hanging up those writing ong mousec and kbdc
|
|
// so just accept it all and keep mouse up-to-date
|
|
alts[MOUSE].c = mousec;
|
|
alts[MOUSE].v = &mouse;
|
|
alts[MOUSE].op = CHANRCV;
|
|
alts[KBD].c = kbdc;
|
|
alts[KBD].v = nil;
|
|
alts[KBD].op = CHANRCV;
|
|
alts[NALT].op = CHANEND;
|
|
|
|
flushimage(display, 1);
|
|
for(;;)
|
|
switch(alt(alts)){
|
|
case SUSPEND:
|
|
if (!suspended && s) {
|
|
suspend(1);
|
|
} else if (suspended && !s) {
|
|
suspend(0);
|
|
lastmx = warp(mouse.xy, lastmx);
|
|
}
|
|
break;
|
|
case TIMER:
|
|
if(suspended)
|
|
break;
|
|
if((t -= tsleep) < 0)
|
|
return;
|
|
break;
|
|
case RESHAPE:
|
|
redraw(1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int
|
|
horiz(void)
|
|
{
|
|
int lev[MAXN];
|
|
int i, j, h;
|
|
Rectangle r;
|
|
|
|
h = 0;
|
|
for(i=0; i<NY; i++){
|
|
for(j=0; board[i][j]; j++)
|
|
if(j == NX-1){
|
|
lev[h++] = i;
|
|
break;
|
|
}
|
|
}
|
|
if(h == 0)
|
|
return 0;
|
|
r = rboard;
|
|
newscreen = 0;
|
|
for(j=0; j<h; j++){
|
|
r.min.y = rboard.min.y + lev[j]*pcsz;
|
|
r.max.y = r.min.y + pcsz;
|
|
draw(screen, r, display->white, whitemask, ZP);
|
|
flushimage(display, 1);
|
|
}
|
|
for(i=0; i<3; i++){
|
|
pause(250);
|
|
if(newscreen){
|
|
drawboard();
|
|
break;
|
|
}
|
|
for(j=0; j<h; j++){
|
|
r.min.y = rboard.min.y + lev[j]*pcsz;
|
|
r.max.y = r.min.y + pcsz;
|
|
draw(screen, r, display->white, whitemask, ZP);
|
|
}
|
|
flushimage(display, 1);
|
|
}
|
|
r = rboard;
|
|
for(j=0; j<h; j++){
|
|
i = NY - lev[j] - 1;
|
|
score(250+10*i*i);
|
|
r.min.y = rboard.min.y;
|
|
r.max.y = rboard.min.y+lev[j]*pcsz;
|
|
draw(screen, rectaddpt(r, Pt(0,pcsz)), screen, nil, r.min);
|
|
r.max.y = rboard.min.y+pcsz;
|
|
draw(screen, r, display->white, nil, ZP);
|
|
memcpy(&board[1][0], &board[0][0], NX*lev[j]);
|
|
memset(&board[0][0], 0, NX);
|
|
}
|
|
flushimage(display, 1);
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
mright(void)
|
|
{
|
|
if(!collide(Pt(pos.x+pcsz, pos.y), piece))
|
|
if(!collide(Pt(pos.x+pcsz, pos.y+pcsz-DY), piece)){
|
|
undrawpiece();
|
|
pos.x += pcsz;
|
|
drawpiece();
|
|
flushimage(display, 1);
|
|
}
|
|
}
|
|
|
|
void
|
|
mleft(void)
|
|
{
|
|
if(!collide(Pt(pos.x-pcsz, pos.y), piece))
|
|
if(!collide(Pt(pos.x-pcsz, pos.y+pcsz-DY), piece)){
|
|
undrawpiece();
|
|
pos.x -= pcsz;
|
|
drawpiece();
|
|
flushimage(display, 1);
|
|
}
|
|
}
|
|
|
|
void
|
|
rright(void)
|
|
{
|
|
if(canfit(rotr(piece))){
|
|
setpiece(rotr(piece));
|
|
drawpiece();
|
|
flushimage(display, 1);
|
|
}
|
|
}
|
|
|
|
void
|
|
rleft(void)
|
|
{
|
|
if(canfit(rotl(piece))){
|
|
setpiece(rotl(piece));
|
|
drawpiece();
|
|
flushimage(display, 1);
|
|
}
|
|
}
|
|
|
|
int fusst = 0;
|
|
int
|
|
drop(int f)
|
|
{
|
|
if(f){
|
|
score(5L*(rboard.max.y-pos.y)/pcsz);
|
|
do; while(movepiece());
|
|
}
|
|
fusst = 0;
|
|
rest();
|
|
if(pos.y==rboard.min.y && !horiz())
|
|
return 1;
|
|
horiz();
|
|
setpiece(0);
|
|
pause(1500);
|
|
choosepiece();
|
|
lastmx = warp(mouse.xy, lastmx);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
play(void)
|
|
{
|
|
int i;
|
|
Mouse om;
|
|
int s;
|
|
Rune r;
|
|
Alt alts[NALT+1];
|
|
|
|
alts[TIMER].c = timerc;
|
|
alts[TIMER].v = nil;
|
|
alts[TIMER].op = CHANRCV;
|
|
alts[MOUSE].c = mousec;
|
|
alts[MOUSE].v = &mouse;
|
|
alts[MOUSE].op = CHANRCV;
|
|
alts[SUSPEND].c = suspc;
|
|
alts[SUSPEND].v = &s;
|
|
alts[SUSPEND].op = CHANRCV;
|
|
alts[RESHAPE].c = mousectl->resizec;
|
|
alts[RESHAPE].v = nil;
|
|
alts[RESHAPE].op = CHANRCV;
|
|
alts[KBD].c = kbdc;
|
|
alts[KBD].v = &r;
|
|
alts[KBD].op = CHANRCV;
|
|
alts[NALT].op = CHANEND;
|
|
|
|
dt = 64;
|
|
lastmx = -1;
|
|
lastmx = movemouse();
|
|
choosepiece();
|
|
lastmx = warp(mouse.xy, lastmx);
|
|
for(;;)
|
|
switch(alt(alts)){
|
|
case MOUSE:
|
|
if(suspended) {
|
|
om = mouse;
|
|
break;
|
|
}
|
|
if(lastmx < 0)
|
|
lastmx = mouse.xy.x;
|
|
if(mouse.xy.x > lastmx+DMOUSE){
|
|
mright();
|
|
lastmx = mouse.xy.x;
|
|
}
|
|
if(mouse.xy.x < lastmx-DMOUSE){
|
|
mleft();
|
|
lastmx = mouse.xy.x;
|
|
}
|
|
if(mouse.buttons&1 && !(om.buttons&1))
|
|
rleft();
|
|
if(mouse.buttons&2 && !(om.buttons&2))
|
|
if(drop(1))
|
|
return 1;
|
|
if(mouse.buttons&4 && !(om.buttons&4))
|
|
rright();
|
|
om = mouse;
|
|
break;
|
|
case SUSPEND:
|
|
if (!suspended && s)
|
|
suspend(1);
|
|
else
|
|
if (suspended && !s) {
|
|
suspend(0);
|
|
lastmx = warp(mouse.xy, lastmx);
|
|
}
|
|
break;
|
|
case RESHAPE:
|
|
redraw(1);
|
|
break;
|
|
case KBD:
|
|
if(suspended)
|
|
break;
|
|
switch(r){
|
|
case 'f':
|
|
case ';':
|
|
mright();
|
|
break;
|
|
case 'a':
|
|
case 'j':
|
|
mleft();
|
|
break;
|
|
case 'd':
|
|
case 'l':
|
|
rright();
|
|
break;
|
|
case 's':
|
|
case 'k':
|
|
rleft();
|
|
break;
|
|
case ' ':
|
|
if(drop(1))
|
|
return 1;
|
|
break;
|
|
}
|
|
break;
|
|
case TIMER:
|
|
if(suspended)
|
|
break;
|
|
dt -= tsleep;
|
|
if(dt < 0){
|
|
i = 1;
|
|
dt = 16 * (points+nrand(10000)-5000) / 10000;
|
|
if(dt >= 32){
|
|
i += (dt-32)/16;
|
|
dt = 32;
|
|
}
|
|
dt = 52-dt;
|
|
while(i-- > 0)
|
|
if(movepiece()==0 && ++fusst==40){
|
|
if(drop(0))
|
|
return 1;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
setparms(void)
|
|
{
|
|
char buf[32];
|
|
int fd, n;
|
|
|
|
tsleep = 50;
|
|
fd = open("/dev/hz", OREAD);
|
|
if(fd < 0)
|
|
return;
|
|
n = read(fd, buf, sizeof buf - 1);
|
|
close(fd);
|
|
if(n < 0)
|
|
return;
|
|
buf[n] = '\0';
|
|
tsleep = strtoul(buf, 0, 10);
|
|
tsleep = (1000 + tsleep - 1) / tsleep;
|
|
}
|
|
|
|
void
|
|
timerproc(void *v)
|
|
{
|
|
Channel *c;
|
|
void **arg;
|
|
|
|
arg = v;
|
|
c = (Channel*)arg;
|
|
|
|
for(;;){
|
|
sleep(tsleep);
|
|
send(c, nil);
|
|
}
|
|
}
|
|
|
|
void
|
|
suspproc(void *)
|
|
{
|
|
Mouse mouse;
|
|
Rune r;
|
|
int s;
|
|
Alt alts[NALT+1];
|
|
|
|
alts[TIMER].op = CHANNOP;
|
|
alts[MOUSE].c = mousectl->c;
|
|
alts[MOUSE].v = &mouse;
|
|
alts[MOUSE].op = CHANRCV;
|
|
alts[SUSPEND].op = CHANNOP;
|
|
alts[RESHAPE].op = CHANNOP;
|
|
alts[KBD].c = kbdctl->c;
|
|
alts[KBD].v = &r;
|
|
alts[KBD].op = CHANRCV;
|
|
alts[NALT].op = CHANEND;
|
|
|
|
s = 0;
|
|
for(;;)
|
|
switch(alt(alts)){
|
|
case MOUSE:
|
|
send(mousec, &mouse);
|
|
break;
|
|
case KBD:
|
|
switch(r){
|
|
case 'q':
|
|
case 'Q':
|
|
case 0x04:
|
|
case 0x7F:
|
|
threadexitsall(nil);
|
|
default:
|
|
if(s) {
|
|
s = 0;
|
|
send(suspc, &s);
|
|
} else
|
|
switch(r){
|
|
case 'z':
|
|
case 'Z':
|
|
case 'p':
|
|
case 'P':
|
|
case 0x1B:
|
|
s = 1;
|
|
send(suspc, &s);
|
|
break;
|
|
default:
|
|
send(kbdc, &r);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
redraw(int new)
|
|
{
|
|
Rectangle r;
|
|
long dx, dy;
|
|
|
|
if(new && getwindow(display, Refmesg) < 0)
|
|
sysfatal("can't reattach to window");
|
|
r = screen->r;
|
|
pos.x = (pos.x - rboard.min.x) / pcsz;
|
|
pos.y = (pos.y - rboard.min.y) / pcsz;
|
|
dx = r.max.x - r.min.x;
|
|
dy = r.max.y - r.min.y - 2*32;
|
|
DY = dx / NX;
|
|
if(DY > dy / NY)
|
|
DY = dy / NY;
|
|
DY /= 8;
|
|
if(DY > 4)
|
|
DY = 4;
|
|
pcsz = DY*8;
|
|
DMOUSE = pcsz/3;
|
|
if(pcsz < 8)
|
|
sysfatal("screen too small: %d", pcsz);
|
|
rboard = screen->r;
|
|
rboard.min.x += (dx-pcsz*NX)/2;
|
|
rboard.min.y += (dy-pcsz*NY)/2+32;
|
|
rboard.max.x = rboard.min.x+NX*pcsz;
|
|
rboard.max.y = rboard.min.y+NY*pcsz;
|
|
pscore.x = rboard.min.x+8;
|
|
pscore.y = rboard.min.y-32;
|
|
scoresz = stringsize(font, "000000");
|
|
pos.x = pos.x*pcsz + rboard.min.x;
|
|
pos.y = pos.y*pcsz + rboard.min.y;
|
|
if(bb){
|
|
freeimage(bb);
|
|
freeimage(bbmask);
|
|
freeimage(bb2);
|
|
freeimage(bb2mask);
|
|
}
|
|
bb = allocimage(display, Rect(0,0,N*pcsz,N*pcsz), screen->chan, 0, 0);
|
|
bbmask = allocimage(display, Rect(0,0,N*pcsz,N*pcsz), GREY1, 0, 0);
|
|
bb2 = allocimage(display, Rect(0,0,N*pcsz,N*pcsz+DY), screen->chan, 0, 0);
|
|
bb2mask = allocimage(display, bb2->r, GREY1, 0, 0);
|
|
if(bb==0 || bbmask==0 || bb2==0 || bb2mask==0)
|
|
sysfatal("allocimage fail (bb)");
|
|
draw(screen, screen->r, display->white, nil, ZP);
|
|
drawboard();
|
|
setpiece(piece);
|
|
if(piece)
|
|
drawpiece();
|
|
lastmx = movemouse();
|
|
newscreen = 1;
|
|
flushimage(display, 1);
|
|
}
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprint(2, "usage: %s\n", argv0);
|
|
exits("usage");
|
|
}
|
|
|
|
void
|
|
threadmain(int argc, char *argv[])
|
|
{
|
|
Image *tb;
|
|
char buf[200];
|
|
int i, scores;
|
|
long starttime, endtime;
|
|
|
|
ARGBEGIN{
|
|
default:
|
|
usage();
|
|
}ARGEND
|
|
if(argc)
|
|
usage();
|
|
|
|
suspended = 0;
|
|
setparms();
|
|
snprint(buf, sizeof(buf), "%ds", N);
|
|
initdraw(0, 0, buf);
|
|
mousectl = initmouse(nil, display->image); /* BUG? */
|
|
if(mousectl == nil)
|
|
sysfatal("[45]s: mouse init failed: %r");
|
|
kbdctl = initkeyboard(nil); /* BUG? */
|
|
if(kbdctl == nil)
|
|
sysfatal("[45]s: keyboard init failed: %r");
|
|
starttime = time(0);
|
|
srand(starttime);
|
|
snprint(buf, sizeof(buf), "/sys/games/lib/%dscores", N);
|
|
scores = open(buf, OWRITE);
|
|
if(scores < 0)
|
|
print("can't open %s: %r\n", buf);
|
|
tb = 0;
|
|
if(screen->depth < 3){
|
|
tb = allocimage(display, Rect(0,0,16,16), 0, 1, -1);
|
|
if(tb == 0)
|
|
sysfatal("allocimage fail (tb)");
|
|
}
|
|
for(i = 0; i<NCOL; i++){
|
|
tx[i] = allocimage(display, Rect(0, 0, 16, 16), screen->chan, 1, txpix[i]);
|
|
if(tx[i] == 0)
|
|
sysfatal("allocimage fail (tx)");
|
|
if(screen->depth < 3){
|
|
loadimage(tb, tb->r, txbits[i], 32);
|
|
draw(tx[i], tx[i]->r, tb, nil, ZP);
|
|
}
|
|
}
|
|
if(tb != 0)
|
|
freeimage(tb);
|
|
|
|
whitemask = allocimage(display, Rect(0,0,1,1), CMAP8, 1, setalpha(DWhite, 0x7F));
|
|
if(whitemask==0)
|
|
sysfatal("allocimage fail (whitemask)");
|
|
|
|
threadsetname("4s-5s");
|
|
timerc= chancreate(sizeof(int), 0);
|
|
proccreate(timerproc, timerc, 8192);
|
|
suspc= chancreate(sizeof(int), 0);
|
|
mousec= chancreate(sizeof(Mouse), 0);
|
|
kbdc= chancreate(sizeof(Rune), 0);
|
|
threadcreate(suspproc, nil, 8192);
|
|
points = 0;
|
|
memset(board, 0, sizeof(board));
|
|
redraw(0);
|
|
if(play() && scores >= 0){
|
|
endtime = time(0);
|
|
fprint(scores, "%ld\t%s\t%lud\t%ld\n",
|
|
points, getuser(), starttime, endtime-starttime);
|
|
}
|
|
threadexitsall(nil);
|
|
exits(0);
|
|
}
|