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.