document shapes array
[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         { { {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 J
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)
40
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)
49
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)
58
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} }, //rep
65           { {0x12, 0x00, 0x00, 0x00}, {0x62, 0x92, 0x00, 0x00},
66             {0x00, 0x22, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, //S (green)
67
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} }, //rep
74           { {0x00, 0x16, 0x00, 0x00}, {0x56, 0xA6, 0x00, 0x00},
75             {0x26, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, //Z (red)
76
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} }, //rep
83           { {0x00, 0x14, 0x00, 0x00}, {0x00, 0x34, 0x00, 0x00},
84             {0x00, 0x34, 0x00, 0x00}, {0x00, 0x24, 0x00, 0x00} } }, //I, stick (blue)
85
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} }, //rep
90           { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
91             {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //rep
92           { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
93             {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} } } //O, square (purple)
94 };
95
96 int ShapeIterate(char s, int scr, int y, int x, ShapeDrawFunc func)
97 { //Draw a certain shape using <ShapeDrawFunc>
98         int i, j, result;
99         char type, rotation;
100
101         type = s / 4;
102         rotation = s & 3;
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]))
107                                         return result;
108         return 0;
109 }
110
111
112 float stdOptions[7] = {1, 1, 1, 1, 1, 1, 1}; //stdOptions
113
114 char ChooseOption(float options[7])
115 { //Return a random piece with given piece weight
116         int i;
117         float total = 0, val;
118
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)
122                 return i << 2;
123         return 0;
124 }
125
126
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];
130 static int shadowy;
131
132 void ClearField(int scr)
133 { //Empty the whole field (all blocks BT_none)
134         int y, x;
135
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;
139                 }
140 }
141
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)
145                 return BT_wall;
146         else if (y >= Players[scr].boardHeight)
147                 return BT_none;
148         else
149                 return board[scr][y][x];
150 }
151
152 void SetBlock(int scr, int y, int x, unsigned char type)
153 {
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;
158         }
159 }
160
161 int RefreshBoard(int scr)
162 { //draw changes to screen
163         int y, x, any = 0;
164         unsigned int c;
165
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];
172                                 }
173                         changed[scr][y] = 0; //reset
174                         any = 1;
175                 } //changed row
176         return any;
177 }
178
179 int GlanceFunc(int scr, int y, int x, unsigned char type)
180 {
181         PlotBlockXY(y, x, type);
182         return 0;
183 }
184
185 int ShadowFunc(int scr, int y, int x, unsigned char type)
186 { //draw shadow
187         SetBlock(scr, y, x, BT_shadow);
188         return 0;
189 }
190
191 int PlotFunc(int scr, int y, int x, unsigned char type)
192 {
193         SetBlock(scr, y, x, type);
194         return 0;
195 }
196 void PlotShape(char shape, int scr, int y, int x, int shadow)
197 { //put shape on field
198         if (shadow) {
199                 for (shadowy = y - 1; shadowy >= 0; shadowy--)
200                         if (!ShapeFits(shape, scr, shadowy, x))
201                                 break;
202                 ShapeIterate(shape, scr, shadowy + 1, x, ShadowFunc);
203         } //draw shadow
204         ShapeIterate(shape, scr, y, x, PlotFunc);
205 }
206
207 int EraseFunc(int scr, int y, int x, unsigned char type)
208 {
209         SetBlock(scr, y, x, BT_none);
210         return 0;
211 }
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);
217 }
218
219 int CollisionFunc(int scr, int y, int x, unsigned char type)
220 {
221         return GetBlock(scr, y, x) > BT_none;
222 }
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);
226 }
227
228 int VisibleFunc(int scr, int y, int x, unsigned char type)
229 {
230         return (y >= 0 && y < Players[scr].boardVisible &&
231                         x >= 0 && x < Players[scr].boardWidth);
232 }
233 int ShapeVisible(char shape, int scr, int y, int x)
234 {
235         return ShapeIterate(shape, scr, y, x, VisibleFunc);
236 }
237
238 int MovePiece(int scr, int deltaY, int deltaX)
239 {
240         int result;
241
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);
246         if (result) {
247                 Players[scr].curY += deltaY;
248                 Players[scr].curX += deltaX;
249         }
250         PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,
251                 scr == me);
252         return result;
253 }
254
255 int RotatePiece(int scr, int dir)
256 {
257         char newshape;
258         int result;
259
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);
265         if (!result) {
266                 short int slideX;
267                 for (slideX = 0; slideX < 2; slideX = -slideX) {
268                         if (slideX >= 0) slideX++; //slide more
269                         if (result = ShapeFits(newshape, scr, Players[scr].curY,
270                                 Players[scr].curX+slideX)) break;
271                 } //slide left and right
272                 if (result) Players[scr].curX += slideX;
273         } //try to fit if it doesn't
274         if (result) Players[scr].curShape = newshape;
275         PlotShape(Players[scr].curShape, scr,
276                 Players[scr].curY, Players[scr].curX, scr == me);
277         return result;
278 }
279
280 int DropPiece(int scr)
281 {
282         int count = 0;
283
284         EraseShape(Players[scr].curShape, scr,
285                 Players[scr].curY, Players[scr].curX, 1);
286         while (ShapeFits(Players[scr].curShape, scr,
287                Players[scr].curY - 1, Players[scr].curX)) {
288                 Players[scr].curY--;
289                 count++;
290         }
291         PlotShape(Players[scr].curShape, scr,
292                 Players[scr].curY, Players[scr].curX, 0);
293         return count;
294 }
295
296 int BlockFree(int scr, int x, int y, unsigned char z)
297 { //Check if blocks are empty below block (x,y) and sticking to (x,y) mask <z>
298         unsigned char curblock;
299
300         if (y == 0) return 0; //at bottom
301         curblock = GetBlock(scr, y, x) & z;
302         if (curblock & 0x10 && !BlockFree(scr, x, y-1, z & 0xD0)) return 0;
303         if (curblock & 0x20 && !BlockFree(scr, x, y+1, z & 0xE0)) return 0;
304         if (curblock & 0x40 && !BlockFree(scr, x+1, y, z & 0x70)) return 0;
305         if (curblock & 0x80 && !BlockFree(scr, x-1, y, z & 0xB0)) return 0;
306         if ((z = GetBlock(scr, y-1, x)) & 0x20) return 1; //stuck to block below
307         if (z > BT_none) return 0; //some other piece below
308         return 1; //nothing below
309 }
310
311 int BlockFall(int scr, int x, int y, unsigned char z)
312 { //Drop down block (x,y) and those sticking to it mask <z>
313         if (GetBlock(scr, y, x) & z & 0x10) BlockFall(scr, x, y-1, z & 0xD0);
314         if (GetBlock(scr, y, x) & z & 0x20) BlockFall(scr, x, y+1, z & 0xE0);
315         if (GetBlock(scr, y, x) & z & 0x40) BlockFall(scr, x+1, y, z & 0x70);
316         if (GetBlock(scr, y, x) & z & 0x80) BlockFall(scr, x-1, y, z & 0xB0);
317         SetBlock(scr, y-1, x, GetBlock(scr, y, x));
318         SetBlock(scr, y, x, BT_none);
319 }
320
321 int CheckFall(int scr)
322 { //Drop any free blocks on field
323         int xloop, x, x2, y, fallen = 0;
324         unsigned char z;
325
326         if (!Game.gravity) return 0;
327         for (y = Players[scr].boardHeight - 1; y > 0; y--)
328                 for (x = 0; x < Players[scr].boardWidth; x++) {
329                         if ((z = GetBlock(scr, y, x)) > BT_none && (z & 0xA0) == 0) {
330                         //doesn't stick left/up => topleft block
331                                 if (BlockFree(scr, x, y, 0xF0)) {
332                                         BlockFall(scr, x, y, 0xF0);
333                                         fallen++;
334                                 } //move blocks down
335                         } //block present
336                 } //handle line
337         return fallen;
338 }
339
340 int LineIsFull(int scr, int y)
341 { //return 0 if any blocks present on line <y>
342         int x;
343
344         for (x = 0; x < Players[scr].boardWidth; x++)
345                 if (GetBlock(scr, y, x) <= BT_none)
346                         return 0;
347         return 1;
348 }
349
350 void CopyLine(int scr, int from, int to)
351 { //move blocks on line <from> to line <to>
352         int x;
353
354         if (from != to)
355                 for (x = 0; x < Players[scr].boardWidth; ++x)
356                         SetBlock(scr, to, x, GetBlock(scr, from, x));
357 }
358
359 int ClearFullLines(int scr)
360 { //remove full lines, return lines cleared
361         int from, to, x, linescleared = 0;
362
363         do {
364         from = to = 0;
365         while (to < Players[scr].boardHeight) {
366                 while (LineIsFull(scr, from)) {
367                         from++; //skip
368                         for (x = 0; x<Players[scr].boardWidth; x++) {
369                                 SetBlock(scr, from, x, GetBlock(scr, from, x) & 0xEF);
370                                 if (from > 1)
371                                         SetBlock(scr, from-2, x, GetBlock(scr, from-2, x) & 0xDF);
372                         } //don't stick blocks to line which we'll remove
373                 } //full lines
374                 CopyLine(scr, from++, to++);
375         }
376         linescleared += from - to;
377         } while (CheckFall(scr));
378         return linescleared;
379 }
380
381 void FreezePiece(int scr)
382 {
383         // remove me! :)
384 }
385
386 void InsertJunk(int scr, int color, int count, int column)
387 { //add <count> junklines with hole at <column> to <scr> by team <color>
388         int y, x;
389
390         EraseShape(Players[scr].curShape, scr,
391                 Players[scr].curY, Players[scr].curX, 1);
392         for (y = Players[scr].boardHeight - count - 1; y >= 0; --y)
393                 CopyLine(scr, y, y + count);
394         for (y = 0; y < count; ++y)
395                 for (x = 0; x < Players[scr].boardWidth; ++x)
396                         SetBlock(scr, y, x, x == column ? BT_none : color + 1
397                                 + 0x40 * (x != column-1 && x < Players[scr].boardWidth-1)
398                                 + 0x80 * (x != column+1 && x > 0));
399         Players[scr].curY += count; //move piece up..
400         for (y = 0; y < count; ++y)
401                 if (ShapeFits(Players[scr].curShape, scr, Players[scr].curY - 1, Players[scr].curX))
402                         Players[scr].curY--; //..and down again as far as possible
403                 else break;
404         PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,
405                 scr == me);
406 }
407