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)
34 {0x47, 0xC7, 0x97, 0000},
36 {0000, 0000, 0000, 0000} },
37 { { 0, 0x17, 0, 0000},
39 {0x47, 0xA7, 0, 0000},
40 {0000, 0000, 0000, 0000} },
42 {0x67, 0xC7, 0x87, 0000},
44 {0000, 0000, 0000, 0000} },
45 { { 0, 0x57, 0x87, 0000},
48 {0000, 0000, 0000, 0000} } },
52 {0x53, 0xC3, 0x83, 0000},
54 {0000, 0000, 0000, 0000} },
55 { {0x43, 0x93, 0, 0000},
58 {0000, 0000, 0000, 0000} },
59 { { 0, 0, 0x13, 0000},
60 {0x43, 0xC3, 0xA3, 0000},
62 {0000, 0000, 0000, 0000} },
63 { { 0, 0x13, 0, 0000},
65 { 0, 0x63, 0x83, 0000},
66 {0000, 0000, 0000, 0000} } },
70 {0x48, 0xD8, 0x88, 0000},
72 {0000, 0000, 0000, 0000} },
73 { { 0, 0x18, 0, 0000},
74 {0x48, 0xB8, 0, 0000},
76 {0000, 0000, 0000, 0000} },
77 { { 0, 0x18, 0, 0000},
78 {0x48, 0xE8, 0x88, 0000},
80 {0000, 0000, 0000, 0000} },
81 { { 0, 0x18, 0, 0000},
82 { 0, 0x78, 0x88, 0000},
84 {0000, 0000, 0000, 0000} } },
88 { 0, 0x52, 0x82, 0000},
89 {0x42, 0xA2, 0, 0000},
90 {0000, 0000, 0000, 0000} },
92 {0x62, 0x92, 0, 0000},
94 {0000, 0000, 0000, 0000} },
96 { 0, 0x52, 0x82, 0000},
97 {0x42, 0xA2, 0, 0000},
98 {0000, 0000, 0000, 0000} },
100 {0x62, 0x92, 0, 0000},
102 {0000, 0000, 0000, 0000} } },
105 { { { 0, 0, 0, 0000},
106 {0x46, 0x96, 0, 0000},
107 { 0, 0x66, 0x86, 0000},
108 {0000, 0000, 0000, 0000} },
109 { { 0, 0x16, 0, 0000},
110 {0x56, 0xA6, 0, 0000},
112 {0000, 0000, 0000, 0000} },
114 {0x46, 0x96, 0, 0000},
115 { 0, 0x66, 0x86, 0000},
116 {0000, 0000, 0000, 0000} },
117 { { 0, 0x16, 0, 0000},
118 {0x56, 0xA6, 0, 0000},
120 {0000, 0000, 0000, 0000} } },
124 {0x44, 0xC4, 0xC4, 0x84},
132 {0x44, 0xC4, 0xC4, 0x84},
138 { 0, 0x24, 0, 0} } },
141 { { { 0, 0, 0, 0000},
142 { 0, 0x55, 0x95, 0000},
143 { 0, 0x65, 0xA5, 0000},
144 {0000, 0000, 0000, 0000} },
146 { 0, 0x55, 0x95, 0000},
147 { 0, 0x65, 0xA5, 0000},
148 {0000, 0000, 0000, 0000} },
150 { 0, 0x55, 0x95, 0000},
151 { 0, 0x65, 0xA5, 0000},
152 {0000, 0000, 0000, 0000} },
154 { 0, 0x55, 0x95, 0000},
155 { 0, 0x65, 0xA5, 0000},
156 {0000, 0000, 0000, 0000} } }
159 int shape_iterate(char s, int scr, int y, int x, ShapeDrawFunc func)
160 { //Draw a certain shape using <ShapeDrawFunc>
166 for (i = 0; i < 4; i++)
167 for (j = 0; j < 4; j++)
168 if (shapes[type][rotation][i][j])
169 if (result = func(scr, y-i, x+j, shapes[type][rotation][i][j]))
175 float stdOptions[7] = {1, 1, 1, 1, 1, 1, 1}; //stdOptions
177 char ChooseOption(float options[7])
178 { //Return a random piece with given piece weight
180 float total = 0, val;
182 for (i = 0; i < 7; i++) total += options[i];
183 val = Random(0, 32767) / 32768.0 * total;
184 for (i = 0; i < 7; i++) if ((val -= options[i]) < 0)
190 static unsigned char board[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
191 static unsigned char oldBoard[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
192 static unsigned int changed[MAX_SCREENS][MAX_BOARD_HEIGHT];
195 void player_empty(int scr)
196 { //Empty the whole field (all blocks BT_none)
199 for (y = Players[scr].boardHeight - 1; y >= 0; --y)
200 for (x = 0; x < Players[scr].boardWidth; ++x) {
201 oldBoard[scr][y][x] = board[scr][y][x] = BT_none;
205 unsigned char block_get(int scr, int y, int x)
206 { //Returns the block on field at position (x,y)
207 if (y < 0 || x < 0 || x >= Players[scr].boardWidth)
209 else if (y >= Players[scr].boardHeight)
212 return board[scr][y][x];
215 static void block_set(int scr, int y, int x, unsigned char type)
217 if (y >= 0 && y < Players[scr].boardHeight
218 && x >= 0 && x < Players[scr].boardWidth) {
219 board[scr][y][x] = type;
220 changed[scr][y] |= 1 << x;
224 int player_draw(int scr)
225 { //draw changes to screen
229 for (y = Players[scr].boardVisible - 1; y >= 0; y--)
230 if ((c = changed[scr][y])) { // line changed
231 for (x = 0; c; (c >>= 1), x++)
232 if (c & 1 && board[scr][y][x] != oldBoard[scr][y][x]) {
233 block_draw_window(scr, y, x, board[scr][y][x]);
234 oldBoard[scr][y][x] = board[scr][y][x];
236 changed[scr][y] = 0; // reset
242 int block_iter_set_status(int scr, int y, int x, unsigned char type)
244 block_draw_status(y, x, type);
248 static int block_iter_shadow(int scr, int y, int x, unsigned char type)
250 block_set(scr, y, x, BT_shadow);
254 static int block_iter_set(int scr, int y, int x, unsigned char type)
256 block_set(scr, y, x, type);
260 void shape_draw(char shape, int scr, int y, int x, int shadow)
261 { //put shape on field
263 for (shadowy = y - 1; shadowy >= 0; shadowy--)
264 if (!shape_get(shape, scr, shadowy, x))
266 shape_iterate(shape, scr, shadowy + 1, x, block_iter_shadow);
268 shape_iterate(shape, scr, y, x, block_iter_set);
271 static int block_iter_erase(int scr, int y, int x, unsigned char type)
273 block_set(scr, y, x, BT_none);
277 void shape_erase(char shape, int scr, int y, int x, int shadow)
278 { //remove block from field
279 shape_iterate(shape, scr, y, x, block_iter_erase);
280 if (shadow && scr == me) // draw shadow
281 shape_iterate(shape, scr, shadowy + 1, x, block_iter_erase);
284 static int block_iter_get(int scr, int y, int x, unsigned char type)
286 return block_get(scr, y, x) > BT_none;
289 int shape_get(char shape, int scr, int y, int x)
290 { //check if there's nothing in the way
291 return !shape_iterate(shape, scr, y, x, block_iter_get);
294 static int block_iter_visible(int scr, int y, int x, unsigned char type)
296 return (y >= 0 && y < Players[scr].boardVisible &&
297 x >= 0 && x < Players[scr].boardWidth);
299 int shape_visible(char shape, int scr, int y, int x)
301 return shape_iterate(shape, scr, y, x, block_iter_visible);
304 int player_move(int scr, int deltaY, int deltaX)
308 shape_erase(Players[scr].curShape, scr,
309 Players[scr].curY, Players[scr].curX, 1);
310 result = shape_get(Players[scr].curShape, scr, Players[scr].curY + deltaY,
311 Players[scr].curX + deltaX);
313 Players[scr].curY += deltaY;
314 Players[scr].curX += deltaX;
316 shape_draw(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,
321 int player_rotate(int scr, int dir)
326 shape_erase(Players[scr].curShape, scr, Players[scr].curY,
327 Players[scr].curX, 1);
328 /* (inc|dec)rement only 3 least significant bits which indicate rotation */
329 newshape = (Players[scr].curShape & 252) + (((Players[scr].curShape & 3) + dir) & 3);
330 result = shape_get(newshape, scr, Players[scr].curY, Players[scr].curX);
332 // move if it doesn't fit anymore
334 for (slideX = 0; slideX < 2; slideX = -slideX) {
335 // slide left and right
336 if (slideX >= 0) slideX++; // slide more
337 if (result = shape_get(newshape, scr, Players[scr].curY,
338 Players[scr].curX+slideX)) break;
340 if (result) Players[scr].curX += slideX;
342 if (result) Players[scr].curShape = newshape;
343 shape_draw(Players[scr].curShape, scr,
344 Players[scr].curY, Players[scr].curX, scr == me);
348 int player_drop(int scr)
352 shape_erase(Players[scr].curShape, scr,
353 Players[scr].curY, Players[scr].curX, 1);
354 while (shape_get(Players[scr].curShape, scr,
355 Players[scr].curY - 1, Players[scr].curX)) {
359 shape_draw(Players[scr].curShape, scr,
360 Players[scr].curY, Players[scr].curX, 0);
364 static int block_free(int scr, int x, int y, unsigned char z)
365 { //Check if blocks are empty below block (x,y) and sticking to (x,y) mask <z>
366 unsigned char curblock;
368 if (y == 0) return 0; // at bottom
369 curblock = block_get(scr, y, x) & z;
370 if (curblock & 0x10 && !block_free(scr, x, y-1, z & 0xD0)) return 0;
371 if (curblock & 0x20 && !block_free(scr, x, y+1, z & 0xE0)) return 0;
372 if (curblock & 0x40 && !block_free(scr, x+1, y, z & 0x70)) return 0;
373 if (curblock & 0x80 && !block_free(scr, x-1, y, z & 0xB0)) return 0;
374 if ((z = block_get(scr, y-1, x)) & 0x20) return 1; // stuck to block below
375 if (z > BT_none) return 0; // some other piece below
376 return 1; // nothing below
379 static int block_down(int scr, int x, int y, unsigned char z)
380 { //Drop down block (x,y) and those sticking to it mask <z>
381 if (block_get(scr, y, x) & z & 0x10) block_down(scr, x, y-1, z & 0xD0);
382 if (block_get(scr, y, x) & z & 0x20) block_down(scr, x, y+1, z & 0xE0);
383 if (block_get(scr, y, x) & z & 0x40) block_down(scr, x+1, y, z & 0x70);
384 if (block_get(scr, y, x) & z & 0x80) block_down(scr, x-1, y, z & 0xB0);
385 block_set(scr, y-1, x, block_get(scr, y, x));
386 block_set(scr, y, x, BT_none);
389 int player_down(int scr)
390 { //Drop any free blocks on field
391 int xloop, x, x2, y, fallen = 0;
394 if (!Game.gravity) return 0;
395 for (y = Players[scr].boardHeight - 1; y > 0; y--)
396 for (x = 0; x < Players[scr].boardWidth; x++) {
397 if ((z = block_get(scr, y, x)) > BT_none && (z & 0xA0) == 0) {
398 // block present which doesn't stick left/up => topleft block
399 if (block_free(scr, x, y, 0xF0)) {
400 block_down(scr, x, y, 0xF0); // move blocks down
408 static int player_linecheck(int scr, int y)
409 { //return 0 if any blocks present on line <y>
412 for (x = 0; x < Players[scr].boardWidth; x++)
413 if (block_get(scr, y, x) <= BT_none)
418 static void player_linecopy(int scr, int from, int to)
419 { //move blocks on line <from> to line <to>
423 for (x = 0; x < Players[scr].boardWidth; ++x)
424 block_set(scr, to, x, block_get(scr, from, x));
427 int player_lineclear(int scr)
428 { //remove full lines, return lines cleared
429 int from, to, x, linescleared = 0;
433 while (to < Players[scr].boardHeight) {
434 while (player_linecheck(scr, from)) {
436 for (x = 0; x<Players[scr].boardWidth; x++) {
437 // don't stick blocks to line which we'll remove
438 block_set(scr, from, x, block_get(scr, from, x) & 0xEF);
440 block_set(scr, from-2, x, block_get(scr, from-2, x) & 0xDF);
443 player_linecopy(scr, from++, to++);
445 linescleared += from - to;
446 } while (player_down(scr));
450 void player_lineadd(int scr, int color, int count, int column)
451 { //add <count> junklines with hole at <column> to <scr> by team <color>
454 shape_erase(Players[scr].curShape, scr,
455 Players[scr].curY, Players[scr].curX, 1);
456 for (y = Players[scr].boardHeight - count - 1; y >= 0; --y)
457 player_linecopy(scr, y, y + count);
458 for (y = 0; y < count; ++y)
459 for (x = 0; x < Players[scr].boardWidth; ++x)
460 block_set(scr, y, x, x == column ? BT_none : color + 1
461 + 0x40 * (x != column-1 && x < Players[scr].boardWidth-1)
462 + 0x80 * (x != column+1 && x > 0));
463 Players[scr].curY += count; // move piece up..
464 for (y = 0; y < count; ++y)
465 if (shape_get(Players[scr].curShape, scr, Players[scr].curY - 1, Players[scr].curX))
466 Players[scr].curY--; // ...and down again as far as possible
468 shape_draw(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,