This is BoardView.m in view mode; [Download] [Up]
/* * BoardView.h * Description: * a view that knows how to draw a game board for a two player game * with a grid board like checkers, chess, reversi or attaxx * History: * 14-Feb-93 Erik Kay - created */ #import "BoardView.h" // the timed entry function for doing board animation when a move has happened void animateMove (DPSTimedEntry te, double now, void *data) { [(id)data animateMove]; } // the timed entry function for doing board animation when some sort of change // has happened, such as a piece capture void animateChange (DPSTimedEntry te, double now, void *data) { [(id)data animateChange]; } @implementation BoardView - initFrame:(const NXRect *)rect { [super initFrame:rect]; scalable = NO; squareSize = 52; pieceSize = squareSize - 4; highlightedRow = highlightedCol = selectedRow = selectedCol = -1; numAnimationFrames = 3; abortGame = NO; rows = cols = 0; curFrame = 0; direction = 1; return self; } // how many frames of animation for moves and changes - (int)numAnimationFrames { return numAnimationFrames; } // the various drawing methods for squares that are in various states - drawSelectedSquare:(NXRect *)rect at:(int)col :(int)row { NXDrawGrayBezel(rect,(NXRect *)0); return self; } - drawEmptySquare:(NXRect *)rect at:(int)col :(int)row { NXDrawGroove(rect,(NXRect *)0); return self; } - drawHighlightedSquare:(NXRect *)rect at:(int)col :(int)row { PSsetgray(0.333); NXRectFill(rect); return self; } - drawObstruction:(NXRect *)rect at:(int)col :(int)row { PSsetgray(0.0); NXRectFill(rect); return self; } - drawPlayerOne:(NXRect *)rect at:(int)col :(int)row { int width; width = NX_WIDTH(rect); PSsetgray(1.0); PSarc(NX_MIDX(rect),NX_MIDY(rect),(width-8)/2, 0,360); PSfill(); return self; } - drawPlayerTwo:(NXRect *)rect at:(int)col :(int)row { int width; width = NX_WIDTH(rect); PSsetgray(0.0); PSarc(NX_MIDX(rect),NX_MIDY(rect),(width-8)/2, 0,360); PSfill(); return self; } // the feeder method to look at a square and decide which method should be // called to draw it, and to then do the drawing - drawSquare:(int)c :(int)r { NXRect rect; NXSetRect(&rect,c * squareSize, r * squareSize, squareSize, squareSize); // first, draw the square background (selected, highlighted, empty) if (c == selectedCol && r == selectedRow) [self drawSelectedSquare:&rect at:c :r]; else if (c == highlightedCol && r == highlightedRow) [self drawHighlightedSquare:&rect at:c :r]; else if ([board pieceAt:c :r] != SQUARE_ERROR) [self drawEmptySquare:&rect at:c :r]; // now draw what's on top of the square(blocked, empty, player 1, player 2) switch ([board pieceAt:c :r]) { case SQUARE_BLOCKED: [self drawObstruction:&rect at:c :r]; break; case SQUARE_ONE: [self drawPlayerOne:&rect at:c :r]; break; case SQUARE_TWO: [self drawPlayerTwo:&rect at:c :r]; break; case SQUARE_EMPTY: default: break; } return self; } // compare the current board to the copy of the last board state we have // only draw the squares that have changed - drawSelf:(const NXRect *)rects :(int)count { int r, c; if (!boardCopy) { PSsetgray(0.667); NXRectFillList(rects,count); } for (c = 0; c < cols; c++) for (r = 0; r < rows; r++) { if (!boardCopy || ([board pieceAt:c :r] != [boardCopy pieceAt:c :r]) || ((c == selectedCol) && (r == selectedRow)) || ((c == highlightedCol) && (r == highlightedRow))) [self drawSquare:c :r]; } if (boardCopy) [boardCopy free]; boardCopy = [board trueCopy]; return self; } // clear the boardCopy, then do a display. This forces a complete redraw of // the board - redrawBoard { if (boardCopy) { [boardCopy free]; boardCopy = nil; } [self display]; return self; } // get the mouseDown, figure out which square it's associated with, then // pass the message on to the game delegate - mouseDown:(NXEvent *)ev { int col, row; [self convertPoint:&ev->location fromView:[[self superview] superview]]; /* convert from view coords to grid coords */ col = ev->location.x / squareSize; row = ev->location.y / squareSize; [game squareHit:col :row]; return self; } // deselect the old selected square, and select the new one // set both of the locations to be a SQUARE_ERROR to force an update // of both of those squares in the display - selectSquare:(int)col :(int)row { [boardCopy setPiece:SQUARE_ERROR at:selectedCol :selectedRow]; selectedCol = col; selectedRow = row; [boardCopy setPiece:SQUARE_ERROR at:selectedCol :selectedRow]; return self; } // set up the move animation timed entry - doMoveAnimation:(move *)mv { if (abortGame) { if (moveEntry) { DPSRemoveTimedEntry(moveEntry); moveEntry = NULL; } abortGame = NO; } currentMove = *mv; board->currentMove = *mv; [self selectSquare:mv->from.col :mv->from.row]; moveEntry = DPSAddTimedEntry(0.1,animateMove, (void *)self,NX_BASETHRESHOLD); [self display]; return self; } // what gets called by the move animation timed entry // this decides the state of things, and then actually calls the doAnimateMove - animateMove { if (abortGame) { curFrame = 0; DPSRemoveTimedEntry(moveEntry); abortGame = NO; return self; } [self doAnimateMove]; curFrame++; if (curFrame >= numAnimationFrames) { curFrame = 0; DPSRemoveTimedEntry(moveEntry); [self finishMoveAnimation]; } return self; } // actually do the animation for a move - doAnimateMove { if (highlightedCol > -1) { [boardCopy setPiece:SQUARE_ERROR at:highlightedCol :highlightedRow]; highlightedCol = highlightedRow = -1; } else { highlightedCol = currentMove.to.col; highlightedRow = currentMove.to.row; [boardCopy setPiece:SQUARE_ERROR at:highlightedCol :highlightedRow]; } [self display]; return self; } // move animation is finished! clean up and tell the delegate - finishMoveAnimation { moveEntry = NULL; highlightedRow = highlightedCol = -1; [self selectSquare:-1 :-1]; [self display]; [game moveAnimationFinished:¤tMove]; return self; } // set up the change animation timed entry // by default, this does nothing - doChangeAnimation:(Board *)orig { [self finishChangeAnimation]; return self; } // what gets called by the move animation timed entry // this decides the state of things, and then actually calls the doAnimateMove - animateChange { if (abortGame) { curFrame = 0; DPSRemoveTimedEntry(changeEntry); abortGame = NO; return self; } [self doAnimateChange]; if (direction == 1) { curFrame++; if (curFrame >= numAnimationFrames) { direction = -1; } } else { curFrame--; if (curFrame < 0) { curFrame = 0; direction = 1; DPSRemoveTimedEntry(changeEntry); [self finishChangeAnimation]; } } return self; } // the actual change animation. it does nothing by default - doAnimateChange { return self; } // change animation is finished! clean up and tell the delegate - finishChangeAnimation { changeEntry = NULL; [game changeAnimationFinished]; return self; } // set the game for this board (our delegate!) - setGame:g { game = g; return self; } // the board we're using - (Board *)board { return board; } // when the board is changed, we need to re-organize a bit // (i.e. resize window if necessary, redraw things, etc) - setBoard:(Board *)b { int r,c; NXRect newfrm, f, contentR; NXSize newSize; board = b; r = [b rows]; c = [b cols]; highlightedRow = highlightedCol = selectedRow = selectedCol = -1; rows = r; cols = c; if (!scalable) { // if not scalable, then resize the view/window NXSetRect(&newfrm, NX_X(&frame), NX_Y(&frame), squareSize * cols, squareSize * rows); [[window contentView] getFrame:&contentR]; newSize.width = (NX_WIDTH(&newfrm) + (NX_WIDTH(&contentR) - NX_WIDTH(&frame))); newSize.height = (NX_HEIGHT(&newfrm) + (NX_HEIGHT(&contentR) - NX_HEIGHT(&frame))); [window sizeWindow:newSize.width :newSize.height]; [self setFrame:&newfrm]; [[[self window] contentView] getFrame:&f]; } else { if (NX_WIDTH(&frame) > NX_HEIGHT(&frame)) squareSize = NX_HEIGHT(&frame) / rows; else squareSize = NX_WIDTH(&frame) / cols; pieceSize = squareSize - 4; } //! this is kind of weird! I'm not sure why the origin is so messed up //! here. It seems to be coming out as (1,9) rather than (0,0) NX_X(&f) = 0; NX_Y(&f) = 0; [[window contentView] lockFocus]; PSsetgray(0.667); NXRectFill(&f); [[window contentView] unlockFocus]; [window flushWindow]; [boardCopy free]; boardCopy = nil; [self display]; return self; } - (int)selectedRow { return selectedRow; } - (int)selectedCol { return selectedCol; } - resetBoardView { abortGame = YES; return self; } - gameOver { return self; } - setScalable:(BOOL)f { scalable = f; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.