This is Piece.m in view mode; [Download] [Up]
#import <appkit/Application.h> #import <appkit/NXImage.h> #import <appkit/Window.h> #import <dpsclient/wraps.h> #import "Piece.h" #import "TetMatrix.h" // The number of pixels to move a block at once when it is dropped. //static const float ANM_DELTA = 12.0; static const float ANM_DELTA = 28.0; extern long random(); extern void srandom(int seed); extern int getpid(); @implementation Piece #define PIECE_NULL (unsigned char *)0; #define SHAPE_NULL (struct shape *)0; const int NUM_ROTATIONS = 4; const int NUM_SHAPES=7; #define XMIN_BOUNDS(BOUNDS) ((BOUNDS & 0xc0) >> 6) #define YMIN_BOUNDS(BOUNDS) ((BOUNDS & 0x30) >> 4) #define XMAX_BOUNDS(BOUNDS) ((BOUNDS & 0xc) >> 2) #define YMAX_BOUNDS(BOUNDS) (BOUNDS & 0x3) - (void) setShapes { /* ** 1st shape ** */ shapes[0].table[0][0] = 0x0; shapes[0].table[0][1] = 0xf; // #### shapes[0].table[0][2] = 0x0; shapes[0].table[0][3] = 0x0; // x,y x1,y1 shapes[0].bounds[0] = 0x2e; // (0, 2, 3, 2) 3*4 + 2 = 14 = 0xe shapes[0].points[0] = 10; // This piece is worth 10 points shapes[0].table[1][0] = 0x4; // # shapes[0].table[1][1] = 0x4; // # shapes[0].table[1][2] = 0x4; // # shapes[0].table[1][3] = 0x4; // # shapes[0].bounds[1] = 0x47; // (1, 0, 1, 3) shapes[0].points[1] = 16; shapes[0].table[2][0] = 0x0; shapes[0].table[2][1] = 0xf; // #### shapes[0].table[2][2] = 0x0; shapes[0].table[2][3] = 0x0; shapes[0].bounds[2] = 0x2e; // (0, 2, 3, 2) shapes[0].points[2] = 10; shapes[0].table[3][0] = 0x4; // # shapes[0].table[3][1] = 0x4; // # shapes[0].table[3][2] = 0x4; // # shapes[0].table[3][3] = 0x4; // # shapes[0].bounds[3] = 0x47; // (1, 0, 1, 3) shapes[0].points[3] = 16; /* ** 2nd shape ** */ shapes[1].table[0][0] = 0xc; // ## shapes[1].table[0][1] = 0xc; // ## shapes[1].table[0][2] = 0x0; shapes[1].table[0][3] = 0x0; shapes[1].bounds[0] = 0x27; // (0, 2, 1, 3) shapes[1].points[0] = 12; shapes[1].table[1][0] = 0xc; // ## shapes[1].table[1][1] = 0xc; // ## shapes[1].table[1][2] = 0x0; shapes[1].table[1][3] = 0x0; shapes[1].bounds[1] = 0x27; // (0, 2, 1, 3) shapes[1].points[1] = 12; shapes[1].table[2][0] = 0xc; // ## shapes[1].table[2][1] = 0xc; // ## shapes[1].table[2][2] = 0x0; shapes[1].table[2][3] = 0x0; shapes[1].bounds[2] = 0x27; // (0, 2, 1, 3) shapes[1].points[2] = 12; shapes[1].table[3][0] = 0xc; // ## shapes[1].table[3][1] = 0xc; // ## shapes[1].table[3][2] = 0x0; shapes[1].table[3][3] = 0x0; shapes[1].bounds[3] = 0x27; // (0, 2, 1, 3) shapes[1].points[3] = 12; /* ** 3rd shape ** */ shapes[2].table[0][0] = 0x4; // # shapes[2].table[0][1] = 0xe; // ### shapes[2].table[0][2] = 0x0; shapes[2].table[0][3] = 0x0; shapes[2].bounds[0] = 0x2b; // (0, 2, 2, 3) shapes[2].points[0] = 10; shapes[2].table[1][0] = 0x4; // # shapes[2].table[1][1] = 0x6; // ## shapes[2].table[1][2] = 0x4; // # shapes[2].table[1][3] = 0x0; shapes[2].bounds[1] = 0x5b; // (1, 1, 2, 3) shapes[2].points[1] = 10; shapes[2].table[2][0] = 0x0; shapes[2].table[2][1] = 0xe; // ### shapes[2].table[2][2] = 0x4; // # shapes[2].table[2][3] = 0x0; shapes[2].bounds[2] = 0x1a; // (0, 1, 2, 2) shapes[2].points[2] = 12; shapes[2].table[3][0] = 0x4; // # shapes[2].table[3][1] = 0xc; // ## shapes[2].table[3][2] = 0x4; // # shapes[2].table[3][3] = 0x0; shapes[2].bounds[3] = 0x17; // (0, 1, 1, 3) shapes[2].points[3] = 10; // 4th shape shapes[3].table[0][0] = 0xc; // ## shapes[3].table[0][1] = 0x6; // ## shapes[3].table[0][2] = 0x0; shapes[3].table[0][3] = 0x0; shapes[3].bounds[0] = 0x2b; // (0, 2, 2, 3) shapes[3].points[0] = 12; shapes[3].table[1][0] = 0x4; // # shapes[3].table[1][1] = 0xc; // ## shapes[3].table[1][2] = 0x8; // # shapes[3].table[1][3] = 0x0; shapes[3].bounds[1] = 0x17; // (0, 1, 1, 3) shapes[3].points[1] = 14; shapes[3].table[2][0] = 0xc; // ## shapes[3].table[2][1] = 0x6; // ## shapes[3].table[2][2] = 0x0; shapes[3].table[2][3] = 0x0; shapes[3].bounds[2] = 0x2b; // (0, 2, 2, 3) shapes[3].points[2] = 12; shapes[3].table[3][0] = 0x4; // # shapes[3].table[3][1] = 0xc; // ## shapes[3].table[3][2] = 0x8; // # shapes[3].table[3][3] = 0x0; shapes[3].bounds[3] = 0x17; // (0, 1, 1, 3) shapes[3].points[3] = 14; // 5th shape shapes[4].table[0][0] = 0x6; // ## shapes[4].table[0][1] = 0xc; // ## shapes[4].table[0][2] = 0x0; shapes[4].table[0][3] = 0x0; shapes[4].bounds[0] = 0x2b; // (0, 2, 2, 3) shapes[4].points[0] = 12; shapes[4].table[1][0] = 0x8; // # shapes[4].table[1][1] = 0xc; // ## shapes[4].table[1][2] = 0x4; // # shapes[4].table[1][3] = 0x0; shapes[4].bounds[1] = 0x17; // (0, 1, 1, 3) shapes[4].points[1] = 14; shapes[4].table[2][0] = 0x6; // ## shapes[4].table[2][1] = 0xc; // ## shapes[4].table[2][2] = 0x0; shapes[4].table[2][3] = 0x0; shapes[4].bounds[2] = 0x2b; // (0, 2, 2, 3) shapes[4].points[2] = 12; shapes[4].table[3][0] = 0x8; // # shapes[4].table[3][1] = 0xc; // ## shapes[4].table[3][2] = 0x4; // # shapes[4].table[3][3] = 0x0; shapes[4].bounds[3] = 0x17; // (0, 1, 1, 3) shapes[4].points[3] = 14; // 6th shape shapes[5].table[0][0] = 0x2; // # shapes[5].table[0][1] = 0xe; // ### shapes[5].table[0][2] = 0x0; shapes[5].table[0][3] = 0x0; shapes[5].bounds[0] = 0x2b; // (0, 2, 2, 3) shapes[5].points[0] = 6; shapes[5].table[1][0] = 0x8; // # shapes[5].table[1][1] = 0x8; // # shapes[5].table[1][2] = 0xc; // ## shapes[5].table[1][3] = 0x0; shapes[5].bounds[1] = 0x17; // (0, 1, 1, 3) shapes[5].points[1] = 7; shapes[5].table[2][0] = 0xe; // ### shapes[5].table[2][1] = 0x8; // # shapes[5].table[2][2] = 0x0; shapes[5].table[2][3] = 0x0; shapes[5].bounds[2] = 0x2b; // (0, 2, 2, 3) shapes[5].points[2] = 6; shapes[5].table[3][0] = 0xc; // ## shapes[5].table[3][1] = 0x4; // # shapes[5].table[3][2] = 0x4; // # shapes[5].table[3][3] = 0x0; shapes[5].bounds[3] = 0x17; // (0, 1, 1, 3) shapes[5].points[3] = 7; // 7th shape shapes[6].table[0][0] = 0xe; // ### shapes[6].table[0][1] = 0x2; // # shapes[6].table[0][2] = 0x0; shapes[6].table[0][3] = 0x0; shapes[6].bounds[0] = 0x2b; // (0, 2, 2, 3) shapes[6].points[0] = 6; shapes[6].table[1][0] = 0x4; // # shapes[6].table[1][1] = 0x4; // # shapes[6].table[1][2] = 0xc; // ## shapes[6].table[1][3] = 0x0; shapes[6].bounds[1] = 0x17; // (0, 1, 1, 3) shapes[6].points[1] = 7; shapes[6].table[2][0] = 0x8; // # shapes[6].table[2][1] = 0xe; // ### shapes[6].table[2][2] = 0x0; shapes[6].table[2][3] = 0x0; shapes[6].bounds[2] = 0x2b; // (0, 2, 2, 3) shapes[6].points[2] = 6; shapes[6].table[3][0] = 0xc; // ## shapes[6].table[3][1] = 0x8; // # shapes[6].table[3][2] = 0x8; // # shapes[6].table[3][3] = 0x0; shapes[6].bounds[3] = 0x17; // (0, 1, 1, 3) shapes[6].points[3] = 7; } BOOL resize(NXSize *aSize) { BOOL resized = NO; if (aSize->height > 32) { aSize->height = 32; resized = YES; } if (aSize->width > 32) { aSize->width = 32; resized = YES; } return resized; } - findBlockImages { int i; char buffer[20]; id image; char *blockName; BOOL isGameColor = [NXApp isGameColor]; if (isGameColor) { blockName = "ColorBlock"; } else { blockName = "MonoBlock"; } for (i=0; i<4; i++) { sprintf(buffer, "%s%d", blockName, i+1); image = [NXImage findImageNamed:buffer]; if (image == NULL) { fprintf(stderr, "Couldn't find block named %s\n", buffer); exit(0); } blockImage[i] = image; } // Assuming all the blocks are the same size. [blockImage[0] getSize:&blockSize]; if (resize(&blockSize)) { #ifdef DEBUG printf("resizing Block Image\n"); #endif [bitmap setScalable:YES]; [bitmap setSize:&blockSize]; } return self; } - init { [super init]; [self setShapes]; curCol = curRow = 0; currentPiece = PIECE_NULL; currentShape = SHAPE_NULL; currentRotation = shapenum = 0; invalidRect.origin.x = invalidRect.origin.y = invalidRect.size.width = invalidRect.size.height = 0.0; // Initialized the animation Rect { NXSize tmp; [self findBlockImages]; // Limit size of block image [blockImage[0] getSize:&tmp]; resize(&tmp); tmp.height *= 4; tmp.width *= 4; anmShape = [[NXImage alloc] initSize:&tmp]; [anmShape lockFocus]; PSsetalpha(0.0); // make this block transparent PSrectfill(0., 0., tmp.width, tmp.height); PSsetalpha(1.0); } anmBackground = nil; viewVarsInited = NO; dropPoints = 0; srandom(getpid()); // Initialize random number generator #ifdef DEBUG printf("Piece object initialized\n"); #endif return self; } /* * Draw the Piece. * The focus must be locked by on calling object's view before invoking this method. */ - draw:sender { unsigned char mask; int xc, yc; int ycmax; NXPoint dPt; ycmax = MAX_SHAPE_SIZE - YMIN_BOUNDS(currentShape->bounds[currentRotation]); for (yc = MAX_SHAPE_SIZE - YMAX_BOUNDS(currentShape->bounds[currentRotation]) - 1; yc < ycmax; yc++) { mask = 1 << (MAX_SHAPE_SIZE - 1); for (xc = 0; xc < MAX_SHAPE_SIZE; xc++) { if (currentPiece[yc] & mask) { // Call the sender's parent object [sender point:&dPt for:curRow + MAX_SHAPE_SIZE - yc - 1 :curCol + xc]; [bitmap composite:NX_SOVER toPoint:&dPt]; } mask >>= 1; } } return self; } /* * Determine what the next piece to drop will be. * */ - newPiece { int bitmapNum; #ifdef DEBUG printf("Generating a new piece\n"); #endif shapenum = random() % NUM_SHAPES; // shapenum = 0; currentShape = &shapes[shapenum]; currentRotation = random() % NUM_ROTATIONS; currentPiece = currentShape->table[currentRotation]; bitmapNum = random() % NUM_BITMAPS; bitmap = blockImage[bitmapNum]; if (bitmap == NULL) { printf("Block %d not found\n", bitmapNum); } return self; } /* * Needed so other objects can query to set the Window and View sizes. */ - getBlockSize:(NXSize *) size { *size = blockSize; return self; } /* * So other objects can query Piece.m about images */ - getBlockImage:(int) blockNum { return blockImage[blockNum]; } - (struct pieceInfo *)pieceInfo { static struct pieceInfo info; info.bitmap = bitmap; info.shape = shapenum; info.rotation = currentRotation; return &info; } - setPiece:(struct pieceInfo *)info { bitmap = info->bitmap; [bitmap getSize:&blockSize]; resize(&blockSize); shapenum = info->shape; currentShape = &shapes[shapenum]; currentRotation = info->rotation; currentPiece = currentShape->table[currentRotation]; return self; } /* * Get the next piece. * */ - (BOOL)reset:sender piece:(struct pieceInfo *)info { BOOL legalMove; unsigned char shapeBounds; int xmin, xmax; int ymin; NXRect bounds; #ifdef DEBUG printf("reseting piece\n"); #endif // info will contain the piece from NextMatrix if Show Next is selected // Otherwise, we'll generate a new piece. if (info) { [self setPiece:info]; } else { [self newPiece]; } shapeBounds = currentShape->bounds[currentRotation]; xmin = XMIN_BOUNDS(shapeBounds); xmax = XMAX_BOUNDS(shapeBounds); ymin = YMIN_BOUNDS(shapeBounds); curCol = (TETRIS_COLUMNS - (xmax - xmin + 1)) / 2; curRow = TETRIS_ROWS - ymin - 1; if (!viewVarsInited) { [sender getBounds:&bounds]; [sender getIntercell:&intercell]; // Create a new animation background anmBackground = [[NXImage alloc] initSize:&bounds.size]; viewVarsInited = YES; } if ((legalMove = [self legalMove:sender :curRow :curCol rotation:currentRotation])) { [self getInvalidRect:&invalidRect for:sender]; [sender display:&invalidRect :1]; } dropPoints = 0; return legalMove; } - (BOOL)legalMove:sender :(int)row :(int)column rotation:(int)rotation { unsigned char mask; int xc, yc; int ycmax; int ex, ey; unsigned char *piece; piece = currentShape->table[rotation]; ycmax = MAX_SHAPE_SIZE - YMIN_BOUNDS(currentShape->bounds[rotation]); for (yc = MAX_SHAPE_SIZE - YMAX_BOUNDS(currentShape->bounds[rotation]) - 1; yc < ycmax; yc++) { mask = 1 << (MAX_SHAPE_SIZE - 1); for (xc = 0; xc < MAX_SHAPE_SIZE; xc++) { if (piece[yc] & mask) { ey = row + MAX_SHAPE_SIZE - yc - 1; ex = column + xc; if (ex < 0 || ex >= TETRIS_COLUMNS || ey < 0 || [sender bitmapAt:ey :ex]) return NO; } mask >>= 1; } } return YES; } - left:sender { if ([self legalMove:sender :curRow :(curCol - 1) rotation:currentRotation]) { curCol--; [sender display:&invalidRect :1]; [self getInvalidRect:&invalidRect for:sender]; } return self; } - right:sender { if ([self legalMove:sender :curRow :(curCol + 1) rotation:currentRotation]) { curCol++; [sender display:&invalidRect :1]; [self getInvalidRect:&invalidRect for:sender]; } return self; } - getInvalidRect:(NXRect *)aRect for:sender { unsigned char bounds; NXRect unionRect; bounds = currentShape->bounds[currentRotation]; [sender getRect:&unionRect for:curRow + YMIN_BOUNDS(bounds) :curCol + XMIN_BOUNDS(bounds)]; [sender getRect: aRect for:curRow + YMAX_BOUNDS(bounds) :curCol + XMAX_BOUNDS(bounds)]; NXUnionRect(&unionRect, aRect); return self; } - point:(NXPoint *)thePoint for:(int)row :(int)column { unsigned char bounds = currentShape->bounds[currentRotation]; int realx = column - curCol - XMIN_BOUNDS(bounds); int realy = row - curRow - YMIN_BOUNDS(bounds); thePoint->x = realx * (intercell.width + blockSize.width); thePoint->y = realy * (intercell.height + blockSize.height); return self; } /* * Called when the player his the spacebar to drop the piece. */ - compositeShape:sender { if (anmShape) { // already exists // [self->anmShape resize:self->invalidRect.size.width :self->invalidRect.size.height]; //self->anmShape = [NXImage newSize:&self->invalidRect.size]; } else { // create new image //self->anmShape = [NXImage newSize:&self->invalidRect.size]; //.width // :self->invalidRect.size.height type:NX_UNIQUEALPHABITMAP]; // [self->anmShape setFlip:NO]; } [anmShape lockFocus]; PSsetalpha(0.0); // Fill the window with alpha PSrectfill(0.0, 0.0, self->invalidRect.size.width, self->invalidRect.size.height); PSsetalpha(1.0); // Draw image w/o alpha [self draw:self]; [anmShape unlockFocus]; return self; } - compositeBackground: sender { NXRect unionRect; [self getInvalidRect:&unionRect for:sender]; NXUnionRect(&self->invalidRect, &unionRect); [anmBackground lockFocus]; [sender setPieceVisible:NO]; [sender drawSelf:&unionRect :1]; [sender setPieceVisible:YES]; [anmBackground unlockFocus]; return self; } /* * Move the piece rapidly down the screen after the user hits the spacebar. * */ - animateDrop:sender { NXRect updRect; [self getInvalidRect:&updRect for:sender]; [sender lockFocus]; // Erase piece by compositing the background over it. [anmBackground composite:NX_SOVER fromRect: &invalidRect toPoint:&invalidRect.origin]; while (invalidRect.origin.y - ANM_DELTA > updRect.origin.y) { invalidRect.origin.y -= ANM_DELTA; // Show the piece dropping one increment [anmShape composite:NX_SOVER toPoint:&invalidRect.origin]; [[sender window] flushWindow]; // Erase the piece by showing the background [anmBackground composite:NX_SOVER fromRect:&invalidRect toPoint:&invalidRect.origin]; } [sender unlockFocus]; [anmShape lockFocus]; [anmShape getSize:&updRect.size]; PScompositerect(0.0, 0.0, updRect.size.width, updRect.size.height, NX_CLEAR); [anmShape unlockFocus]; return self; } /* * Return the amount of points that we get for this piece. */ - (int)points { return currentShape->points[currentRotation] + dropPoints; } /* * Called when the user hits the space bar. * */ - drop:sender { int ymin; int yc; ymin = YMIN_BOUNDS(currentShape->bounds[currentRotation]); for (yc = curRow - 1; yc >= -1 - ymin; yc--) { if (![self legalMove:sender :yc :curCol rotation:currentRotation]) break; } dropPoints += curRow - yc + 1; // You get extra points for dropping it curRow = yc + 1; [self compositeShape: sender]; [self compositeBackground: sender]; [self animateDrop: sender]; [sender display:&invalidRect : 1]; [self getInvalidRect:&invalidRect for:sender]; return self; } /* * Move the piece down. */ - (BOOL)down:sender { BOOL canGoDown; if ((canGoDown = [self legalMove:sender :curRow-1 :curCol rotation:currentRotation])){ curRow--; [sender display:&invalidRect :1]; [self getInvalidRect:&invalidRect for:sender]; } return canGoDown; } - turn:sender { int newRotation = (currentRotation + 3) % NUM_ROTATIONS; if ([self legalMove:sender :curRow :curCol rotation:newRotation]) { currentRotation = newRotation; currentPiece = currentShape->table[newRotation]; [sender display:&invalidRect :1]; [self getInvalidRect:&invalidRect for:sender]; } return self; } /* * Called when the piece has reached the bottom and can no longer be moved. * * For each block that composes the image, put the block's image id in the * proper iconMatrix[row][column] position. Thus to redraw the screen, * we need only to composite the images from the iconMatrix. */ - stick:sender { unsigned char mask; int col, theRow; int rowMax; rowMax = MAX_SHAPE_SIZE - YMIN_BOUNDS(currentShape->bounds[currentRotation]); for (theRow = MAX_SHAPE_SIZE - YMAX_BOUNDS(currentShape->bounds[currentRotation]) - 1; theRow < rowMax; theRow++) { mask = 1 << (MAX_SHAPE_SIZE - 1); for (col = 0; col < MAX_SHAPE_SIZE; col++) { // If there's a block in if (currentPiece[theRow] & mask) // Set the (row, column) of the matrix equal to Block's image. [sender setBitmap:bitmap at:curRow + MAX_SHAPE_SIZE - theRow - 1 :curCol + col]; mask >>= 1; } } return self; } - (int)getCurRow { return curRow; } - free { if (anmShape) [anmShape free]; if (anmBackground) [anmBackground free]; return [super free]; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.