X-Git-Url: http://git.shiar.nl/netris.git/blobdiff_plain/a3b41d71c27b89c5f3e82226316e031e3f8eb253..HEAD:/board.c diff --git a/board.c b/board.c index d1cb66b..71ad1dc 100644 --- a/board.c +++ b/board.c @@ -15,233 +15,455 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id: board.c,v 1.15 1999/05/16 06:56:24 mhw Exp $ */ #include "netris.h" #include -#ifdef DEBUG_FALLING -# define B_OLD -#else -# define B_OLD abs -#endif +#include "board.h" + +static const char shapes[7][4][4][4] = { + /* + * 4 rotations of 4x4 pixels per shape + * high nibble signifies joinage (left, right, top, bottom, from MSB) + * low nibble identifies block type (typically 2..8) + */ + + // J + { { { 0, 0, 0, 0000}, + {0x47, 0xC7, 0x97, 0000}, + { 0, 0, 0x27, 0000}, + {0000, 0000, 0000, 0000} }, + { { 0, 0x17, 0, 0000}, + { 0, 0x37, 0, 0000}, + {0x47, 0xA7, 0, 0000}, + {0000, 0000, 0000, 0000} }, + { {0x17, 0, 0, 0000}, + {0x67, 0xC7, 0x87, 0000}, + { 0, 0, 0, 0000}, + {0000, 0000, 0000, 0000} }, + { { 0, 0x57, 0x87, 0000}, + { 0, 0x37, 0, 0000}, + { 0, 0x27, 0, 0000}, + {0000, 0000, 0000, 0000} } }, + + // L + { { { 0, 0, 0, 0000}, + {0x53, 0xC3, 0x83, 0000}, + {0x23, 0, 0, 0000}, + {0000, 0000, 0000, 0000} }, + { {0x43, 0x93, 0, 0000}, + { 0, 0x33, 0, 0000}, + { 0, 0x23, 0, 0000}, + {0000, 0000, 0000, 0000} }, + { { 0, 0, 0x13, 0000}, + {0x43, 0xC3, 0xA3, 0000}, + { 0, 0, 0, 0000}, + {0000, 0000, 0000, 0000} }, + { { 0, 0x13, 0, 0000}, + { 0, 0x33, 0, 0000}, + { 0, 0x63, 0x83, 0000}, + {0000, 0000, 0000, 0000} } }, + + // T + { { { 0, 0, 0, 0000}, + {0x48, 0xD8, 0x88, 0000}, + { 0, 0x28, 0, 0000}, + {0000, 0000, 0000, 0000} }, + { { 0, 0x18, 0, 0000}, + {0x48, 0xB8, 0, 0000}, + { 0, 0x28, 0, 0000}, + {0000, 0000, 0000, 0000} }, + { { 0, 0x18, 0, 0000}, + {0x48, 0xE8, 0x88, 0000}, + { 0, 0, 0, 0000}, + {0000, 0000, 0000, 0000} }, + { { 0, 0x18, 0, 0000}, + { 0, 0x78, 0x88, 0000}, + { 0, 0x28, 0, 0000}, + {0000, 0000, 0000, 0000} } }, + + // S + { { { 0, 0, 0, 0000}, + { 0, 0x52, 0x82, 0000}, + {0x42, 0xA2, 0, 0000}, + {0000, 0000, 0000, 0000} }, + { {0x12, 0, 0, 0000}, + {0x62, 0x92, 0, 0000}, + { 0, 0x22, 0, 0000}, + {0000, 0000, 0000, 0000} }, + { { 0, 0, 0, 0000}, + { 0, 0x52, 0x82, 0000}, + {0x42, 0xA2, 0, 0000}, + {0000, 0000, 0000, 0000} }, + { {0x12, 0, 0, 0000}, + {0x62, 0x92, 0, 0000}, + { 0, 0x22, 0, 0000}, + {0000, 0000, 0000, 0000} } }, + + // Z + { { { 0, 0, 0, 0000}, + {0x46, 0x96, 0, 0000}, + { 0, 0x66, 0x86, 0000}, + {0000, 0000, 0000, 0000} }, + { { 0, 0x16, 0, 0000}, + {0x56, 0xA6, 0, 0000}, + {0x26, 0, 0, 0000}, + {0000, 0000, 0000, 0000} }, + { { 0, 0, 0, 0000}, + {0x46, 0x96, 0, 0000}, + { 0, 0x66, 0x86, 0000}, + {0000, 0000, 0000, 0000} }, + { { 0, 0x16, 0, 0000}, + {0x56, 0xA6, 0, 0000}, + {0x26, 0, 0, 0000}, + {0000, 0000, 0000, 0000} } }, + + // I + { { { 0, 0, 0, 0}, + {0x44, 0xC4, 0xC4, 0x84}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0} }, + { { 0, 0x14, 0, 0}, + { 0, 0x34, 0, 0}, + { 0, 0x34, 0, 0}, + { 0, 0x24, 0, 0} }, + { { 0, 0, 0, 0}, + {0x44, 0xC4, 0xC4, 0x84}, + { 0, 0, 0, 0}, + { 0, 0, 0, 0} }, + { { 0, 0x14, 0, 0}, + { 0, 0x34, 0, 0}, + { 0, 0x34, 0, 0}, + { 0, 0x24, 0, 0} } }, + + // O + { { { 0, 0, 0, 0000}, + { 0, 0x55, 0x95, 0000}, + { 0, 0x65, 0xA5, 0000}, + {0000, 0000, 0000, 0000} }, + { { 0, 0, 0, 0000}, + { 0, 0x55, 0x95, 0000}, + { 0, 0x65, 0xA5, 0000}, + {0000, 0000, 0000, 0000} }, + { { 0, 0, 0, 0000}, + { 0, 0x55, 0x95, 0000}, + { 0, 0x65, 0xA5, 0000}, + {0000, 0000, 0000, 0000} }, + { { 0, 0, 0, 0000}, + { 0, 0x55, 0x95, 0000}, + { 0, 0x65, 0xA5, 0000}, + {0000, 0000, 0000, 0000} } } +}; + +int shape_iterate(char s, int scr, int y, int x, ShapeDrawFunc func) +{ //Draw a certain shape using + int i, j, result; + char type, rotation; + + type = s / 4; + rotation = s & 3; + for (i = 0; i < 4; i++) + for (j = 0; j < 4; j++) + if (shapes[type][rotation][i][j]) + if (result = func(scr, y-i, x+j, shapes[type][rotation][i][j])) + return result; + return 0; +} -static BlockType board[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH]; -static BlockType oldBoard[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH]; -static unsigned int changed[MAX_SCREENS][MAX_BOARD_HEIGHT]; -static int falling[MAX_SCREENS][MAX_BOARD_WIDTH]; -static int oldFalling[MAX_SCREENS][MAX_BOARD_WIDTH]; -ExtFunc void InitBoard(int scr) -{ - boardHeight[scr] = MAX_BOARD_HEIGHT; - boardVisible[scr] = 20; - boardWidth[scr] = 10; - InitScreen(scr); +char ChooseOption(float options[7]) +{ //Return a random piece with given piece weight + int i; + float total = 0, val; + + for (i = 0; i < 7; i++) total += options[i]; + val = Random(0, 32767) / 32768.0 * total; + for (i = 0; i < 7; i++) if ((val -= options[i]) < 0) + return i << 2; + return 0; } -ExtFunc void CleanupBoard(int scr) -{ - CleanupScreen(scr); + +static unsigned char board[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH]; +static unsigned char oldBoard[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH]; +static unsigned int changed[MAX_SCREENS][MAX_BOARD_HEIGHT]; +static int shadowy; + +void player_empty(int scr) +{ //Empty the whole field (all blocks BT_none) + int y, x; + + for (y = Players[scr].boardHeight - 1; y >= 0; --y) + for (x = 0; x < Players[scr].boardWidth; ++x) { + oldBoard[scr][y][x] = board[scr][y][x] = BT_none; + } } -ExtFunc BlockType GetBlock(int scr, int y, int x) -{ - if (y < 0 || x < 0 || x >= boardWidth[scr]) +unsigned char block_get(int scr, int y, int x) +{ //Returns the block on field at position (x,y) + if (y < 0 || x < 0 || x >= Players[scr].boardWidth) return BT_wall; - else if (y >= boardHeight[scr]) + else if (y >= Players[scr].boardHeight) return BT_none; else - return abs(board[scr][y][x]); + return board[scr][y][x]; } -ExtFunc void SetBlock(int scr, int y, int x, BlockType type) +static void block_set(int scr, int y, int x, unsigned char type) { - if (y >= 0 && y < boardHeight[scr] && x >= 0 && x < boardWidth[scr]) { - if (y < boardVisible[scr]) - falling[scr][x] += (type < 0) - (board[scr][y][x] < 0); + if (y >= 0 && y < Players[scr].boardHeight + && x >= 0 && x < Players[scr].boardWidth) { board[scr][y][x] = type; changed[scr][y] |= 1 << x; } } -ExtFunc int RefreshBoard(int scr) -{ +int player_draw(int scr) +{ //draw changes to screen int y, x, any = 0; unsigned int c; - BlockType b; - - for (y = boardVisible[scr] - 1; y >= 0; --y) - if ((c = changed[scr][y])) { - if (robotEnable) { - RobotCmd(0, "RowUpdate %d %d", scr, y); - for (x = 0; x < boardWidth[scr]; ++x) { - b = board[scr][y][x]; - if (fairRobot) - b = abs(b); - RobotCmd(0, " %d", b); - } - RobotCmd(0, "\n"); - } - changed[scr][y] = 0; - any = 1; - for (x = 0; c; (c >>= 1), (++x)) - if ((c & 1) && B_OLD(board[scr][y][x])!=oldBoard[scr][y][x]) { - PlotBlock(scr, y, x, B_OLD(board[scr][y][x])); - oldBoard[scr][y][x] = B_OLD(board[scr][y][x]); + + for (y = Players[scr].boardVisible - 1; y >= 0; y--) + if ((c = changed[scr][y])) { // line changed + for (x = 0; c; (c >>= 1), x++) + if (c & 1 && board[scr][y][x] != oldBoard[scr][y][x]) { + block_draw_window(scr, y, x, board[scr][y][x]); + oldBoard[scr][y][x] = board[scr][y][x]; } - } - if (robotEnable) - RobotTimeStamp(); - for (x = 0; x < boardWidth[scr]; ++x) - if (oldFalling[scr][x] != !!falling[scr][x]) { - oldFalling[scr][x] = !!falling[scr][x]; - PlotUnderline(scr, x, oldFalling[scr][x]); + changed[scr][y] = 0; // reset any = 1; - } + } //changed row return any; } -ExtFunc int PlotFunc(int scr, int y, int x, BlockType type, void *data) +int block_iter_set_status(int scr, int y, int x, unsigned char type) { - SetBlock(scr, y, x, type); + block_draw_status(y, x, type); return 0; } -ExtFunc int EraseFunc(int scr, int y, int x, BlockType type, void *data) -{ - SetBlock(scr, y, x, BT_none); +static int block_iter_shadow(int scr, int y, int x, unsigned char type) +{ //draw shadow + block_set(scr, y, x, BT_shadow); return 0; } -ExtFunc int CollisionFunc(int scr, int y, int x, BlockType type, void *data) +static int block_iter_set(int scr, int y, int x, unsigned char type) { - return GetBlock(scr, y, x) != BT_none; + block_set(scr, y, x, type); + return 0; } -ExtFunc int VisibleFunc(int scr, int y, int x, BlockType type, void *data) -{ - return (y >= 0 && y < boardVisible[scr] && x >= 0 && x < boardWidth[scr]); +void shape_draw(char shape, int scr, int y, int x, int shadow) +{ //put shape on field + if (shadow) { + for (shadowy = y - 1; shadowy >= 0; shadowy--) + if (!shape_get(shape, scr, shadowy, x)) + break; + shape_iterate(shape, scr, shadowy + 1, x, block_iter_shadow); + } //draw shadow + shape_iterate(shape, scr, y, x, block_iter_set); } -ExtFunc void PlotShape(Shape *shape, int scr, int y, int x, int falling) +static int block_iter_erase(int scr, int y, int x, unsigned char type) { - ShapeIterate(shape, scr, y, x, falling, PlotFunc, NULL); + block_set(scr, y, x, BT_none); + return 0; } -ExtFunc void EraseShape(Shape *shape, int scr, int y, int x) -{ - ShapeIterate(shape, scr, y, x, 0, EraseFunc, NULL); +void shape_erase(char shape, int scr, int y, int x, int shadow) +{ //remove block from field + shape_iterate(shape, scr, y, x, block_iter_erase); + if (shadow && scr == me) // draw shadow + shape_iterate(shape, scr, shadowy + 1, x, block_iter_erase); } -ExtFunc int ShapeFits(Shape *shape, int scr, int y, int x) +static int block_iter_get(int scr, int y, int x, unsigned char type) { - return !ShapeIterate(shape, scr, y, x, 0, CollisionFunc, NULL); + return block_get(scr, y, x) > BT_none; +} + +int shape_get(char shape, int scr, int y, int x) +{ //check if there's nothing in the way + return !shape_iterate(shape, scr, y, x, block_iter_get); } -ExtFunc int ShapeVisible(Shape *shape, int scr, int y, int x) +static int block_iter_visible(int scr, int y, int x, unsigned char type) +{ + return (y >= 0 && y < Players[scr].boardVisible && + x >= 0 && x < Players[scr].boardWidth); +} +int shape_visible(char shape, int scr, int y, int x) { - return ShapeIterate(shape, scr, y, x, 0, VisibleFunc, NULL); + return shape_iterate(shape, scr, y, x, block_iter_visible); } -ExtFunc int MovePiece(int scr, int deltaY, int deltaX) +int player_move(int scr, int deltaY, int deltaX) { int result; - EraseShape(curShape[scr], scr, curY[scr], curX[scr]); - result = ShapeFits(curShape[scr], scr, curY[scr] + deltaY, - curX[scr] + deltaX); + shape_erase(Players[scr].curShape, scr, + Players[scr].curY, Players[scr].curX, 1); + result = shape_get(Players[scr].curShape, scr, Players[scr].curY + deltaY, + Players[scr].curX + deltaX); if (result) { - curY[scr] += deltaY; - curX[scr] += deltaX; + Players[scr].curY += deltaY; + Players[scr].curX += deltaX; } - PlotShape(curShape[scr], scr, curY[scr], curX[scr], 1); + shape_draw(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX, + scr == me); return result; } -ExtFunc int RotatePiece(int scr) +int player_rotate(int scr, int dir) { + char newshape; int result; - EraseShape(curShape[scr], scr, curY[scr], curX[scr]); - result = ShapeFits(curShape[scr]->rotateTo, scr, curY[scr], curX[scr]); - if (result) - curShape[scr] = curShape[scr]->rotateTo; - PlotShape(curShape[scr], scr, curY[scr], curX[scr], 1); + shape_erase(Players[scr].curShape, scr, Players[scr].curY, + Players[scr].curX, 1); + /* (inc|dec)rement only 3 least significant bits which indicate rotation */ + newshape = (Players[scr].curShape & 252) + (((Players[scr].curShape & 3) + dir) & 3); + result = shape_get(newshape, scr, Players[scr].curY, Players[scr].curX); + if (!result) { + // move if it doesn't fit anymore + short int slideX; + for (slideX = 0; slideX < 2; slideX = -slideX) { + // slide left and right + if (slideX >= 0) slideX++; // slide more + if (result = shape_get(newshape, scr, Players[scr].curY, + Players[scr].curX+slideX)) break; + } + if (result) Players[scr].curX += slideX; + } + if (result) Players[scr].curShape = newshape; + shape_draw(Players[scr].curShape, scr, + Players[scr].curY, Players[scr].curX, scr == me); return result; } -ExtFunc int DropPiece(int scr) +int player_drop(int scr) { int count = 0; - EraseShape(curShape[scr], scr, curY[scr], curX[scr]); - while (ShapeFits(curShape[scr], scr, curY[scr] - 1, curX[scr])) { - --curY[scr]; - ++count; + shape_erase(Players[scr].curShape, scr, + Players[scr].curY, Players[scr].curX, 1); + while (shape_get(Players[scr].curShape, scr, + Players[scr].curY - 1, Players[scr].curX)) { + Players[scr].curY--; + count++; } - PlotShape(curShape[scr], scr, curY[scr], curX[scr], 1); + shape_draw(Players[scr].curShape, scr, + Players[scr].curY, Players[scr].curX, 0); return count; } -ExtFunc int LineIsFull(int scr, int y) -{ +static int block_free(int scr, int x, int y, unsigned char z) +{ //Check if blocks are empty below block (x,y) and sticking to (x,y) mask + unsigned char curblock; + + if (y == 0) return 0; // at bottom + curblock = block_get(scr, y, x) & z; + if (curblock & 0x10 && !block_free(scr, x, y-1, z & 0xD0)) return 0; + if (curblock & 0x20 && !block_free(scr, x, y+1, z & 0xE0)) return 0; + if (curblock & 0x40 && !block_free(scr, x+1, y, z & 0x70)) return 0; + if (curblock & 0x80 && !block_free(scr, x-1, y, z & 0xB0)) return 0; + if ((z = block_get(scr, y-1, x)) & 0x20) return 1; // stuck to block below + if (z > BT_none) return 0; // some other piece below + return 1; // nothing below +} + +static int block_down(int scr, int x, int y, unsigned char z) +{ //Drop down block (x,y) and those sticking to it mask + if (block_get(scr, y, x) & z & 0x10) block_down(scr, x, y-1, z & 0xD0); + if (block_get(scr, y, x) & z & 0x20) block_down(scr, x, y+1, z & 0xE0); + if (block_get(scr, y, x) & z & 0x40) block_down(scr, x+1, y, z & 0x70); + if (block_get(scr, y, x) & z & 0x80) block_down(scr, x-1, y, z & 0xB0); + block_set(scr, y-1, x, block_get(scr, y, x)); + block_set(scr, y, x, BT_none); +} + +int player_down(int scr) +{ //Drop any free blocks on field + int xloop, x, x2, y, fallen = 0; + unsigned char z; + + if (!Game.gravity) return 0; + for (y = Players[scr].boardHeight - 1; y > 0; y--) + for (x = 0; x < Players[scr].boardWidth; x++) { + if ((z = block_get(scr, y, x)) > BT_none && (z & 0xA0) == 0) { + // block present which doesn't stick left/up => topleft block + if (block_free(scr, x, y, 0xF0)) { + block_down(scr, x, y, 0xF0); // move blocks down + fallen++; + } + } //block present + } + return fallen; +} + +static int player_linecheck(int scr, int y) +{ //return 0 if any blocks present on line int x; - for (x = 0; x < boardWidth[scr]; ++x) - if (GetBlock(scr, y, x) == BT_none) + for (x = 0; x < Players[scr].boardWidth; x++) + if (block_get(scr, y, x) <= BT_none) return 0; return 1; } -ExtFunc void CopyLine(int scr, int from, int to) -{ +static void player_linecopy(int scr, int from, int to) +{ //move blocks on line to line int x; if (from != to) - for (x = 0; x < boardWidth[scr]; ++x) - SetBlock(scr, to, x, GetBlock(scr, from, x)); + for (x = 0; x < Players[scr].boardWidth; ++x) + block_set(scr, to, x, block_get(scr, from, x)); } -ExtFunc int ClearFullLines(int scr) -{ - int from, to; +int player_lineclear(int scr) +{ //remove full lines, return lines cleared + int from, to, x, linescleared = 0; + do { from = to = 0; - while (to < boardHeight[scr]) { - while (LineIsFull(scr, from)) - ++from; - CopyLine(scr, from++, to++); + while (to < Players[scr].boardHeight) { + while (player_linecheck(scr, from)) { + from++; // skip + for (x = 0; x 1) + block_set(scr, from-2, x, block_get(scr, from-2, x) & 0xDF); + } + } + player_linecopy(scr, from++, to++); } - return from - to; + linescleared += from - to; + } while (player_down(scr)); + return linescleared; } -ExtFunc void FreezePiece(int scr) -{ +void player_lineadd(int scr, int color, int count, int column) +{ //add junklines with hole at to by team int y, x; - BlockType type; - - for (y = 0; y < boardHeight[scr]; ++y) - for (x = 0; x < boardWidth[scr]; ++x) - if ((type = board[scr][y][x]) < 0) - SetBlock(scr, y, x, -type); -} -ExtFunc void InsertJunk(int scr, int count, int column) -{ - int y, x; - - for (y = boardHeight[scr] - count - 1; y >= 0; --y) - CopyLine(scr, y, y + count); + shape_erase(Players[scr].curShape, scr, + Players[scr].curY, Players[scr].curX, 1); + for (y = Players[scr].boardHeight - count - 1; y >= 0; --y) + player_linecopy(scr, y, y + count); for (y = 0; y < count; ++y) - for (x = 0; x < boardWidth[scr]; ++x) - SetBlock(scr, y, x, (x == column) ? BT_none : BT_white); - curY[scr] += count; + for (x = 0; x < Players[scr].boardWidth; ++x) + block_set(scr, y, x, x == column ? BT_none : color + 1 + + 0x40 * (x != column-1 && x < Players[scr].boardWidth-1) + + 0x80 * (x != column+1 && x > 0)); + Players[scr].curY += count; // move piece up.. + for (y = 0; y < count; ++y) + if (shape_get(Players[scr].curShape, scr, Players[scr].curY - 1, Players[scr].curX)) + Players[scr].curY--; // ...and down again as far as possible + else break; + shape_draw(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX, + scr == me); } -/* - * vi: ts=4 ai - * vim: noai si - */