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] = {
26 { { {0x00, 0x00, 0x00, 0x00}, {0x47, 0xC7, 0x97, 0x00},
27 {0x00, 0x00, 0x27, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //sharp horizontal
28 { {0x00, 0x17, 0x00, 0x00}, {0x00, 0x37, 0x00, 0x00},
29 {0x47, 0xA7, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //blunt vertical J
30 { {0x17, 0x00, 0x00, 0x00}, {0x67, 0xC7, 0x87, 0x00},
31 {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //blunt horizontal
32 { {0x00, 0x57, 0x87, 0x00}, {0x00, 0x37, 0x00, 0x00},
33 {0x00, 0x27, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, //J (yellow)
35 { { {0x00, 0x00, 0x00, 0x00}, {0x53, 0xC3, 0x83, 0x00},
36 {0x23, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //sharp horizontal
37 { {0x43, 0x93, 0x00, 0x00}, {0x00, 0x33, 0x00, 0x00},
38 {0x00, 0x23, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //sharp vertical
39 { {0x00, 0x00, 0x13, 0x00}, {0x43, 0xC3, 0xA3, 0x00},
40 {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //blunt horizontal
41 { {0x00, 0x13, 0x00, 0x00}, {0x00, 0x33, 0x00, 0x00},
42 {0x00, 0x63, 0x83, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, //L (cyan)
44 { { {0x00, 0x00, 0x00, 0x00}, {0x48, 0xD8, 0x88, 0x00},
45 {0x00, 0x28, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //pointing down
46 { {0x00, 0x18, 0x00, 0x00}, {0x48, 0xB8, 0x00, 0x00},
47 {0x00, 0x28, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //pointing left
48 { {0x00, 0x18, 0x00, 0x00}, {0x48, 0xE8, 0x88, 0x00},
49 {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //pointing up
50 { {0x00, 0x18, 0x00, 0x00}, {0x00, 0x78, 0x88, 0x00},
51 {0x00, 0x28, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, //T (white)
53 { { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x52, 0x82, 0x00},
54 {0x42, 0xA2, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} },
55 { {0x12, 0x00, 0x00, 0x00}, {0x62, 0x92, 0x00, 0x00},
56 {0x00, 0x22, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} },
57 { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x52, 0x82, 0x00},
58 {0x42, 0xA2, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //rep
59 { {0x12, 0x00, 0x00, 0x00}, {0x62, 0x92, 0x00, 0x00},
60 {0x00, 0x22, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, //S (green)
62 { { {0x00, 0x00, 0x00, 0x00}, {0x46, 0x96, 0x00, 0x00},
63 {0x00, 0x66, 0x86, 0x00}, {0x00, 0x00, 0x00, 0x00} },
64 { {0x00, 0x16, 0x00, 0x00}, {0x56, 0xA6, 0x00, 0x00},
65 {0x26, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} },
66 { {0x00, 0x00, 0x00, 0x00}, {0x46, 0x96, 0x00, 0x00},
67 {0x00, 0x66, 0x86, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //rep
68 { {0x00, 0x16, 0x00, 0x00}, {0x56, 0xA6, 0x00, 0x00},
69 {0x26, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, //Z (red)
71 { { {0x00, 0x00, 0x00, 0x00}, {0x44, 0xC4, 0xC4, 0x84},
72 {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //lieing
73 { {0x00, 0x14, 0x00, 0x00}, {0x00, 0x34, 0x00, 0x00},
74 {0x00, 0x34, 0x00, 0x00}, {0x00, 0x24, 0x00, 0x00} }, //standing
75 { {0x00, 0x00, 0x00, 0x00}, {0x44, 0xC4, 0xC4, 0x84},
76 {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //rep
77 { {0x00, 0x14, 0x00, 0x00}, {0x00, 0x34, 0x00, 0x00},
78 {0x00, 0x34, 0x00, 0x00}, {0x00, 0x24, 0x00, 0x00} } }, //stick (blue)
80 { { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
81 {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} },
82 { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
83 {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //rep
84 { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
85 {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //rep
86 { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
87 {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} } } //square (purple)
90 int ShapeIterate(char s, int scr, int y, int x, ShapeDrawFunc func)
91 { //Draw a certain shape using <ShapeDrawFunc>
97 for (i = 0; i < 4; i++)
98 for (j = 0; j < 4; j++)
99 if (shapes[type][rotation][i][j])
100 if (result = func(scr, y-i, x+j, shapes[type][rotation][i][j]))
106 float stdOptions[7] = {1, 1, 1, 1, 1, 1, 1}; //stdOptions
108 char ChooseOption(float options[7])
109 { //Return a random piece with given piece weight
111 float total = 0, val;
113 for (i = 0; i < 7; i++) total += options[i];
114 val = Random(0, 32767) / 32768.0 * total;
115 for (i = 0; i < 7; i++) if ((val -= options[i]) < 0)
121 static unsigned char board[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
122 static unsigned char oldBoard[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
123 static unsigned int changed[MAX_SCREENS][MAX_BOARD_HEIGHT];
126 void ClearField(int scr)
127 { //Empty the whole field (all blocks BT_none)
130 for (y = Players[scr].boardHeight - 1; y >= 0; --y)
131 for (x = 0; x < Players[scr].boardWidth; ++x) {
132 oldBoard[scr][y][x] = board[scr][y][x] = BT_none;
136 unsigned char GetBlock(int scr, int y, int x)
137 { //Returns the block on field at position (x,y)
138 if (y < 0 || x < 0 || x >= Players[scr].boardWidth)
140 else if (y >= Players[scr].boardHeight)
143 return board[scr][y][x];
146 void SetBlock(int scr, int y, int x, unsigned char type)
148 if (y >= 0 && y < Players[scr].boardHeight
149 && x >= 0 && x < Players[scr].boardWidth) {
150 board[scr][y][x] = type;
151 changed[scr][y] |= 1 << x;
155 int RefreshBoard(int scr)
156 { //draw changes to screen
160 for (y = Players[scr].boardVisible - 1; y >= 0; y--)
161 if ((c = changed[scr][y])) { //line changed
162 for (x = 0; c; (c >>= 1), x++)
163 if (c & 1 && board[scr][y][x] != oldBoard[scr][y][x]) {
164 PlotBlock(scr, y, x, board[scr][y][x]);
165 oldBoard[scr][y][x] = board[scr][y][x];
167 changed[scr][y] = 0; //reset
173 int GlanceFunc(int scr, int y, int x, unsigned char type)
175 PlotBlockXY(y, x, type);
179 int ShadowFunc(int scr, int y, int x, unsigned char type)
181 SetBlock(scr, y, x, BT_shadow);
185 int PlotFunc(int scr, int y, int x, unsigned char type)
187 SetBlock(scr, y, x, type);
190 void PlotShape(char shape, int scr, int y, int x, int shadow)
191 { //put shape on field
193 for (shadowy = y - 1; shadowy >= 0; shadowy--)
194 if (!ShapeFits(shape, scr, shadowy, x))
196 ShapeIterate(shape, scr, shadowy + 1, x, ShadowFunc);
198 ShapeIterate(shape, scr, y, x, PlotFunc);
201 int EraseFunc(int scr, int y, int x, unsigned char type)
203 SetBlock(scr, y, x, BT_none);
206 void EraseShape(char shape, int scr, int y, int x, int shadow)
207 { //remove block from field
208 ShapeIterate(shape, scr, y, x, EraseFunc);
209 if (shadow && scr == me) //draw shadow
210 ShapeIterate(shape, scr, shadowy + 1, x, EraseFunc);
213 int CollisionFunc(int scr, int y, int x, unsigned char type)
215 return GetBlock(scr, y, x) > BT_none;
217 int ShapeFits(char shape, int scr, int y, int x)
218 { //check if there's nothing in the way
219 return !ShapeIterate(shape, scr, y, x, CollisionFunc);
222 int VisibleFunc(int scr, int y, int x, unsigned char type)
224 return (y >= 0 && y < Players[scr].boardVisible &&
225 x >= 0 && x < Players[scr].boardWidth);
227 int ShapeVisible(char shape, int scr, int y, int x)
229 return ShapeIterate(shape, scr, y, x, VisibleFunc);
232 int MovePiece(int scr, int deltaY, int deltaX)
236 EraseShape(Players[scr].curShape, scr,
237 Players[scr].curY, Players[scr].curX, 1);
238 result = ShapeFits(Players[scr].curShape, scr, Players[scr].curY + deltaY,
239 Players[scr].curX + deltaX);
241 Players[scr].curY += deltaY;
242 Players[scr].curX += deltaX;
244 PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,
249 int RotatePiece(int scr, int dir)
254 EraseShape(Players[scr].curShape, scr, Players[scr].curY,
255 Players[scr].curX, 1);
256 /* (inc|dec)rement only 3 least significant bits which indicate rotation */
257 newshape = (Players[scr].curShape & 252) + (((Players[scr].curShape & 3) + dir) & 3);
258 result = ShapeFits(newshape, scr, Players[scr].curY, Players[scr].curX);
261 for (slideX = 0; slideX < 2; slideX = -slideX) {
262 if (slideX >= 0) slideX++; //slide more
263 if (result = ShapeFits(newshape, scr, Players[scr].curY,
264 Players[scr].curX+slideX)) break;
265 } //slide left and right
266 if (result) Players[scr].curX += slideX;
267 } //try to fit if it doesn't
268 if (result) Players[scr].curShape = newshape;
269 PlotShape(Players[scr].curShape, scr,
270 Players[scr].curY, Players[scr].curX, scr == me);
274 int DropPiece(int scr)
278 EraseShape(Players[scr].curShape, scr,
279 Players[scr].curY, Players[scr].curX, 1);
280 while (ShapeFits(Players[scr].curShape, scr,
281 Players[scr].curY - 1, Players[scr].curX)) {
285 PlotShape(Players[scr].curShape, scr,
286 Players[scr].curY, Players[scr].curX, 0);
290 int BlockFree(int scr, int x, int y, unsigned char z)
291 { //Check if blocks are empty below block (x,y) and sticking to (x,y) mask <z>
292 unsigned char curblock;
294 if (y == 0) return 0; //at bottom
295 curblock = GetBlock(scr, y, x) & z;
296 if (curblock & 16 && !BlockFree(scr, x, y-1, z & 208)) return 0;
297 if (curblock & 32 && !BlockFree(scr, x, y+1, z & 224)) return 0;
298 if (curblock & 64 && !BlockFree(scr, x+1, y, z & 112)) return 0;
299 if (curblock & 128 && !BlockFree(scr, x-1, y, z & 176)) return 0;
300 if ((z = GetBlock(scr, y-1, x)) & 32) return 1; //stuck to block below
301 if (z > BT_none) return 0; //some other piece below
302 return 1; //nothing below
305 int BlockFall(int scr, int x, int y, unsigned char z)
306 { //Drop down block (x,y) and those sticking to it mask <z>
307 if (GetBlock(scr, y, x) & z & 16) BlockFall(scr, x, y-1, z & 208);
308 if (GetBlock(scr, y, x) & z & 32) BlockFall(scr, x, y+1, z & 224);
309 if (GetBlock(scr, y, x) & z & 64) BlockFall(scr, x+1, y, z & 112);
310 if (GetBlock(scr, y, x) & z & 128) BlockFall(scr, x-1, y, z & 174);
311 SetBlock(scr, y-1, x, GetBlock(scr, y, x));
312 SetBlock(scr, y, x, BT_none);
315 int CheckFall(int scr)
316 { //Drop any free blocks on field
317 int xloop, x, x2, y, fallen = 0;
320 if (!Game.gravity) return 0;
321 for (y = Players[scr].boardHeight - 1; y > 0; y--)
322 for (x = 0; x < Players[scr].boardWidth; x++) {
323 if ((z = GetBlock(scr, y, x)) > BT_none && (z & 160) == 0) {
324 //doesn't stick left/up => topleft block
325 if (BlockFree(scr, x, y, 240)) {
326 BlockFall(scr, x, y, 240);
334 int LineIsFull(int scr, int y)
335 { //return 0 if any blocks present on line <y>
338 for (x = 0; x < Players[scr].boardWidth; x++)
339 if (GetBlock(scr, y, x) <= BT_none)
344 void CopyLine(int scr, int from, int to)
345 { //move blocks on line <from> to line <to>
349 for (x = 0; x < Players[scr].boardWidth; ++x)
350 SetBlock(scr, to, x, GetBlock(scr, from, x));
353 int ClearFullLines(int scr)
354 { //remove full lines, return lines cleared
355 int from, to, x, linescleared = 0;
359 while (to < Players[scr].boardHeight) {
360 while (LineIsFull(scr, from)) {
362 for (x = 0; x<Players[scr].boardWidth; x++) {
363 SetBlock(scr, from, x, GetBlock(scr, from, x) & 239);
365 SetBlock(scr, from-2, x, GetBlock(scr, from-2, x) & 223);
366 } //don't stick blocks to line which we'll remove
368 CopyLine(scr, from++, to++);
370 linescleared += from - to;
371 } while (CheckFall(scr));
375 void FreezePiece(int scr)
380 void InsertJunk(int scr, int color, int count, int column)
381 { //add <count> junklines with hole at <column> to <scr> by team <color>
384 EraseShape(Players[scr].curShape, scr,
385 Players[scr].curY, Players[scr].curX, 1);
386 for (y = Players[scr].boardHeight - count - 1; y >= 0; --y)
387 CopyLine(scr, y, y + count);
388 for (y = 0; y < count; ++y)
389 for (x = 0; x < Players[scr].boardWidth; ++x)
390 SetBlock(scr, y, x, x == column ? BT_none : color + 1
391 + 64 * (x != column-1 && x < Players[scr].boardWidth-1)
392 + 128 * (x != column+1 && x > 0));
393 Players[scr].curY += count; //move piece up..
394 for (y = 0; y < count; ++y)
395 if (ShapeFits(Players[scr].curShape, scr, Players[scr].curY - 1, Players[scr].curX))
396 Players[scr].curY--; //..and down again as far as possible
398 PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,