#include "pz.h" /* Ali Ozer ** Main user interface/action loop for IFF2PCS ** AND a whole lot more... ** This file shouldn't be this long, but unfortunately, it is... ** It is also a bit messy (too many extern references, ack!) ** Nov 1987 */ /* We assume several things: ** puzph = puzpw ** picx = integer multiple of (puzpw) ** picy = integer multiple of (puzph) */ extern int puzpn, puzdepth; /* These are the parameters for a piece */ extern int picx, picy; /* Where the picture is */ extern int picw, pich; /* The size of the picture */ extern int textcolor; extern int bordercolor; extern int normcolor; extern int nonzerocolor; struct BitMap bgbm, rotbm; /* Background bitmap, rotation bitmap */ extern struct PopUp_Menu pzmenu; unsigned long starttime; /* In seconds */ int minmousex, maxmousex, minmousey, maxmousey; int puzph, puzpw; int picxbase, picybase; /* Simply picx-puzpw/2 and picy-puzph/2 */ extern struct BitMap picbm; struct BitMap *wbm; struct RastPort *wrp; struct Window *picwin; struct Screen *picscr; int winoffset; /* Offset of the puzzle area within the window */ int numpieces, numx, numy; int toppiece, bottompiece; /* Number of pieces will be (picw / puzpw) * (pich / puzph) */ struct piecestr { int xloc, yloc; /* Pixel location on screen */ int next, prev; /* Next piece, in top to bottom sorted order */ int rotation; /* 0 normal, 1 ninety cw, 2 upsidedown, 3 90ccw */ int ingrid; /* True if piece is in the puzzle somewhere... */ } *pieces; /* Bottompiece -> Prev -> Prev -> ... -> -1 */ ubyte *gridinfo; /* Whether a grid piece is occupied or not... */ DrawGrid () { long x = picx-1; long y = picy-1; int cnt; SetAPen (wrp, (long)bordercolor); for (cnt = 0; cnt <= numy; cnt++) { Move (wrp, x, y); Draw (wrp, x + picw + 1, y); Move (wrp, x, y+1); Draw (wrp, x + picw + 1, y+1); y += puzph; }; y = picy-1; for (cnt = 0; cnt <= numx; cnt++) { Move (wrp, x, y); Draw (wrp, x, y + pich + 1); Move (wrp, x+1, y); Draw (wrp, x+1, y + pich + 1); x += puzpw; }; } static ubyte tmp[8]; /* Temp for rotates... */ Rotate (plane, nbytes) /* n is the side of the square in bytes... */ ubyte *plane; int nbytes; { int cnt, side, n, nbytec = nbytes << 3; ubyte *s1, *s2, *s3, *s4; for (n = nbytes; n > 1; n -= 2) { s1 = plane; s2 = plane + n - 1; s4 = s1 + nbytec * (n - 1); s3 = s4 + n - 1; for (side = 0; side < n-1; side++) { for (cnt = 0; cnt < 8; cnt++) tmp[cnt] = s1[cnt * nbytes]; flip (s4, nbytes, s1, nbytes); flip (s3, nbytes, s4, nbytes); flip (s2, nbytes, s3, nbytes); flip (&tmp[0], 1, s2, nbytes); s1++; s3--; s2 += nbytec; s4 -= nbytec; }; plane += (nbytec + 1); } } /* Border modes */ #define NOBORDER 0 #define BORDER 1 #define HLBORDER 2 /* Highlighted border */ #define ERASE 3 #define JUSTBORDER 4 /* Come in with x = -1 to use the pieces default position... */ DrawPiece (pnum, x, y, border) int border, x, y, pnum; { long cnt; int rot; int bmx = (pnum % numx) * puzpw; int bmy = (pnum / numx) * puzph; if (x == -1) { x = pieces[pnum].xloc; y = pieces[pnum].yloc; }; if (border == ERASE || border == JUSTBORDER) { SetAPen (wrp, 0L); RectFill (wrp, (long)x, (long)y, (long)(x+puzpw-1), (long)(y+puzph-1)); } else { if (pieces[pnum].rotation) { SavePuzBM (&picbm, bmx, bmy, &rotbm); for (rot = 0; rot < pieces[pnum].rotation; rot++) for (cnt = 0; cnt < rotbm.Depth; cnt++) Rotate (rotbm.Planes[cnt], puzph >> 3); CopyFromBMToBM (&rotbm, 0, 0, wbm, x, y, puzpw, puzph); } else CopyFromBMToBM (&picbm, bmx, bmy, wbm, x, y, puzpw, puzph); }; if (border != ERASE) { if (border != NOBORDER) { if (border == HLBORDER) SetAPen (wrp, (long)textcolor); else SetAPen (wrp, (long)bordercolor); Move (wrp, (long)x, (long)y); Draw (wrp, (long)x+puzpw-1, (long)y); Draw (wrp, (long)x+puzpw-1, (long)y+puzph-1); Draw (wrp, (long)x, (long)y+puzph-1); Draw (wrp, (long)x, (long)y); } } } int RndInt(); int Min (); int Max (); unsigned long TimeInSecs (); int PuzzleSolved () { int width = (picw >> 3); int planecnt, bytecnt, rowcnt; unsigned char *curplane; for (planecnt = 0; planecnt < wbm->Depth; planecnt++) { curplane = (unsigned char *)(wbm->Planes[planecnt]) + winoffset; for (rowcnt = 0; rowcnt < pich; rowcnt++) { for (bytecnt = 0; bytecnt < width; bytecnt++) { if (*(curplane+bytecnt)) return (false); } curplane += wbm->BytesPerRow; } } return (true); } CheckPuzzle () { int solved; unsigned long timedif = TimeInSecs() - starttime; static char *solvestr = "Time: 0:00:00 *** Puzzle is SOLVED ***"; XORFromBMToBM (&picbm, 0, 0, wbm, picx, picy, picw, pich); if (timedif >= 360000) solvestr[5] = '0' + (timedif / 360000) % 10; if (timedif >= 36000) solvestr[6] = '0' + (timedif / 36000) % 10; solvestr[7] = '0' + (timedif / 3600) % 10; solvestr[9] = '0' + (timedif / 600) % 6; solvestr[10] = '0' + (timedif / 60) % 10; solvestr[12] = '0' + (timedif / 10) % 6; solvestr[13] = '0' + (timedif % 10); SetWindowTitles (picwin, NULL, "Checking..."); if (solved = PuzzleSolved()) { solvestr[14] = ' '; XORFromBMToBM (&picbm, 0, 0, wbm, picx, picy, picw, pich); } else solvestr[14] = '\0'; SetWindowTitles (picwin, NULL, solvestr); Wait (1L << picwin->UserPort->mp_SigBit); if (solved == false) XORFromBMToBM (&picbm, 0, 0, wbm, picx, picy, picw, pich); SetWindowTitles (picwin, NULL, PROGNAME); } void MakePuzzle () { int cnt, miny, maxy, minx, maxx; /* Puzzle piece size. Puzpn is the power of two size of each piece. ** For now, pieces have to be square, so we set puzph = puzpw. */ /* Minimum and maximum locations for puzzle pieces */ miny = picy + pich + 4; maxy = picwin->Height - puzph - 4; minx = 2; maxx = picwin->Width - puzpw - 2; minmousex = 0; maxmousex = picscr->Width - puzpw - 1; minmousey = picscr->BarHeight + 1; maxmousey = picscr->Height - puzph - 1; if (((pieces = (struct piecestr *) AllocMem ((long)(numpieces * sizeof(struct piecestr)), 0L)) == NULL) || ((gridinfo = (ubyte *) AllocMem ((long)(((numx * numy) >> 3) + 1), MEMF_CLEAR)) == NULL)) { FreeBMs (); Panic ("No memory for bitmaps"); }; DrawGrid (); for (cnt = 0; cnt < numpieces; cnt++) { /* piecexnum = cnt % numx AND pieceynum = cnt / numx */ pieces[cnt].ingrid = false; pieces[cnt].rotation = RndInt (0, 3); pieces[cnt].next = cnt + 1; pieces[cnt].prev = cnt - 1; pieces[cnt].xloc = RndInt (minx, maxx); pieces[cnt].yloc = RndInt (miny, maxy); }; pieces[numpieces-1].next = -1; bottompiece = numpieces - 1; toppiece = 0; /* Shuffle some so that we don't always get the same order... */ for (cnt = 0; cnt < (numpieces>>3); cnt++) PutPieceAtTop (RndInt(0, numpieces-1)); DrawAllPieces (); } PutPieceAtTop (piece) int piece; { if (pieces[piece].next != -1) pieces[pieces[piece].next].prev = pieces[piece].prev; else bottompiece = pieces[piece].prev; if (pieces[piece].prev != -1) pieces[pieces[piece].prev].next = pieces[piece].next; else toppiece = pieces[piece].next; pieces[piece].next = toppiece; pieces[toppiece].prev = piece; toppiece = piece; pieces[piece].prev = -1; } SeeIfInGrid (piece, newx, newy) int piece, newx, newy; { if ((newx <= picx + picw) && (newx >= picx - puzpw) && (newy <= picy + pich) && (newy >= picy - puzph)) { if (newx < picxbase) newx = picxbase; else if (newx >= picxbase + picw) newx = picxbase + picw - 1; if (newy < picybase) newy = picybase; else if (newy >= picybase + pich) newy = picybase + pich - 1; newx = ((newx - picxbase) / puzpw) * puzpw + picx; newy = ((newy - picybase) / puzph) * puzph + picy; if (GridBoxOccupied (newx, newy, -1) == false) { GridBoxOccupied (newx, newy, true); pieces[piece].ingrid = true; pieces[piece].xloc = newx; pieces[piece].yloc = newy; } else SeeIfInGrid (piece, pieces[piece].xloc, pieces[piece].yloc); } else { pieces[piece].ingrid = false; pieces[piece].xloc = newx; pieces[piece].yloc = newy; } } /* Draws pieces that overlap the specified area in any way. Then grows the ** specified area by the bounding box of the new piece. A rather inefficient ** way of fixing up the picture after a change... The argument notpiece ** specifies a piece (if any) that does not need to be redrawn. Set to -1 if ** no such piece. */ /* ReDrawThePiecesAt (x1, x2, y1, y2, notpiece) int x1, x2, y1, y2, notpiece; { int cnt = bottompiece; cnt = bottompiece; while (cnt != -1) { if ((pieces[cnt].xloc <= x2) && (pieces[cnt].xloc+puzpw-1 >= x1) && (pieces[cnt].yloc <= y2) && (pieces[cnt].yloc+puzph-1 >= y1) && (cnt != notpiece)) { DrawPiece (cnt, -1, -1, BORDER); x1 = Min (x1, pieces[cnt].xloc); x2 = Max (x2, pieces[cnt].xloc+puzpw-1); y1 = Min (y1, pieces[cnt].yloc); y2 = Max (y2, pieces[cnt].yloc+puzph-1); }; cnt = pieces[cnt].prev; } } */ ReDrawThePiecesAt (x1, x2, y1, y2, notpiece) int x1, x2, y1, y2, notpiece; { int cnt = bottompiece, i, tmpprev; for (i = 0; i < numpieces; i++) { tmpprev = pieces[cnt].prev; if ((pieces[cnt].xloc <= x2) && (pieces[cnt].xloc+puzpw-1 >= x1) && (pieces[cnt].yloc <= y2) && (pieces[cnt].yloc+puzph-1 >= y1) && (cnt != notpiece)) { DrawPiece (cnt, -1, -1, BORDER); PutPieceAtTop (cnt); }; cnt = tmpprev; } } ErasePiece (piece) int piece; { if (pieces[piece].ingrid == true) { DrawPiece (piece, -1, -1, JUSTBORDER); GridBoxOccupied (pieces[piece].xloc, pieces[piece].yloc, false); pieces[piece].ingrid = false; } else { DrawPiece (piece, -1, -1, ERASE); ReDrawThePiecesAt (pieces[piece].xloc, pieces[piece].xloc + puzph - 1, pieces[piece].yloc, pieces[piece].yloc + puzpw - 1, piece); } } DrawAllPieces () { int cnt = bottompiece; while (cnt != -1) { DrawPiece (cnt, -1, -1, BORDER); cnt = pieces[cnt].prev; }; } FreeBMs () { FreeBM (&bgbm); FreeBM (&rotbm); if (gridinfo != NULL) FreeMem (gridinfo, (long)(((numx * numy) >> 3) + 1)); if (pieces != NULL) FreeMem (pieces, (long)(numpieces * sizeof(struct piecestr))); } /* Come in with x, y to test and possibly set the mode of the grid at location ** x, y. If mode is true or false, then the new mode is set. If mode is -1, then ** the old mode is returned... */ int GridBoxOccupied (x, y, mode) int x, y, mode; { int index; ubyte bit; x = (x - picx) / puzpw; y = (y - picy) / puzph; index = (y * numx + x) >> 3; bit = (1 << ((y * numx + x) & 7)); if (mode == true) gridinfo[index] |= bit; else if (mode == false) gridinfo[index] &= ~bit; else if ((gridinfo[index] & bit) != 0) return (true); else return (false); } /* SavePuzBM will save the indicated section of bm in tmpbm; RestorePuzBM ** will bring that section back into the indicated section in bm. */ SavePuzBM (bm, x, y, tmpbm) struct BitMap *bm, *tmpbm; int x, y; { CopyFromBMToBM (bm, x, y, tmpbm, 0, 0, puzpw, puzph); } RestorePuzBM (bm, x, y, tmpbm) struct BitMap *bm, *tmpbm; int x, y; { CopyFromBMToBM (tmpbm, 0, 0, bm, x, y, puzpw, puzph); } int PuzzlePieceAt (x, y) int x, y; { int i = toppiece; while (i != -1) { if ((pieces[i].xloc-1 <= x) && (pieces[i].xloc+puzpw >= x) && (pieces[i].yloc-1 <= y) && (pieces[i].yloc+puzph >= y)) return(i); i = pieces[i].next; } return(-1); /* Not on any piece... */ } RotatePiece (piece) int piece; { if (++(pieces[piece].rotation) > 3) pieces[piece].rotation = 0; } GetAndHandleEvents (win) struct Window *win; { struct IntuiMessage *msg; int mousemoved, selected; ULONG class; USHORT code; int x, y, lastx, lasty, deltax, deltay, restorex, restorey, selectedpiece; picwin = win; picscr = win->WScreen; wbm = &(picscr->BitMap); wrp = win->RPort; CopyFromBMToBM (&picbm, 0, 0, wbm, picx, picy, picw, pich); SetDrMd (wrp, JAM1); if ((puzpn = GetDifficulty(win)) == 0) Panic (NULL); SetAPen (wrp, 0L); RectFill (wrp, 0L, (long)picy, (long)(win->Width-1), (long)(win->Height-1)); puzph = puzpw = (1 << puzpn); picxbase = picx - (puzpw >> 1); picybase = picy - (puzph >> 1); pich = (pich >> puzpn) << puzpn; picw = (picw >> puzpn) << puzpn; numx = (picw / puzpw); numy = (pich / puzph); numpieces = numx * numy; winoffset = picy * wbm->BytesPerRow + (picx >> 3); /* Offset in plane, in bytes */ if (InitBM (&bgbm, puzph, puzpw, puzdepth) == false || InitBM (&rotbm, puzph, puzpw, puzdepth) == false) { FreeBMs (); Panic ("Out of memory!"); }; MakePuzzle (); selectedpiece = -1; starttime = TimeInSecs (); while (1) { mousemoved = false; while (msg = (struct IntuiMessage *) GetMsg (win->UserPort)) { class = msg->Class; code = msg->Code; x = msg->MouseX; y = msg->MouseY; ReplyMsg (msg); switch (class) { case MENUPICK: switch (ITEMNUM(code)) { case 0: /* Show */ break; case 1: /* New */ break; case 2: /* Quit */ FreeBMs (); return; /* For now, MENUUP signifies EXIT */ break; }; break; case MOUSEMOVE: mousemoved = true; break; case MOUSEBUTTONS: switch (code) { case SELECTDOWN: lastx = x; lasty = y; if ((selectedpiece = PuzzlePieceAt (x, y)) != -1) { ReportMouse (TRUE, win); /* Start telling us about mouse loc */ lastx = pieces[selectedpiece].xloc; deltax = lastx - x; lasty = pieces[selectedpiece].yloc; deltay = lasty - y; ErasePiece (selectedpiece); SavePuzBM (wbm, lastx, lasty, &bgbm); /* Save background */ DrawPiece (selectedpiece, -1, -1, HLBORDER); SavePuzBM (wbm, lastx, lasty, &rotbm); /* Piece picked up */ mousemoved = true; }; break; case SELECTUP: if (selectedpiece != -1) { ReportMouse (FALSE, win); RestorePuzBM (wbm, lastx, lasty, &bgbm); PutPieceAtTop (selectedpiece); SeeIfInGrid (selectedpiece, x+deltax, y+deltay); DrawPiece (selectedpiece, -1, -1, (pieces[selectedpiece].ingrid == true ? NOBORDER : BORDER)); selectedpiece = -1; mousemoved = false; }; break; case MENUDOWN: if (selectedpiece != -1) { RotatePiece (selectedpiece); DrawPiece (selectedpiece, lastx, lasty, HLBORDER); SavePuzBM (wbm, lastx, lasty, &rotbm); /* Piece picked up */ } else switch (PopUp (&pzmenu, win)) { case CHECKCMD: CheckPuzzle (); break; case SHOWCMD: XORFromBMToBM (&picbm, 0, 0, wbm, picx, picy, picw, pich); XORFromBMToBM (wbm, picx, picy, &picbm, 0, 0, picw, pich); XORFromBMToBM (&picbm, 0, 0, wbm, picx, picy, picw, pich); Wait (1L << win->UserPort->mp_SigBit); XORFromBMToBM (&picbm, 0, 0, wbm, picx, picy, picw, pich); XORFromBMToBM (wbm, picx, picy, &picbm, 0, 0, picw, pich); XORFromBMToBM (&picbm, 0, 0, wbm, picx, picy, picw, pich); break; case NEWCMD: /* SetWindowTitles (picwin, NULL, "Option not available yet"); ** Wait (1L << picwin->UserPort->mp_SigBit); ** SetWindowTitles (picwin, NULL, PROGNAME); */ break; case HELPCMD: if (DoAboutBox (bordercolor, textcolor) == false) { SetWindowTitles (picwin, NULL, COPYRIGHT); Wait (1L << picwin->UserPort->mp_SigBit); SetWindowTitles (picwin, NULL, PROGNAME); }; break; case QUITCMD: FreeBMs (); return; break; default: break; }; break; case MENUUP: break; default: break; }; break; default: break; } } if ((mousemoved == true) && (selectedpiece != -1)) { restorex = lastx; restorey = lasty; lastx = x + deltax; lasty = y + deltay; if (lastx < minmousex) lastx = minmousex; else if (lastx > maxmousex) lastx = maxmousex; if (lasty < minmousey) lasty = minmousey; else if (lasty > maxmousey) lasty = maxmousey; deltax = lastx - x; deltay = lasty - y; WaitBOVP (&(picscr->ViewPort)); RestorePuzBM (wbm, restorex, restorey, &bgbm); SavePuzBM (wbm, lastx, lasty, &bgbm); CopyFromBMToBM (&rotbm, 0, 0, wbm, lastx, lasty, puzpw, puzph); Delay (2L); }; Wait (1L << win->UserPort->mp_SigBit); } } /* Gadgets to get the difficulty from the user... */ #define GADWIDTH 120 #define GADHEIGHT 12 #define GADX 400 #define GADY 300 #define NUMGADS 4 static struct Gadget pzgad[NUMGADS]; static struct Gadget samplegad = { NULL, GADX, 0, GADWIDTH, GADHEIGHT, GADGHBOX, RELVERIFY, BOOLGADGET, NULL, NULL, NULL, 0L, NULL, 0, NULL}; static char *gadtext[NUMGADS] = { "Real easy", "Not so easy", "Difficult", "Quit"}; int GetDifficulty () { int cnt; struct IntuiMessage *msg; SetAPen (wrp, (long)nonzerocolor); Move (wrp, (long)GADX-8, (long)GADY-8); Text (wrp, "Select difficulty level:", 24L); for (cnt = 0; cnt < NUMGADS; cnt++) { pzgad[cnt] = samplegad; /* Structure copy */ pzgad[cnt].TopEdge = GADY+cnt*(GADHEIGHT+2)+(cnt == NUMGADS-1 ? 8 : 0); if (cnt != NUMGADS-1) { pzgad[cnt].GadgetID = DIFF_EASY - cnt; pzgad[cnt].NextGadget = &pzgad[cnt+1]; }; Move (wrp, (long)pzgad[cnt].LeftEdge+10, (long)pzgad[cnt].TopEdge+8); Text (wrp, gadtext[cnt], (long)strlen(gadtext[cnt])); } AddGList (picwin, pzgad, -1L, -1L, NULL); /*RefreshGList (picwin->FirstGadget, picwin, NULL, -1L);*/ cnt = -1; while (true) { while (msg = (struct IntuiMessage *)GetMsg(picwin->UserPort)) { if (msg->Class == GADGETUP && msg->IAddress != NULL) cnt = ((struct Gadget *)(msg->IAddress))->GadgetID; ReplyMsg (msg); if (cnt != -1) { RemoveGList (picwin, pzgad, -1L); return (cnt); } } Wait (1L << picwin->UserPort->mp_SigBit); } }