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.