/* Copyright (c) 1992, Trevor Smigiel. All rights reserved. (I hope Commodore doesn't mind that I borrowed their copyright notice.) The source and executable code of this program may only be distributed in free electronic form, via bulletin board or as part of a fully non-commercial and freely redistributable diskette. Both the source and executable code (including comments) must be included, without modification, in any copy. This example may not be published in printed form or distributed with any commercial product. This program is provided "as-is" and is subject to change; no warranties are made. All use is at your own risk. No liability or responsibility is assumed. */ #include #include #include #include #include "tetris.h" #include #include #include #include #include "main_protos.h" #include "TetrisImages_protos.h" struct TBoard * NewBoard(struct RastPort *rp, WORD bleft, WORD btop) { struct TBoard *board; int i; board = AllocMem(sizeof(struct TBoard), MEMF_CLEAR); if (board) { InitBitMap(&board->bitmap, Depth, PWidth, PHeight); board->bitmap.Planes[0] = AllocMem(board->bitmap.BytesPerRow * board->bitmap.Rows * board->bitmap.Depth, MEMF_CHIP | MEMF_CLEAR); if (board->bitmap.Planes[0]) { for (i = 1; i < Depth; i++) board->bitmap.Planes[i] = board->bitmap.Planes[i-1] + board->bitmap.BytesPerRow; board->bitmap.BytesPerRow *= Depth; board->rastport = rp; board->board_off.x = bleft; board->board_off.y = btop; board->score_off.x = bleft; board->score_off.y = 1 * (board->rastport->TxHeight + 1) + btop - YSize * 4; board->line_off.x = bleft; board->line_off.y = 3 * (board->rastport->TxHeight + 1) + btop - YSize * 4; board->time_off.x = bleft; board->time_off.y = 2 * (board->rastport->TxHeight + 1) + btop - YSize * 4; board->next_off.x = bleft + PWidth - XSize * 4; board->next_off.y = btop - ((YSize * 7) / 2); for (i = 0; i < PFHEIGHT; i++) { board->level.board[i] = 0x801f; } board->level.board[i] = 0xffff; board->level.goal = GOAL_TIME_LIMIT | GOAL_CLEAR_LINES; board->level.lines = 5; board->level.time = 60; board->level.line_up = 30; CurrentTime((LONG *)&board->seed[1], (LONG *)&board->seed[0]); board->next_which = TFirst[nrand48(board->seed) % 7]; board->piece.which = TFirst[nrand48(board->seed) % 7]; board->piece.bx = (PFWIDTH / 2) - 2; board->piece.x = board->piece.bx * XSize; board->piece.by = 0; board->piece.y = 0; board->next_sec = UPDATE_SECONDS; board->points = 0; board->clevel = Level; board->next_line_up = board->level.line_up; board->drop_up = 30; board->next_drop_up = board->drop_up; board->drop = 10; board->next_drop = board->drop; board->lines = board->level.lines; board->time = board->level.time; return board; } FreeMem(board, sizeof(struct TBoard)); } return NULL; } void FreeBoard(struct TBoard *board) { FreeMem(board->bitmap.Planes[0], board->bitmap.BytesPerRow * board->bitmap.Rows); FreeMem(board, sizeof(struct TBoard)); } void AddPiece(struct TBoard *board) { UWORD piece = TPieces[board->piece.which], p; UWORD *b = &board->level.board[board->piece.by]; WORD x = board->piece.bx + 1; *b |= ((piece & 0xf000) >> x); if (p = piece & 0x0f00) { b++; x -= 4; *b |= ((x >= 0) ? (p >> x) : (p << -x)); if (p = piece & 0x00f0) { b++; x -= 4; *b |= ((x >= 0) ? (p >> x) : (p << -x)); if (p = piece & 0x000f) { b++; x -= 4; *b |= ((x >= 0) ? (p >> x) : (p << -x)); } } } } WORD TestMove(struct TBoard *board, WORD which, WORD x, WORD y) { UWORD piece = TPieces[which]; UWORD *b = &board->level.board[y]; UWORD p; x++; if (*b & ((piece & 0xf000) >> x)) return 0; if (p = piece & 0x0f00) { b++; x -= 4; if (*b & ((x >= 0) ? (p >> x) : (p << -x))) return 0; if (p = piece & 0x00f0) { b++; x -= 4; if (*b & ((x >= 0) ? (p >> x) : (p << -x))) return 0; if (p = piece & 0x000f) { b++; x -= 4; if (*b & ((x >= 0) ? (p >> x) : (p << -x))) return 0; } } } return 1; } void CheckRows(struct TBoard *board, WORD btop, WORD pheight) { UWORD *b, *src, *dst; WORD top, height, ymax; int first; int i, j; char buf[12]; int pt = 0; top = 0; for (i = btop - 1; board->level.board[i] & 0x7fe0; i--); first = i + 1; top = first * YSize; height = (btop - first) * YSize; ymax = board->board_off.y + top + height + YSize - 1; for (b = &board->level.board[btop], i = 0; i < pheight; i++, b++) { if (*b == 0xffff) { if (height) BltBitMap(&board->bitmap, 0, top, &board->bitmap, 0, top + YSize, PWidth, height, 0xC0, 0xff, NULL); BltClear(board->bitmap.Planes[0] + top * board->bitmap.BytesPerRow, board->bitmap.BytesPerRow * YSize, 0); ScrollRaster(board->rastport, 0, -YSize, board->board_off.x, board->board_off.y + top, board->board_off.x + PWidth - 1, ymax); for (src = b - 1, dst = b, j = i + btop - first; j > 0; j--) *dst-- = *src--; board->level.board[first] = 0x801f; top += YSize; first++; pt++; board->points += pt; sprintf(buf, "Score:%4d", board->points); Move(board->rastport, board->score_off.x, board->score_off.y); Text(board->rastport, buf, 10); board->lines--; sprintf(buf, "Lines:%4d", board->lines); Move(board->rastport, board->line_off.x, board->line_off.y); Text(board->rastport, buf, 10); if ((board->level.goal & GOAL_CLEAR_LINES) && (board->lines <= 0)) { board->flags |= TETF_NEXTLEVEL; } } else { height += YSize; } ymax += YSize; } } void PrintBits(struct TBoard *board) { int i, j; for (i = 0; i <= PFHEIGHT; i++) { for (j = 0x4000; j & 0x7fe0; j >>= 1) printf((j & board->level.board[i]) ? "1" : "0"); printf("\n"); } printf("\n"); } void MoveTetris(struct TBoard *board, WORD move) { struct TPiece npiece; WORD left, top, width, height; if ((board) && (board->flags & (TETF_GAMEOVER | TETF_NEXTLEVEL))) return; npiece = board->piece; left = board->piece.x; top = board->piece.y; width = TSizes[board->piece.which].x; height = TSizes[board->piece.which].y; switch (move) { case COMMAND_DOWN: npiece.by++; npiece.y += YSize; height += YSize; break; case COMMAND_LEFT: npiece.bx--; npiece.x -= XSize; left -= XSize; width += XSize; break; case COMMAND_RIGHT: npiece.bx++; npiece.x += XSize; width += XSize; break; case COMMAND_ROTATE: npiece.which = TNext[npiece.which]; if (TSizes[npiece.which].x > width) width = TSizes[npiece.which].x; if (TSizes[npiece.which].y > height) height = TSizes[npiece.which].y; break; default: break; } if (TestMove(board, npiece.which, npiece.bx, npiece.by)) { BltBitMap(&TBitMaps[board->piece.which], 0, 0, &board->bitmap, board->piece.x, board->piece.y, TSizes[board->piece.which].x, TSizes[board->piece.which].y, 0x22, 0xff, NULL); board->piece = npiece; BltBitMap(&TBitMaps[npiece.which], 0, 0, &board->bitmap, board->piece.x, board->piece.y, TSizes[npiece.which].x, TSizes[npiece.which].y, 0xEE, 0xff, NULL); WaitTOF(); BltBitMapRastPort(&board->bitmap, left, top, board->rastport, board->board_off.x + left, board->board_off.y + top, width, height, 0xC0); } else if (move == COMMAND_DOWN) { AddPiece(board); CheckRows(board, board->piece.by, TSSizes[board->piece.which].y); board->piece.bx = (PFWIDTH / 2) - 2; board->piece.x = board->piece.bx * XSize; board->piece.by = 0; board->piece.y = 0; board->piece.which = board->next_which; board->next_which = TFirst[nrand48(board->seed) % 7]; //board->pieces[board->piece.which]++; BltBitMap(&TBitMaps[board->piece.which], 0, 0, &board->bitmap, board->piece.x, board->piece.y, TSizes[board->piece.which].x, TSizes[board->piece.which].y, 0xEE, 0xff, NULL); BltBitMapRastPort(&board->bitmap, board->piece.x, board->piece.y, board->rastport, board->board_off.x + board->piece.x, board->board_off.y + board->piece.y, TSizes[board->piece.which].x, TSizes[board->piece.which].y, 0xC0); BltBitMapRastPort(&TBitMaps[board->piece.which], 0, 0, board->rastport, board->next_off.x, board->next_off.y, TSizes[board->piece.which].x, TSizes[board->piece.which].y, 0x22); BltBitMapRastPort(&TBitMaps[board->next_which], 0, 0, board->rastport, board->next_off.x, board->next_off.y, TSizes[board->next_which].x, TSizes[board->next_which].y, 0xC0); if (!TestMove(board, board->piece.which, board->piece.bx, board->piece.by)) board->flags |= TETF_GAMEOVER | TETF_OUTOFROOM; } } void DrawLine(struct TBoard *board, WORD line) { int i, top, left, size = YSize * board->bitmap.BytesPerRow; BltClear(board->bitmap.Planes[0] + line * size, size, 0); top = line * YSize; for (left = 0, i = 0x4000; i & 0x7fe0; i >>= 1, left += XSize) { if (board->level.board[line] & i) BltBitMap((struct BitMap *)&TBitMaps[19], 0, 0, &board->bitmap, left, top, XSize, YSize, 0xC0, 0xff, NULL); } } /* called every timer event, (every 1/10th of a second) */ void UpdateTetris(struct TBoard *board) { int i; char buf[12]; if ((board) && (board->flags & (TETF_GAMEOVER | TETF_NEXTLEVEL))) return; board->next_drop--; if (board->next_drop < 0) { board->next_drop = board->drop; MoveTetris(board, COMMAND_DOWN); } board->next_sec--; if (board->next_sec < 0) { board->next_sec = UPDATE_SECONDS; board->time--; sprintf(buf, "Time:%2d:%02d", board->time / 60, board->time % 60); Move(board->rastport, board->time_off.x, board->time_off.y); Text(board->rastport, buf, 10); if (board->time <= 0) { if (board->level.goal & GOAL_TIME_LIMIT) board->flags |= TETF_GAMEOVER | TETF_OUTOFTIME; else if (board->level.goal & GOAL_ENDURANCE) board->flags |= TETF_NEXTLEVEL; } board->next_line_up--; if (board->next_line_up < 0) { board->next_line_up = board->level.line_up; for (i = 0; i < PFHEIGHT; i++) board->level.board[i] = board->level.board[i + 1]; board->level.board[PFHEIGHT - 1] &= ~(0x20 << (lrand48() % 10)); /* move piece up */ if (board->piece.by) { board->piece.by--; board->piece.y -= YSize; } else { /* Erase piece */ BltBitMap(&TBitMaps[board->piece.which], 0, 0, &board->bitmap, board->piece.x, board->piece.y, TSizes[board->piece.which].x, TSizes[board->piece.which].y, 0x22, 0xff, NULL); /* Redraw piece down one so that its in the right place when the board is scrolled up */ BltBitMap(&TBitMaps[board->piece.which], 0, 0, &board->bitmap, board->piece.x, board->piece.y + YSize, TSizes[board->piece.which].x, TSizes[board->piece.which].y, 0xEE, 0xff, NULL); } /* scroll board up */ BltBitMap(&board->bitmap, 0, YSize, &board->bitmap, 0, 0, PWidth, PHeight - YSize, 0xC0, 0xff, NULL); /* draw the new line */ DrawLine(board, PFHEIGHT - 1); /* update entire board to screen */ BltBitMapRastPort(&board->bitmap, 0, 0, board->rastport, board->board_off.x, board->board_off.y, PWidth, PHeight, 0xC0); } board->next_drop_up--; if (board->next_drop_up < 0) { board->next_drop_up = board->drop_up; if (board->drop) board->drop--; } } } void DrawBoard(struct TBoard *board) { int len; char buf[12]; SetAPen(board->rastport, 3); RectFill(board->rastport, board->board_off.x - BSize, board->board_off.y, board->board_off.x, board->board_off.y + PHeight + BSize - 1); RectFill(board->rastport, board->board_off.x - 1, board->board_off.y + PHeight, board->board_off.x + PWidth, board->board_off.y + PHeight + BSize - 1); RectFill(board->rastport, board->board_off.x + PWidth, board->board_off.y, board->board_off.x + PWidth + BSize - 1, board->board_off.y + PHeight + BSize - 1); SetAPen(board->rastport, 2); Move(board->rastport, board->board_off.x - 1, board->board_off.y); Draw(board->rastport, board->board_off.x - BSize, board->board_off.y); Draw(board->rastport, board->board_off.x - BSize, board->board_off.y + PHeight + BSize - 1); SetAPen(board->rastport, 1); Move(board->rastport, board->board_off.x - BSize + 1, board->board_off.y + PHeight + BSize - 1); Draw(board->rastport, board->board_off.x + PWidth + BSize - 1, board->board_off.y + PHeight + BSize - 1); Draw(board->rastport, board->board_off.x + PWidth + BSize - 1, board->board_off.y); SetAPen(board->rastport, 2); Draw(board->rastport, board->board_off.x + PWidth, board->board_off.y); Draw(board->rastport, board->board_off.x + PWidth, board->board_off.y + PHeight); Draw(board->rastport, board->board_off.x - 1, board->board_off.y + PHeight); SetAPen(board->rastport, 1); Draw(board->rastport, board->board_off.x - 1, board->board_off.y + 1); BltBitMap(&TBitMaps[board->piece.which], 0, 0, &board->bitmap, board->piece.x, board->piece.y, TSizes[board->piece.which].x, TSizes[board->piece.which].y, 0xEE, 0xff, NULL); BltBitMapRastPort(&board->bitmap, 0, 0, board->rastport, board->board_off.x, board->board_off.y, PWidth, PHeight, 0xC0); EraseRect(board->rastport, board->next_off.x, board->next_off.y, board->next_off.x + XSize * 4 - 1, board->next_off.y + YSize * 7 / 2 - 1); BltBitMapRastPort(&TBitMaps[board->next_which], 0, 0, board->rastport, board->next_off.x, board->next_off.y, TSizes[board->next_which].x, TSizes[board->next_which].y, 0xC0); len = sprintf(buf, "Score:%4d", board->points); Move(board->rastport, board->score_off.x, board->score_off.y); Text(board->rastport, buf, len); len = sprintf(buf, "Lines:%4d", board->lines); Move(board->rastport, board->line_off.x, board->line_off.y); Text(board->rastport, buf, len); len = sprintf(buf, "Time:%2d:%02d", board->time / 60, board->time % 60); Move(board->rastport, board->time_off.x, board->time_off.y); Text(board->rastport, buf, len); } void StartLevel(struct TBoard *board, WORD l) { int i; char filename[32]; BPTR file; if ((board) && (board->flags & TETF_GAMEOVER)) return; BltClear(board->bitmap.Planes[0], board->bitmap.Rows * board->bitmap.BytesPerRow, 0); sprintf(filename, "tetris:levels/%d", l); file = Open(filename, MODE_OLDFILE); if (file) { Read(file, &board->level, sizeof(struct TLevel)); Close(file); for (i = 0; i < PFHEIGHT; i++) { DrawLine(board, i); } } else { board->level.goal = GOAL_TIME_LIMIT | GOAL_CLEAR_LINES; board->level.lines = 5 + l * 5; board->level.time = 60 + l * 30; board->level.line_up = 45 - l * 5 / 2; for (i = 0; i < PFHEIGHT; i++) { board->level.board[i] = 0x801f; } board->level.board[i] = 0xffff; } board->flags &= ~TETF_NEXTLEVEL; board->lines = 0; board->next_which = TFirst[nrand48(board->seed) % 7]; board->piece.which = TFirst[nrand48(board->seed) % 7]; board->piece.bx = (PFWIDTH / 2) - 2; board->piece.x = board->piece.bx * XSize; board->piece.by = 0; board->piece.y = 0; // board->drop_up -= 1; // board->drop = board->drop_up; board->next_drop = board->drop; board->next_sec = UPDATE_SECONDS; board->lines = board->level.lines; board->time = board->level.time; board->next_line_up = board->level.line_up; board->next_drop_up = board->drop_up; board->clevel = l; DrawBoard(board); } /* void EndLevel(struct TBoard *board) { } */