2 * Netris -- A free networked version of T*tris
3 * Copyright (C) 1994-1996,1999 Mark H. Weaver <mhw@netris.org>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 static const char shapes[7][4][4][4] = {
27 * 4 rotations of 4x4 pixels per shape
28 * high nibble signifies joinage (left, right, top, bottom, from MSB)
29 * low nibble identifies block type (typically 2..8)
32 { { {0x00, 0x00, 0x00, 0x00}, {0x47, 0xC7, 0x97, 0x00},
33 {0x00, 0x00, 0x27, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // sharp horizontal
34 { {0x00, 0x17, 0x00, 0x00}, {0x00, 0x37, 0x00, 0x00},
35 {0x47, 0xA7, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // blunt vertical
36 { {0x17, 0x00, 0x00, 0x00}, {0x67, 0xC7, 0x87, 0x00},
37 {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // blunt horizontal
38 { {0x00, 0x57, 0x87, 0x00}, {0x00, 0x37, 0x00, 0x00},
39 {0x00, 0x27, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, // J (yellow)
41 { { {0x00, 0x00, 0x00, 0x00}, {0x53, 0xC3, 0x83, 0x00},
42 {0x23, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // sharp horizontal
43 { {0x43, 0x93, 0x00, 0x00}, {0x00, 0x33, 0x00, 0x00},
44 {0x00, 0x23, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // sharp vertical
45 { {0x00, 0x00, 0x13, 0x00}, {0x43, 0xC3, 0xA3, 0x00},
46 {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // blunt horizontal
47 { {0x00, 0x13, 0x00, 0x00}, {0x00, 0x33, 0x00, 0x00},
48 {0x00, 0x63, 0x83, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, // L (cyan)
50 { { {0x00, 0x00, 0x00, 0x00}, {0x48, 0xD8, 0x88, 0x00},
51 {0x00, 0x28, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // pointing down
52 { {0x00, 0x18, 0x00, 0x00}, {0x48, 0xB8, 0x00, 0x00},
53 {0x00, 0x28, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // pointing left
54 { {0x00, 0x18, 0x00, 0x00}, {0x48, 0xE8, 0x88, 0x00},
55 {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // pointing up
56 { {0x00, 0x18, 0x00, 0x00}, {0x00, 0x78, 0x88, 0x00},
57 {0x00, 0x28, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, // T (white)
59 { { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x52, 0x82, 0x00},
60 {0x42, 0xA2, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // lieing
61 { {0x12, 0x00, 0x00, 0x00}, {0x62, 0x92, 0x00, 0x00},
62 {0x00, 0x22, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // standing
63 { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x52, 0x82, 0x00},
64 {0x42, 0xA2, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // repeats
65 { {0x12, 0x00, 0x00, 0x00}, {0x62, 0x92, 0x00, 0x00},
66 {0x00, 0x22, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, // S (green)
68 { { {0x00, 0x00, 0x00, 0x00}, {0x46, 0x96, 0x00, 0x00},
69 {0x00, 0x66, 0x86, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // lieing
70 { {0x00, 0x16, 0x00, 0x00}, {0x56, 0xA6, 0x00, 0x00},
71 {0x26, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // standing
72 { {0x00, 0x00, 0x00, 0x00}, {0x46, 0x96, 0x00, 0x00},
73 {0x00, 0x66, 0x86, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // repeats
74 { {0x00, 0x16, 0x00, 0x00}, {0x56, 0xA6, 0x00, 0x00},
75 {0x26, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, // Z (red)
77 { { {0x00, 0x00, 0x00, 0x00}, {0x44, 0xC4, 0xC4, 0x84},
78 {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // lieing
79 { {0x00, 0x14, 0x00, 0x00}, {0x00, 0x34, 0x00, 0x00},
80 {0x00, 0x34, 0x00, 0x00}, {0x00, 0x24, 0x00, 0x00} }, // standing
81 { {0x00, 0x00, 0x00, 0x00}, {0x44, 0xC4, 0xC4, 0x84},
82 {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // repeats
83 { {0x00, 0x14, 0x00, 0x00}, {0x00, 0x34, 0x00, 0x00},
84 {0x00, 0x34, 0x00, 0x00}, {0x00, 0x24, 0x00, 0x00} } }, // I, stick (blue)
86 { { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
87 {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} },
88 { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
89 {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} }, // repeats
90 { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
91 {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} },
92 { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
93 {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} } } // O, square (purple)
96 int ShapeIterate(char s, int scr, int y, int x, ShapeDrawFunc func)
97 { //Draw a certain shape using <ShapeDrawFunc>
103 for (i = 0; i < 4; i++)
104 for (j = 0; j < 4; j++)
105 if (shapes[type][rotation][i][j])
106 if (result = func(scr, y-i, x+j, shapes[type][rotation][i][j]))
112 float stdOptions[7] = {1, 1, 1, 1, 1, 1, 1}; //stdOptions
114 char ChooseOption(float options[7])
115 { //Return a random piece with given piece weight
117 float total = 0, val;
119 for (i = 0; i < 7; i++) total += options[i];
120 val = Random(0, 32767) / 32768.0 * total;
121 for (i = 0; i < 7; i++) if ((val -= options[i]) < 0)
127 static unsigned char board[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
128 static unsigned char oldBoard[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
129 static unsigned int changed[MAX_SCREENS][MAX_BOARD_HEIGHT];
132 void ClearField(int scr)
133 { //Empty the whole field (all blocks BT_none)
136 for (y = Players[scr].boardHeight - 1; y >= 0; --y)
137 for (x = 0; x < Players[scr].boardWidth; ++x) {
138 oldBoard[scr][y][x] = board[scr][y][x] = BT_none;
142 unsigned char GetBlock(int scr, int y, int x)
143 { //Returns the block on field at position (x,y)
144 if (y < 0 || x < 0 || x >= Players[scr].boardWidth)
146 else if (y >= Players[scr].boardHeight)
149 return board[scr][y][x];
152 void SetBlock(int scr, int y, int x, unsigned char type)
154 if (y >= 0 && y < Players[scr].boardHeight
155 && x >= 0 && x < Players[scr].boardWidth) {
156 board[scr][y][x] = type;
157 changed[scr][y] |= 1 << x;
161 int RefreshBoard(int scr)
162 { //draw changes to screen
166 for (y = Players[scr].boardVisible - 1; y >= 0; y--)
167 if ((c = changed[scr][y])) { // line changed
168 for (x = 0; c; (c >>= 1), x++)
169 if (c & 1 && board[scr][y][x] != oldBoard[scr][y][x]) {
170 PlotBlock(scr, y, x, board[scr][y][x]);
171 oldBoard[scr][y][x] = board[scr][y][x];
173 changed[scr][y] = 0; // reset
179 int GlanceFunc(int scr, int y, int x, unsigned char type)
181 PlotBlockXY(y, x, type);
185 int ShadowFunc(int scr, int y, int x, unsigned char type)
187 SetBlock(scr, y, x, BT_shadow);
191 int PlotFunc(int scr, int y, int x, unsigned char type)
193 SetBlock(scr, y, x, type);
196 void PlotShape(char shape, int scr, int y, int x, int shadow)
197 { //put shape on field
199 for (shadowy = y - 1; shadowy >= 0; shadowy--)
200 if (!ShapeFits(shape, scr, shadowy, x))
202 ShapeIterate(shape, scr, shadowy + 1, x, ShadowFunc);
204 ShapeIterate(shape, scr, y, x, PlotFunc);
207 int EraseFunc(int scr, int y, int x, unsigned char type)
209 SetBlock(scr, y, x, BT_none);
212 void EraseShape(char shape, int scr, int y, int x, int shadow)
213 { //remove block from field
214 ShapeIterate(shape, scr, y, x, EraseFunc);
215 if (shadow && scr == me) // draw shadow
216 ShapeIterate(shape, scr, shadowy + 1, x, EraseFunc);
219 int CollisionFunc(int scr, int y, int x, unsigned char type)
221 return GetBlock(scr, y, x) > BT_none;
223 int ShapeFits(char shape, int scr, int y, int x)
224 { //check if there's nothing in the way
225 return !ShapeIterate(shape, scr, y, x, CollisionFunc);
228 int VisibleFunc(int scr, int y, int x, unsigned char type)
230 return (y >= 0 && y < Players[scr].boardVisible &&
231 x >= 0 && x < Players[scr].boardWidth);
233 int ShapeVisible(char shape, int scr, int y, int x)
235 return ShapeIterate(shape, scr, y, x, VisibleFunc);
238 int MovePiece(int scr, int deltaY, int deltaX)
242 EraseShape(Players[scr].curShape, scr,
243 Players[scr].curY, Players[scr].curX, 1);
244 result = ShapeFits(Players[scr].curShape, scr, Players[scr].curY + deltaY,
245 Players[scr].curX + deltaX);
247 Players[scr].curY += deltaY;
248 Players[scr].curX += deltaX;
250 PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,
255 int RotatePiece(int scr, int dir)
260 EraseShape(Players[scr].curShape, scr, Players[scr].curY,
261 Players[scr].curX, 1);
262 /* (inc|dec)rement only 3 least significant bits which indicate rotation */
263 newshape = (Players[scr].curShape & 252) + (((Players[scr].curShape & 3) + dir) & 3);
264 result = ShapeFits(newshape, scr, Players[scr].curY, Players[scr].curX);
266 // move if it doesn't fit anymore
268 for (slideX = 0; slideX < 2; slideX = -slideX) {
269 // slide left and right
270 if (slideX >= 0) slideX++; // slide more
271 if (result = ShapeFits(newshape, scr, Players[scr].curY,
272 Players[scr].curX+slideX)) break;
274 if (result) Players[scr].curX += slideX;
276 if (result) Players[scr].curShape = newshape;
277 PlotShape(Players[scr].curShape, scr,
278 Players[scr].curY, Players[scr].curX, scr == me);
282 int DropPiece(int scr)
286 EraseShape(Players[scr].curShape, scr,
287 Players[scr].curY, Players[scr].curX, 1);
288 while (ShapeFits(Players[scr].curShape, scr,
289 Players[scr].curY - 1, Players[scr].curX)) {
293 PlotShape(Players[scr].curShape, scr,
294 Players[scr].curY, Players[scr].curX, 0);
298 int BlockFree(int scr, int x, int y, unsigned char z)
299 { //Check if blocks are empty below block (x,y) and sticking to (x,y) mask <z>
300 unsigned char curblock;
302 if (y == 0) return 0; // at bottom
303 curblock = GetBlock(scr, y, x) & z;
304 if (curblock & 0x10 && !BlockFree(scr, x, y-1, z & 0xD0)) return 0;
305 if (curblock & 0x20 && !BlockFree(scr, x, y+1, z & 0xE0)) return 0;
306 if (curblock & 0x40 && !BlockFree(scr, x+1, y, z & 0x70)) return 0;
307 if (curblock & 0x80 && !BlockFree(scr, x-1, y, z & 0xB0)) return 0;
308 if ((z = GetBlock(scr, y-1, x)) & 0x20) return 1; // stuck to block below
309 if (z > BT_none) return 0; // some other piece below
310 return 1; // nothing below
313 int BlockFall(int scr, int x, int y, unsigned char z)
314 { //Drop down block (x,y) and those sticking to it mask <z>
315 if (GetBlock(scr, y, x) & z & 0x10) BlockFall(scr, x, y-1, z & 0xD0);
316 if (GetBlock(scr, y, x) & z & 0x20) BlockFall(scr, x, y+1, z & 0xE0);
317 if (GetBlock(scr, y, x) & z & 0x40) BlockFall(scr, x+1, y, z & 0x70);
318 if (GetBlock(scr, y, x) & z & 0x80) BlockFall(scr, x-1, y, z & 0xB0);
319 SetBlock(scr, y-1, x, GetBlock(scr, y, x));
320 SetBlock(scr, y, x, BT_none);
323 int CheckFall(int scr)
324 { //Drop any free blocks on field
325 int xloop, x, x2, y, fallen = 0;
328 if (!Game.gravity) return 0;
329 for (y = Players[scr].boardHeight - 1; y > 0; y--)
330 for (x = 0; x < Players[scr].boardWidth; x++) {
331 if ((z = GetBlock(scr, y, x)) > BT_none && (z & 0xA0) == 0) {
332 // block present which doesn't stick left/up => topleft block
333 if (BlockFree(scr, x, y, 0xF0)) {
334 BlockFall(scr, x, y, 0xF0); // move blocks down
342 int LineIsFull(int scr, int y)
343 { //return 0 if any blocks present on line <y>
346 for (x = 0; x < Players[scr].boardWidth; x++)
347 if (GetBlock(scr, y, x) <= BT_none)
352 void CopyLine(int scr, int from, int to)
353 { //move blocks on line <from> to line <to>
357 for (x = 0; x < Players[scr].boardWidth; ++x)
358 SetBlock(scr, to, x, GetBlock(scr, from, x));
361 int ClearFullLines(int scr)
362 { //remove full lines, return lines cleared
363 int from, to, x, linescleared = 0;
367 while (to < Players[scr].boardHeight) {
368 while (LineIsFull(scr, from)) {
370 for (x = 0; x<Players[scr].boardWidth; x++) {
371 // don't stick blocks to line which we'll remove
372 SetBlock(scr, from, x, GetBlock(scr, from, x) & 0xEF);
374 SetBlock(scr, from-2, x, GetBlock(scr, from-2, x) & 0xDF);
377 CopyLine(scr, from++, to++);
379 linescleared += from - to;
380 } while (CheckFall(scr));
384 void InsertJunk(int scr, int color, int count, int column)
385 { //add <count> junklines with hole at <column> to <scr> by team <color>
388 EraseShape(Players[scr].curShape, scr,
389 Players[scr].curY, Players[scr].curX, 1);
390 for (y = Players[scr].boardHeight - count - 1; y >= 0; --y)
391 CopyLine(scr, y, y + count);
392 for (y = 0; y < count; ++y)
393 for (x = 0; x < Players[scr].boardWidth; ++x)
394 SetBlock(scr, y, x, x == column ? BT_none : color + 1
395 + 0x40 * (x != column-1 && x < Players[scr].boardWidth-1)
396 + 0x80 * (x != column+1 && x > 0));
397 Players[scr].curY += count; // move piece up..
398 for (y = 0; y < count; ++y)
399 if (ShapeFits(Players[scr].curShape, scr, Players[scr].curY - 1, Players[scr].curX))
400 Players[scr].curY--; // ...and down again as far as possible
402 PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,