This is ScoreBoard.m in view mode; [Download] [Up]
// ScoreBoard.m // Project: Cribbage // Stephan Wacker // 93-09-21 #import "ScoreBoard.h" #import "ScoreGoal.h" #import <assert.h> // Number of pegs in a row and in a group. #define LEN 30 // number of pegs in a row #define GRP 5 // number of pegs in a group #define DIST 0.5 // additional distance between groups of pegs #define TOTAL_LEN (2 + LEN + (LEN/GRP)*DIST) // == 35 // width of board expressed in units of one peg field width @implementation ScoreBoard - (NXColor) color { [self subclassResponsibility: _cmd]; return NX_COLORCLEAR; } - (BOOL) topRowFirst { [self subclassResponsibility: _cmd]; return NO; } - makePegImage: (NXColor) color // // Create the peg image from a shape image, a color and a mask image. // { NXImage *shape = [NXImage findImageNamed: "pegShape"]; NXImage *mask = [NXImage findImageNamed: "pegMask"]; NXRect rect; // Create a new image having the same size as the shape. [shape getSize: &rect.size]; rect.origin.x = rect.origin.y = 0; if( !pegImage ) { pegImage = [[NXImage alloc] initSize: &rect.size]; [pegImage useCacheWithDepth: NX_TwelveBitRGBDepth]; } // Draw into the pegImage. [pegImage lockFocus]; // Fill it with the given color. NXSetColor( color ); NXRectFill( &rect ); // Composite the shape over it; the shape's transparency will determine // where the color is visible. [shape composite: NX_SOVER toPoint: &rect.origin]; // Composite the mask over it; the pegImage will take on the transparency // of the mask. [mask composite: NX_DIN toPoint: &rect.origin]; [pegImage unlockFocus]; return self; } - initFrame: (const NXRect *) frameRect { [super initFrame: frameRect]; [self setOpaque: NO]; [self makePegImage: [self color]]; games = -1; // signals first game return self; } - changePegColor { [self makePegImage: [self color]]; [window disableFlushWindow]; [self lockFocus]; [self putPeg: prevScore]; [self putPeg: score]; [self unlockFocus]; [window reenableFlushWindow]; [window flushWindowIfNeeded]; return self; } - clear { [self lockFocus]; if( games < 0 ) { games = 0; [gamesField setIntValue: games]; } else { // There is a problem when after a long game the game limit is // reduced to a short game. [self limit] will be less than score // and the pegs are not removed correctly. The solution is to // make sure that all scores are less than [self limit]. while( prevScore > 2*LEN ) prevScore -= 2*LEN; while( score > 2*LEN ) score -= 2*LEN; [self removePeg: prevScore]; [self removePeg: score]; [self removePeg: [self limit]]; // winner's peg } score = 0, prevScore = -1; [self putPeg: prevScore]; [self putPeg: score]; [self unlockFocus]; return self; } - drawBackground { NXRect rect = bounds; [self convertRect: &rect toView: backgroundView]; // Display background behind the rect we will composite over. [backgroundView display: &rect : 1 : YES]; return self; } - drawDecoration { NXImage *decoration = [NXImage findImageNamed: "boardDecoration"]; NXRect rect; [backgroundView getBounds: &rect]; if( [backgroundView isFlipped] ) { rect.origin.y += rect.size.height; } [backgroundView lockFocus]; // [decoration composite: NX_SOVER toPoint: &rect.origin]; [decoration dissolve: 50 toPoint: &rect.origin]; [backgroundView unlockFocus]; return self; } - drawSelf: (const NXRect *) rects: (int) rectCount // // Draw the pegging board. // { int i; [self drawBackground]; // [self drawDecoration]; for( i = -1; i <= 2*LEN; i++ ) { [self removePeg: i]; } [self removePeg: [self limit]]; [self putPeg: prevScore]; [self putPeg: score]; return self; } - (int) limit { return [goalView limit]; } - (int) score { return score; } - peg: (int) points // // Return non-nil iff game is won. // { id result = nil; if( points > 0 ) { [self lockFocus]; [self removePeg: prevScore]; prevScore = score; score += points; if( score >= [self limit] ) { score = [self limit]; result = self; } [self putPeg: score]; [self unlockFocus]; } return result; } - (NXPoint) pegCoords: (int) pos // // Return center coordinates of peg number pos. // All peg images should have the same size: 11x11. // { NXPoint result; NXRect rect = bounds; int x, y; // Determine x and y coordinates of the peg. // x = pos, y = 0; while( x > 2*LEN ) { x -= 2*LEN; } if( x > LEN ) { x = (2*LEN+1)-x, y = 1; } if( [self topRowFirst] ) y = 1-y; assert( -1 <= x && x <= LEN ); // Partition total width and set x coordinate of peg's rect. // rect.size.width /= TOTAL_LEN; rect.origin.x += (x+1 + ((x+GRP-1)/GRP) * DIST) * rect.size.width; // Put peg's rect at top or bottom of view and make it square. // if( y == 1 ) { rect.origin.y = rect.size.height - rect.size.width; } rect.size.height = rect.size.width; // Find center of rect. // result.x = NX_MIDX(&rect); result.y = NX_MIDY(&rect); return result; } - putImage: (NXImage *) image at: (int) pos // // All peg images should have the same size. // Pegs are drawn as transparent images over an opaque background object. // The last peg is drawn in the goalView. // { NXRect rect, rect2; id drawingView = self; if( pos >= [self limit] ) { [(drawingView = goalView) lockFocus]; rect.origin = [goalView pegCoords]; } else { rect.origin = [self pegCoords: pos]; } [image getSize: &rect.size]; rect.origin.x -= rect.size.width / 2; rect.origin.y -= rect.size.height / 2; // Convert to coordinate system of the background. rect2 = rect; [drawingView convertRect: &rect2 toView: backgroundView]; // Display background behind the rect we will composite over. [backgroundView display: &rect2 : 1 : YES]; // Display the peg image. [image composite: NX_SOVER toPoint: &rect.origin]; if( pos >= [self limit] ) { [goalView unlockFocus]; } return self; } - removePeg: (int) pos { return [self putImage: [NXImage findImageNamed: "pegHole"] at: pos]; } - putPeg: (int) pos { [scoreField setIntValue: score]; return [self putImage: pegImage at: pos]; } - win: (int) gameValue { games += gameValue; [gamesField setIntValue: games]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.