code duplication
[netris.git] / board.c
1 /*
2  * Netris -- A free networked version of T*tris
3  * Copyright (C) 1994-1996,1999  Mark H. Weaver <mhw@netris.org>
4  * 
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.
9  * 
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.
14  * 
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.
18  */
19
20 #include "netris.h"
21 #include <stdlib.h>
22
23 #include "board.h"
24
25 static const char shapes[7][4][4][4] = {
26         /*
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)
30          */
31         
32         // J
33         { { {   0,    0,    0, 0000},
34             {0x47, 0xC7, 0x97, 0000},
35             {   0,    0, 0x27, 0000},
36             {0000, 0000, 0000, 0000} },
37           { {   0, 0x17,    0, 0000},
38             {   0, 0x37,    0, 0000},
39             {0x47, 0xA7,    0, 0000},
40             {0000, 0000, 0000, 0000} },
41           { {0x17,    0,    0, 0000},
42             {0x67, 0xC7, 0x87, 0000},
43             {   0,    0,    0, 0000},
44             {0000, 0000, 0000, 0000} },
45           { {   0, 0x57, 0x87, 0000},
46             {   0, 0x37,    0, 0000},
47             {   0, 0x27,    0, 0000},
48             {0000, 0000, 0000, 0000} } },
49         
50         // L
51         { { {   0,    0,    0, 0000},
52             {0x53, 0xC3, 0x83, 0000},
53             {0x23,    0,    0, 0000},
54             {0000, 0000, 0000, 0000} },
55           { {0x43, 0x93,    0, 0000},
56             {   0, 0x33,    0, 0000},
57             {   0, 0x23,    0, 0000},
58             {0000, 0000, 0000, 0000} },
59           { {   0,    0, 0x13, 0000},
60             {0x43, 0xC3, 0xA3, 0000},
61             {   0,    0,    0, 0000},
62             {0000, 0000, 0000, 0000} },
63           { {   0, 0x13,    0, 0000},
64             {   0, 0x33,    0, 0000},
65             {   0, 0x63, 0x83, 0000},
66             {0000, 0000, 0000, 0000} } },
67         
68         // T
69         { { {   0,    0,    0, 0000},
70             {0x48, 0xD8, 0x88, 0000},
71             {   0, 0x28,    0, 0000},
72             {0000, 0000, 0000, 0000} },
73           { {   0, 0x18,    0, 0000},
74             {0x48, 0xB8,    0, 0000},
75             {   0, 0x28,    0, 0000},
76             {0000, 0000, 0000, 0000} },
77           { {   0, 0x18,    0, 0000},
78             {0x48, 0xE8, 0x88, 0000},
79             {   0,    0,    0, 0000},
80             {0000, 0000, 0000, 0000} },
81           { {   0, 0x18,    0, 0000},
82             {   0, 0x78, 0x88, 0000},
83             {   0, 0x28,    0, 0000},
84             {0000, 0000, 0000, 0000} } },
85         
86         // S
87         { { {   0,    0,    0, 0000},
88             {   0, 0x52, 0x82, 0000},
89             {0x42, 0xA2,    0, 0000},
90             {0000, 0000, 0000, 0000} },
91           { {0x12,    0,    0, 0000},
92             {0x62, 0x92,    0, 0000},
93             {   0, 0x22,    0, 0000},
94             {0000, 0000, 0000, 0000} },
95           { {   0,    0,    0, 0000},
96             {   0, 0x52, 0x82, 0000},
97             {0x42, 0xA2,    0, 0000},
98             {0000, 0000, 0000, 0000} },
99           { {0x12,    0,    0, 0000},
100             {0x62, 0x92,    0, 0000},
101             {   0, 0x22,    0, 0000},
102             {0000, 0000, 0000, 0000} } },
103         
104         // Z
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},
111             {0x26,    0,    0, 0000},
112             {0000, 0000, 0000, 0000} },
113           { {   0,    0,    0, 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},
119             {0x26,    0,    0, 0000},
120             {0000, 0000, 0000, 0000} } },
121         
122         // I
123         { { {   0,    0,    0,    0},
124             {0x44, 0xC4, 0xC4, 0x84},
125             {   0,    0,    0,    0},
126             {   0,    0,    0,    0} },
127           { {   0, 0x14,    0,    0},
128             {   0, 0x34,    0,    0},
129             {   0, 0x34,    0,    0},
130             {   0, 0x24,    0,    0} },
131           { {   0,    0,    0,    0},
132             {0x44, 0xC4, 0xC4, 0x84},
133             {   0,    0,    0,    0},
134             {   0,    0,    0,    0} },
135           { {   0, 0x14,    0,    0},
136             {   0, 0x34,    0,    0},
137             {   0, 0x34,    0,    0},
138             {   0, 0x24,    0,    0} } },
139         
140         // O
141         { { {   0,    0,    0, 0000},
142             {   0, 0x55, 0x95, 0000},
143             {   0, 0x65, 0xA5, 0000},
144             {0000, 0000, 0000, 0000} },
145           { {   0,    0,    0, 0000},
146             {   0, 0x55, 0x95, 0000},
147             {   0, 0x65, 0xA5, 0000},
148             {0000, 0000, 0000, 0000} },
149           { {   0,    0,    0, 0000},
150             {   0, 0x55, 0x95, 0000},
151             {   0, 0x65, 0xA5, 0000},
152             {0000, 0000, 0000, 0000} },
153           { {   0,    0,    0, 0000},
154             {   0, 0x55, 0x95, 0000},
155             {   0, 0x65, 0xA5, 0000},
156             {0000, 0000, 0000, 0000} } }
157 };
158
159 int shape_iterate(char s, int scr, int y, int x, ShapeDrawFunc func)
160 { //Draw a certain shape using <ShapeDrawFunc>
161         int i, j, result;
162         char type, rotation;
163
164         type = s / 4;
165         rotation = s & 3;
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]))
170                                         return result;
171         return 0;
172 }
173
174
175 char ChooseOption(float options[7])
176 { //Return a random piece with given piece weight
177         int i;
178         float total = 0, val;
179
180         for (i = 0; i < 7; i++) total += options[i];
181         val = Random(0, 32767) / 32768.0 * total;
182         for (i = 0; i < 7; i++) if ((val -= options[i]) < 0)
183                 return i << 2;
184         return 0;
185 }
186
187
188 static unsigned char board[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
189 static unsigned char oldBoard[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
190 static unsigned int changed[MAX_SCREENS][MAX_BOARD_HEIGHT];
191 static int shadowy;
192
193 void player_empty(int scr)
194 { //Empty the whole field (all blocks BT_none)
195         int y, x;
196
197         for (y = Players[scr].boardHeight - 1; y >= 0; --y)
198                 for (x = 0; x < Players[scr].boardWidth; ++x) {
199                         oldBoard[scr][y][x] = board[scr][y][x] = BT_none;
200                 }
201 }
202
203 unsigned char block_get(int scr, int y, int x)
204 { //Returns the block on field at position (x,y)
205         if (y < 0 || x < 0 || x >= Players[scr].boardWidth)
206                 return BT_wall;
207         else if (y >= Players[scr].boardHeight)
208                 return BT_none;
209         else
210                 return board[scr][y][x];
211 }
212
213 static void block_set(int scr, int y, int x, unsigned char type)
214 {
215         if (y >= 0 && y < Players[scr].boardHeight
216          && x >= 0 && x < Players[scr].boardWidth) {
217                 board[scr][y][x] = type;
218                 changed[scr][y] |= 1 << x;
219         }
220 }
221
222 int player_draw(int scr)
223 { //draw changes to screen
224         int y, x, any = 0;
225         unsigned int c;
226
227         for (y = Players[scr].boardVisible - 1; y >= 0; y--)
228                 if ((c = changed[scr][y])) { // line changed
229                         for (x = 0; c; (c >>= 1), x++)
230                                 if (c & 1 && board[scr][y][x] != oldBoard[scr][y][x]) {
231                                         block_draw_window(scr, y, x, board[scr][y][x]);
232                                         oldBoard[scr][y][x] = board[scr][y][x];
233                                 }
234                         changed[scr][y] = 0; // reset
235                         any = 1;
236                 } //changed row
237         return any;
238 }
239
240 int block_iter_set_status(int scr, int y, int x, unsigned char type)
241 {
242         block_draw_status(y, x, type);
243         return 0;
244 }
245
246 static int block_iter_shadow(int scr, int y, int x, unsigned char type)
247 { //draw shadow
248         block_set(scr, y, x, BT_shadow);
249         return 0;
250 }
251
252 static int block_iter_set(int scr, int y, int x, unsigned char type)
253 {
254         block_set(scr, y, x, type);
255         return 0;
256 }
257
258 void shape_draw(char shape, int scr, int y, int x, int shadow)
259 { //put shape on field
260         if (shadow) {
261                 for (shadowy = y - 1; shadowy >= 0; shadowy--)
262                         if (!shape_get(shape, scr, shadowy, x))
263                                 break;
264                 shape_iterate(shape, scr, shadowy + 1, x, block_iter_shadow);
265         } //draw shadow
266         shape_iterate(shape, scr, y, x, block_iter_set);
267 }
268
269 static int block_iter_erase(int scr, int y, int x, unsigned char type)
270 {
271         block_set(scr, y, x, BT_none);
272         return 0;
273 }
274
275 void shape_erase(char shape, int scr, int y, int x, int shadow)
276 { //remove block from field
277         shape_iterate(shape, scr, y, x, block_iter_erase);
278         if (shadow && scr == me) // draw shadow
279                 shape_iterate(shape, scr, shadowy + 1, x, block_iter_erase);
280 }
281
282 static int block_iter_get(int scr, int y, int x, unsigned char type)
283 {
284         return block_get(scr, y, x) > BT_none;
285 }
286
287 int shape_get(char shape, int scr, int y, int x)
288 { //check if there's nothing in the way
289         return !shape_iterate(shape, scr, y, x, block_iter_get);
290 }
291
292 static int block_iter_visible(int scr, int y, int x, unsigned char type)
293 {
294         return (y >= 0 && y < Players[scr].boardVisible &&
295                         x >= 0 && x < Players[scr].boardWidth);
296 }
297 int shape_visible(char shape, int scr, int y, int x)
298 {
299         return shape_iterate(shape, scr, y, x, block_iter_visible);
300 }
301
302 int player_move(int scr, int deltaY, int deltaX)
303 {
304         int result;
305
306         shape_erase(Players[scr].curShape, scr,
307                 Players[scr].curY, Players[scr].curX, 1);
308         result = shape_get(Players[scr].curShape, scr, Players[scr].curY + deltaY,
309                            Players[scr].curX + deltaX);
310         if (result) {
311                 Players[scr].curY += deltaY;
312                 Players[scr].curX += deltaX;
313         }
314         shape_draw(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,
315                 scr == me);
316         return result;
317 }
318
319 int player_rotate(int scr, int dir)
320 {
321         char newshape;
322         int result;
323
324         shape_erase(Players[scr].curShape, scr, Players[scr].curY,
325                 Players[scr].curX, 1);
326         /* (inc|dec)rement only 3 least significant bits which indicate rotation */
327         newshape = (Players[scr].curShape & 252) + (((Players[scr].curShape & 3) + dir) & 3);
328         result = shape_get(newshape, scr, Players[scr].curY, Players[scr].curX);
329         if (!result) {
330                 // move if it doesn't fit anymore
331                 short int slideX;
332                 for (slideX = 0; slideX < 2; slideX = -slideX) {
333                         // slide left and right
334                         if (slideX >= 0) slideX++; // slide more
335                         if (result = shape_get(newshape, scr, Players[scr].curY,
336                                 Players[scr].curX+slideX)) break;
337                 }
338                 if (result) Players[scr].curX += slideX;
339         }
340         if (result) Players[scr].curShape = newshape;
341         shape_draw(Players[scr].curShape, scr,
342                 Players[scr].curY, Players[scr].curX, scr == me);
343         return result;
344 }
345
346 int player_drop(int scr)
347 {
348         int count = 0;
349
350         shape_erase(Players[scr].curShape, scr,
351                 Players[scr].curY, Players[scr].curX, 1);
352         while (shape_get(Players[scr].curShape, scr,
353                Players[scr].curY - 1, Players[scr].curX)) {
354                 Players[scr].curY--;
355                 count++;
356         }
357         shape_draw(Players[scr].curShape, scr,
358                 Players[scr].curY, Players[scr].curX, 0);
359         return count;
360 }
361
362 static int block_free(int scr, int x, int y, unsigned char z)
363 { //Check if blocks are empty below block (x,y) and sticking to (x,y) mask <z>
364         unsigned char curblock;
365
366         if (y == 0) return 0; // at bottom
367         curblock = block_get(scr, y, x) & z;
368         if (curblock & 0x10 && !block_free(scr, x, y-1, z & 0xD0)) return 0;
369         if (curblock & 0x20 && !block_free(scr, x, y+1, z & 0xE0)) return 0;
370         if (curblock & 0x40 && !block_free(scr, x+1, y, z & 0x70)) return 0;
371         if (curblock & 0x80 && !block_free(scr, x-1, y, z & 0xB0)) return 0;
372         if ((z = block_get(scr, y-1, x)) & 0x20) return 1; // stuck to block below
373         if (z > BT_none) return 0; // some other piece below
374         return 1; // nothing below
375 }
376
377 static int block_down(int scr, int x, int y, unsigned char z)
378 { //Drop down block (x,y) and those sticking to it mask <z>
379         if (block_get(scr, y, x) & z & 0x10) block_down(scr, x, y-1, z & 0xD0);
380         if (block_get(scr, y, x) & z & 0x20) block_down(scr, x, y+1, z & 0xE0);
381         if (block_get(scr, y, x) & z & 0x40) block_down(scr, x+1, y, z & 0x70);
382         if (block_get(scr, y, x) & z & 0x80) block_down(scr, x-1, y, z & 0xB0);
383         block_set(scr, y-1, x, block_get(scr, y, x));
384         block_set(scr, y, x, BT_none);
385 }
386
387 int player_down(int scr)
388 { //Drop any free blocks on field
389         int xloop, x, x2, y, fallen = 0;
390         unsigned char z;
391
392         if (!Game.gravity) return 0;
393         for (y = Players[scr].boardHeight - 1; y > 0; y--)
394                 for (x = 0; x < Players[scr].boardWidth; x++) {
395                         if ((z = block_get(scr, y, x)) > BT_none && (z & 0xA0) == 0) {
396                                 // block present which doesn't stick left/up => topleft block
397                                 if (block_free(scr, x, y, 0xF0)) {
398                                         block_down(scr, x, y, 0xF0); // move blocks down
399                                         fallen++;
400                                 }
401                         } //block present
402                 }
403         return fallen;
404 }
405
406 static int player_linecheck(int scr, int y)
407 { //return 0 if any blocks present on line <y>
408         int x;
409
410         for (x = 0; x < Players[scr].boardWidth; x++)
411                 if (block_get(scr, y, x) <= BT_none)
412                         return 0;
413         return 1;
414 }
415
416 static void player_linecopy(int scr, int from, int to)
417 { //move blocks on line <from> to line <to>
418         int x;
419
420         if (from != to)
421                 for (x = 0; x < Players[scr].boardWidth; ++x)
422                         block_set(scr, to, x, block_get(scr, from, x));
423 }
424
425 int player_lineclear(int scr)
426 { //remove full lines, return lines cleared
427         int from, to, x, linescleared = 0;
428
429         do {
430         from = to = 0;
431         while (to < Players[scr].boardHeight) {
432                 while (player_linecheck(scr, from)) {
433                         from++; // skip
434                         for (x = 0; x<Players[scr].boardWidth; x++) {
435                                 // don't stick blocks to line which we'll remove
436                                 block_set(scr, from, x, block_get(scr, from, x) & 0xEF);
437                                 if (from > 1)
438                                         block_set(scr, from-2, x, block_get(scr, from-2, x) & 0xDF);
439                         }
440                 }
441                 player_linecopy(scr, from++, to++);
442         }
443         linescleared += from - to;
444         } while (player_down(scr));
445         return linescleared;
446 }
447
448 void player_lineadd(int scr, int color, int count, int column)
449 { //add <count> junklines with hole at <column> to <scr> by team <color>
450         int y, x;
451
452         shape_erase(Players[scr].curShape, scr,
453                 Players[scr].curY, Players[scr].curX, 1);
454         for (y = Players[scr].boardHeight - count - 1; y >= 0; --y)
455                 player_linecopy(scr, y, y + count);
456         for (y = 0; y < count; ++y)
457                 for (x = 0; x < Players[scr].boardWidth; ++x)
458                         block_set(scr, y, x, x == column ? BT_none : color + 1
459                                 + 0x40 * (x != column-1 && x < Players[scr].boardWidth-1)
460                                 + 0x80 * (x != column+1 && x > 0));
461         Players[scr].curY += count; // move piece up..
462         for (y = 0; y < count; ++y)
463                 if (shape_get(Players[scr].curShape, scr, Players[scr].curY - 1, Players[scr].curX))
464                         Players[scr].curY--; // ...and down again as far as possible
465                 else break;
466         shape_draw(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,
467                 scr == me);
468 }
469