unofficial version 0.8: chat, code cleanup
[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  * $Id: board.c,v 1.15 1999/05/16 06:56:24 mhw Exp $
20  */
21
22 #include "netris.h"
23 #include <stdlib.h>
24
25 #include "board.h"
26
27 static const char shapes[7][4][4][4] = {
28         { { {0x00, 0x00, 0x00, 0x00}, {0x47, 0xC7, 0x97, 0x00},
29             {0x00, 0x00, 0x27, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //sharp horizontal
30           { {0x00, 0x17, 0x00, 0x00}, {0x00, 0x37, 0x00, 0x00},
31             {0x47, 0xA7, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //blunt vertical J
32           { {0x17, 0x00, 0x00, 0x00}, {0x67, 0xC7, 0x87, 0x00},
33             {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //blunt horizontal
34           { {0x00, 0x57, 0x87, 0x00}, {0x00, 0x37, 0x00, 0x00},
35             {0x00, 0x27, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, //J (yellow)
36
37         { { {0x00, 0x00, 0x00, 0x00}, {0x53, 0xC3, 0x83, 0x00},
38             {0x23, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //sharp horizontal
39           { {0x43, 0x93, 0x00, 0x00}, {0x00, 0x33, 0x00, 0x00},
40             {0x00, 0x23, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //sharp vertical
41           { {0x00, 0x00, 0x13, 0x00}, {0x43, 0xC3, 0xA3, 0x00},
42             {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //blunt horizontal
43           { {0x00, 0x13, 0x00, 0x00}, {0x00, 0x33, 0x00, 0x00},
44             {0x00, 0x63, 0x83, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, //L (cyan)
45
46         { { {0x00, 0x00, 0x00, 0x00}, {0x48, 0xD8, 0x88, 0x00},
47             {0x00, 0x28, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //pointing down
48           { {0x00, 0x18, 0x00, 0x00}, {0x48, 0xB8, 0x00, 0x00},
49             {0x00, 0x28, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //pointing left
50           { {0x00, 0x18, 0x00, 0x00}, {0x48, 0xE8, 0x88, 0x00},
51             {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //pointing up
52           { {0x00, 0x18, 0x00, 0x00}, {0x00, 0x78, 0x88, 0x00},
53             {0x00, 0x28, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, //T (white)
54
55         { { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x52, 0x82, 0x00},
56             {0x42, 0xA2, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} },
57           { {0x12, 0x00, 0x00, 0x00}, {0x62, 0x92, 0x00, 0x00},
58             {0x00, 0x22, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} },
59           { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x52, 0x82, 0x00},
60             {0x42, 0xA2, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //rep
61           { {0x12, 0x00, 0x00, 0x00}, {0x62, 0x92, 0x00, 0x00},
62             {0x00, 0x22, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, //S (green)
63
64         { { {0x00, 0x00, 0x00, 0x00}, {0x46, 0x96, 0x00, 0x00},
65             {0x00, 0x66, 0x86, 0x00}, {0x00, 0x00, 0x00, 0x00} },
66           { {0x00, 0x16, 0x00, 0x00}, {0x56, 0xA6, 0x00, 0x00},
67             {0x26, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} },
68           { {0x00, 0x00, 0x00, 0x00}, {0x46, 0x96, 0x00, 0x00},
69             {0x00, 0x66, 0x86, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //rep
70           { {0x00, 0x16, 0x00, 0x00}, {0x56, 0xA6, 0x00, 0x00},
71             {0x26, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} } }, //Z (red)
72
73         { { {0x00, 0x00, 0x00, 0x00}, {0x44, 0xC4, 0xC4, 0x84},
74             {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //lieing
75           { {0x00, 0x14, 0x00, 0x00}, {0x00, 0x34, 0x00, 0x00},
76             {0x00, 0x34, 0x00, 0x00}, {0x00, 0x24, 0x00, 0x00} }, //standing
77           { {0x00, 0x00, 0x00, 0x00}, {0x44, 0xC4, 0xC4, 0x84},
78             {0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00} }, //rep
79           { {0x00, 0x14, 0x00, 0x00}, {0x00, 0x34, 0x00, 0x00},
80             {0x00, 0x34, 0x00, 0x00}, {0x00, 0x24, 0x00, 0x00} } }, //stick (blue)
81
82         { { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
83             {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} },
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} }, //rep
88           { {0x00, 0x00, 0x00, 0x00}, {0x00, 0x55, 0x95, 0x00},
89             {0x00, 0x65, 0xA5, 0x00}, {0x00, 0x00, 0x00, 0x00} } } //square (purple)
90 };
91
92 int ShapeIterate(char s, int scr, int y, int x, ShapeDrawFunc func)
93 { //Draw a certain shape using <ShapeDrawFunc>
94         int i, j, result;
95         char type, rotation;
96
97         type = s/4;
98         rotation = s&3;
99         for (i = 0; i<4; i++)
100                 for (j = 0; j<4; j++)
101                         if (shapes[type][rotation][i][j])
102                                 if (result = func(scr, y-i, x+j, shapes[type][rotation][i][j]))
103                                         return result;
104         return 0;
105 }
106
107
108 float stdOptions[7] = {1, 1, 1, 1, 1, 1, 1}; //stdOptions
109
110 char ChooseOption(float options[7])
111 { //Return a random piece with given piece weight
112         int i;
113         float total = 0, val;
114
115         for (i = 0; i<7; i++) total += options[i];
116         val = Random(0, 32767)/32768.0*total;
117         for (i = 0; i<7; i++) if ((val -= options[i])<0) return i<<2;
118         return 0;
119 }
120
121
122 static unsigned char board[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
123 static unsigned char oldBoard[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
124 static unsigned int changed[MAX_SCREENS][MAX_BOARD_HEIGHT];
125 static int shadowy;
126
127 void ClearField(int scr)
128 { //Empty the whole field (all blocks BT_none)
129         int y, x;
130
131         for (y = Players[scr].boardHeight - 1; y >= 0; --y)
132                 for (x = 0; x < Players[scr].boardWidth; ++x) {
133                         oldBoard[scr][y][x] = board[scr][y][x] = BT_none;
134                 }
135 } //ClearField
136
137 unsigned char GetBlock(int scr, int y, int x)
138 { //Returns the block on field at position (x,y)
139         if (y < 0 || x < 0 || x >= Players[scr].boardWidth)
140                 return BT_wall;
141         else if (y >= Players[scr].boardHeight)
142                 return BT_none;
143         else
144                 return board[scr][y][x];
145 } //GetBlock
146
147 void SetBlock(int scr, int y, int x, unsigned char type)
148 {
149         if (y >= 0 && y < Players[scr].boardHeight &&
150                 x >= 0 && x < Players[scr].boardWidth) {
151                 board[scr][y][x] = type;
152                 changed[scr][y] |= 1 << x;
153         }
154 } //SetBlock
155
156 int RefreshBoard(int scr)
157 { //draw changes to screen
158         int y, x, any = 0;
159         unsigned int c;
160
161         for (y = Players[scr].boardVisible - 1; y >= 0; y--)
162                 if ((c = changed[scr][y])) { //line changed
163                         for (x = 0; c; (c >>= 1), x++)
164                                 if ((c & 1) && board[scr][y][x] != oldBoard[scr][y][x]) {
165                                         PlotBlock(scr, y, x, board[scr][y][x]);
166                                         oldBoard[scr][y][x] = board[scr][y][x];
167                                 }
168                         changed[scr][y] = 0; //reset
169                         any = 1;
170                 } //changed row
171         return any;
172 } //RefreshBoard
173
174 int GlanceFunc(int scr, int y, int x, unsigned char type)
175 {
176         PlotBlockXY(y, x, type);
177         return 0;
178 } //GlanceFunc
179
180 int ShadowFunc(int scr, int y, int x, unsigned char type)
181 { //draw shadow
182         SetBlock(scr, y, x, BT_shadow);
183         return 0;
184 } //ShadowFunc
185
186 int PlotFunc(int scr, int y, int x, unsigned char type)
187 {
188         SetBlock(scr, y, x, type);
189         return 0;
190 }
191 void PlotShape(char shape, int scr, int y, int x, int shadow)
192 { //put shape on field
193         if (shadow) {
194                 for (shadowy = y - 1; shadowy >= 0; shadowy--)
195                         if (!ShapeFits(shape, scr, shadowy, x))
196                                 break;
197                 ShapeIterate(shape, scr, shadowy + 1, x, ShadowFunc);
198         } //draw shadow
199         ShapeIterate(shape, scr, y, x, PlotFunc);
200 } //PlotShape
201
202 int EraseFunc(int scr, int y, int x, unsigned char type)
203 {
204         SetBlock(scr, y, x, BT_none);
205         return 0;
206 }
207 void EraseShape(char shape, int scr, int y, int x, int shadow)
208 { //remove block from field
209         ShapeIterate(shape, scr, y, x, EraseFunc);
210         if (shadow && scr == me) //draw shadow
211                 ShapeIterate(shape, scr, shadowy + 1, x, EraseFunc);
212 } //EraseShape
213
214 int CollisionFunc(int scr, int y, int x, unsigned char type)
215 {
216         return GetBlock(scr, y, x) > BT_none;
217 }
218 int ShapeFits(char shape, int scr, int y, int x)
219 { //check if there's nothing in the way
220         return !ShapeIterate(shape, scr, y, x, CollisionFunc);
221 } //ShapeFits
222
223 int VisibleFunc(int scr, int y, int x, unsigned char type)
224 {
225         return (y >= 0 && y < Players[scr].boardVisible &&
226                         x >= 0 && x < Players[scr].boardWidth);
227 }
228 int ShapeVisible(char shape, int scr, int y, int x)
229 {
230         return ShapeIterate(shape, scr, y, x, VisibleFunc);
231 } //ShapeVisible
232
233 int MovePiece(int scr, int deltaY, int deltaX)
234 {
235         int result;
236
237         EraseShape(Players[scr].curShape, scr,
238                 Players[scr].curY, Players[scr].curX, 1);
239         result = ShapeFits(Players[scr].curShape, scr, Players[scr].curY + deltaY,
240                                 Players[scr].curX + deltaX);
241         if (result) {
242                 Players[scr].curY += deltaY;
243                 Players[scr].curX += deltaX;
244         }
245         PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,
246                 scr == me);
247         return result;
248 } //MovePiece
249
250 int RotatePiece(int scr, int dir)
251 {
252         char newshape;
253         int result;
254
255         EraseShape(Players[scr].curShape, scr, Players[scr].curY,
256                 Players[scr].curX, 1);
257         /* (inc|dec)rement only 3 least significant bits which indicate rotation */
258         newshape = (Players[scr].curShape & 252) + (((Players[scr].curShape & 3) + dir) & 3);
259         result = ShapeFits(newshape, scr, Players[scr].curY, Players[scr].curX);
260         if (!result) {
261                 short int slideX;
262                 for (slideX = 0; slideX < 2; slideX = -slideX) {
263                         if (slideX >= 0) slideX++; //slide more
264                         if (result = ShapeFits(newshape, scr, Players[scr].curY,
265                                 Players[scr].curX+slideX)) break;
266                 } //slide left and right
267                 if (result) Players[scr].curX += slideX;
268         } //try to fit if it doesn't
269         if (result) Players[scr].curShape = newshape;
270         PlotShape(Players[scr].curShape, scr,
271                         Players[scr].curY, Players[scr].curX, scr == me);
272         return result;
273 } //RotatePiece
274
275 int DropPiece(int scr)
276 {
277         int count = 0;
278
279         EraseShape(Players[scr].curShape, scr,
280                 Players[scr].curY, Players[scr].curX, 1);
281         while (ShapeFits(Players[scr].curShape, scr,
282                         Players[scr].curY - 1, Players[scr].curX)) {
283                 Players[scr].curY--;
284                 count++;
285         }
286         PlotShape(Players[scr].curShape, scr,
287                 Players[scr].curY, Players[scr].curX, 0);
288         return count;
289 } //DropPiece
290
291 int BlockFree(int scr, int x, int y, unsigned char z)
292 { //Check if blocks are empty below block (x,y) and sticking to (x,y) mask <z>
293         unsigned char curblock;
294
295         if (y == 0) return 0; //at bottom
296         curblock = GetBlock(scr, y, x) & z;
297         if (curblock & 16  && !BlockFree(scr, x, y-1, z & 208)) return 0;
298         if (curblock & 32  && !BlockFree(scr, x, y+1, z & 224)) return 0;
299         if (curblock & 64  && !BlockFree(scr, x+1, y, z & 112)) return 0;
300         if (curblock & 128 && !BlockFree(scr, x-1, y, z & 176)) return 0;
301         if ((z = GetBlock(scr, y-1, x)) & 32) return 1; //stuck to block below
302         if (z > BT_none) return 0; //some other piece below
303         return 1; //nothing below
304 }
305
306 int BlockFall(int scr, int x, int y, unsigned char z)
307 { //Drop down block (x,y) and those sticking to it mask <z>
308         if (GetBlock(scr, y, x) & z & 16)  BlockFall(scr, x, y-1, z & 208);
309         if (GetBlock(scr, y, x) & z & 32)  BlockFall(scr, x, y+1, z & 224);
310         if (GetBlock(scr, y, x) & z & 64)  BlockFall(scr, x+1, y, z & 112);
311         if (GetBlock(scr, y, x) & z & 128) BlockFall(scr, x-1, y, z & 174);
312         SetBlock(scr, y-1, x, GetBlock(scr, y, x));
313         SetBlock(scr, y, x, BT_none);
314 }
315
316 int CheckFall(int scr)
317 { //Drop any free blocks on field
318         int xloop, x, x2, y, fallen = 0;
319         unsigned char z;
320
321         if (!Game.gravity) return 0;
322         for (y = Players[scr].boardHeight-1; y > 0; y--)
323                 for (x = 0; x < Players[scr].boardWidth; x++) {
324                         if (((z = GetBlock(scr, y, x)) > BT_none) && ((z & 160) == 0)) {
325                         //doesn't stick left/up => topleft block
326                                 if (BlockFree(scr, x, y, 240)) {
327                                         BlockFall(scr, x, y, 240);
328                                         fallen++;
329                                 } //move blocks down
330                         } //block present
331                 } //handle line
332         return fallen;
333 }
334
335 int LineIsFull(int scr, int y)
336 { //return 0 if any blocks present on line <y>
337         int x;
338
339         for (x = 0; x < Players[scr].boardWidth; x++)
340                 if (GetBlock(scr, y, x) <= BT_none)
341                         return 0;
342         return 1;
343 }
344
345 void CopyLine(int scr, int from, int to)
346 { //move blocks on line <from> to line <to>
347         int x;
348
349         if (from != to)
350                 for (x = 0; x < Players[scr].boardWidth; ++x)
351                         SetBlock(scr, to, x, GetBlock(scr, from, x));
352 }
353
354 int ClearFullLines(int scr)
355 { //remove full lines, return lines cleared
356         int from, to, x, linescleared = 0;
357
358         do {
359         from = to = 0;
360         while (to < Players[scr].boardHeight) {
361                 while (LineIsFull(scr, from)) {
362                         from++; //skip
363                         for (x = 0; x<Players[scr].boardWidth; x++) {
364                                 SetBlock(scr, from, x, GetBlock(scr, from, x)&239);
365                                 if (from>1)
366                                         SetBlock(scr, from-2, x, GetBlock(scr, from-2, x)&223);
367                         } //don't stick blocks to line which we'll remove
368                 } //full lines
369                 CopyLine(scr, from++, to++);
370         }
371         linescleared += from - to;
372         } while (CheckFall(scr));
373         return linescleared;
374 }
375
376 void FreezePiece(int scr)
377 {
378         // remove me! :)
379 }
380
381 void InsertJunk(int scr, int color, int count, int column)
382 { //add <count> junklines with hole at <column> to <scr> by team <color>
383         int y, x;
384
385         EraseShape(Players[scr].curShape, scr,
386                 Players[scr].curY, Players[scr].curX, 1);
387         for (y = Players[scr].boardHeight - count - 1; y >= 0; --y)
388                 CopyLine(scr, y, y + count);
389         for (y = 0; y < count; ++y)
390                 for (x = 0; x < Players[scr].boardWidth; ++x)
391                         SetBlock(scr, y, x, (x == column) ? BT_none : color + 1
392                                 + 64 * (x != column-1 && x < Players[scr].boardWidth-1)
393                                 + 128 * (x != column+1 && x > 0));
394         Players[scr].curY += count; //move piece up..
395         for (y = 0; y < count; ++y)
396                 if (ShapeFits(Players[scr].curShape, scr, Players[scr].curY - 1, Players[scr].curX))
397                         Players[scr].curY--; //..and down again as far as possible
398                 else break;
399         PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX,
400                 scr == me);
401 } //InoertJunk
402
403 /*
404  * vi: ts=4 ai
405  * vim: noai si
406  */