Mercurial > hg > index.fcgi > lj > lj046-2players
view src/lj.c @ 2:80a2761bd3a4
change DS keys (add alt. rotate)
author | paulo@localhost |
---|---|
date | Mon, 23 Mar 2009 01:19:12 -0700 |
parents | |
children |
line source
1 /* Engine of LOCKJAW, an implementation of the Soviet Mind Game
3 Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net>
5 This work is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 Original game concept and design by Alexey Pajitnov.
20 The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg,
21 or The Tetris Company LLC.
24 */
26 #define LJ_INTERNAL
27 #include "lj.h"
29 unsigned int ljRand(LJField *p) {
30 p->seed = p->seed * 2147001325 + 715136305;
31 return p->seed >> 17;
32 }
34 static inline void ljAssert(LJField *p, int shouldBeTrue, const char *reason) {
35 if (!shouldBeTrue) {
36 p->state = LJS_GAMEOVER;
37 }
38 }
40 static const char xShapes[N_PIECE_SHAPES][4][4] = {
41 { {0,1,2,3}, {2,2,2,2}, {3,2,1,0}, {1,1,1,1} }, // I
42 { {0,1,2,0}, {1,1,1,2}, {2,1,0,2}, {1,1,1,0} }, // J
43 { {0,1,2,2}, {1,1,1,2}, {2,1,0,0}, {1,1,1,0} }, // L
44 { {1,1,2,2}, {1,2,2,1}, {2,2,1,1}, {2,1,1,2} }, // O
45 { {0,1,1,2}, {1,1,2,2}, {2,1,1,0}, {1,1,0,0} }, // S
46 { {0,1,2,1}, {1,1,1,2}, {2,1,0,1}, {1,1,1,0} }, // T
47 { {0,1,1,2}, {2,2,1,1}, {2,1,1,0}, {0,0,1,1} }, // Z
48 { {0,1,2,3}, {2,2,2,2}, {3,2,1,0}, {1,1,1,1} }, // I2
49 { {0,1,2,0}, {1,1,1,2}, {2,1,0,2}, {1,1,1,0} }, // I3
50 { {1,1,2,2}, {1,2,2,1}, {2,2,1,1}, {2,1,1,2} }, // L3
51 };
53 static const char yShapes[N_PIECE_SHAPES][4][4] = {
54 { {2,2,2,2}, {3,2,1,0}, {1,1,1,1}, {0,1,2,3} }, // I
55 { {2,2,2,3}, {3,2,1,3}, {2,2,2,1}, {1,2,3,1} }, // J
56 { {2,2,2,3}, {3,2,1,1}, {2,2,2,1}, {1,2,3,3} }, // L
57 { {2,3,3,2}, {3,3,2,2}, {3,2,2,3}, {2,2,3,3} }, // O
58 { {2,2,3,3}, {3,2,2,1}, {2,2,1,1}, {1,2,2,3} }, // S
59 { {2,2,2,3}, {3,2,1,2}, {2,2,2,1}, {1,2,3,2} }, // T
60 { {3,3,2,2}, {3,2,2,1}, {1,1,2,2}, {1,2,2,3} }, // Z
61 { {2,2,2,2}, {3,2,1,0}, {1,1,1,1}, {0,1,2,3} }, // I2
62 { {2,2,2,3}, {3,2,1,3}, {2,2,2,1}, {1,2,3,1} }, // I3
63 { {2,3,3,2}, {3,3,2,2}, {3,2,2,3}, {2,2,3,3} }, // L3
64 };
66 const char pieceColors[N_PIECE_SHAPES] = {
67 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
68 0x40, 0x10, 0x20
69 };
71 static const signed char connShapes[N_PIECE_SHAPES][4] = {
72 {CONNECT_R, CONNECT_LR, CONNECT_LR, CONNECT_L}, // I
73 {CONNECT_UR, CONNECT_LR, CONNECT_L, CONNECT_D}, // J
74 {CONNECT_R, CONNECT_LR, CONNECT_UL, CONNECT_D}, // L
75 {CONNECT_UR, CONNECT_DR, CONNECT_DL, CONNECT_UL}, // O
76 {CONNECT_R, CONNECT_UL, CONNECT_DR, CONNECT_L}, // S
77 {CONNECT_R, CONNECT_ULR, CONNECT_L, CONNECT_D}, // T
78 {CONNECT_R, CONNECT_DL, CONNECT_UR, CONNECT_L}, // Z
79 {-1, CONNECT_R, CONNECT_L, -1}, // I2
80 {CONNECT_R, CONNECT_LR, CONNECT_L, -1}, // I3
81 {CONNECT_UR, CONNECT_D, -1, CONNECT_L}, // L3
82 };
84 static inline int pieceToFieldBlock(int piece, int conn) {
85 return conn | pieceColors[piece];
86 }
88 /**
89 * @theta rotation state: 0-3 normal, 4 for next piece
90 */
91 void expandPieceToBlocks(LJBlkSpec out[],
92 const LJField *p, int piece, int xBase, int yBase, int theta) {
93 int shape = piece & LJP_MASK;
95 if (theta >= 4) {
96 const LJRotSystem *rs = rotSystems[p->rotationSystem];
97 int kickData = rs->entryOffset[shape];
98 xBase += WKX(kickData);
99 yBase += WKY(kickData);
100 kickData = rs->entryOffset[LJP_I];
101 xBase -= WKX(kickData);
102 yBase -= WKY(kickData);
103 theta = rs->entryTheta[shape];
104 }
106 const char *xBl = xShapes[shape][theta];
107 const char *yBl = yShapes[shape][theta];
109 for (int blk = 0; blk < 4; ++blk) {
110 if (connShapes[shape][blk] == -1) {
111 out[blk].conn = 0;
112 } else {
113 int conn = connShapes[shape][blk] << theta;
114 int connRotated = (conn | (conn >> 4)) & CONNECT_MASK;
115 int color = ((0x10 << blk) & piece) ? 8 : piece;
116 out[blk].y = yBl[blk] + yBase;
117 out[blk].x = xBl[blk] + xBase;
118 out[blk].conn = pieceToFieldBlock(color, connRotated);
119 }
120 }
121 }
123 int isOccupied(const LJField *p, int x, int y) {
124 if (x < p->leftWall || x >= p->rightWall || y < 0)
125 return 1;
126 if (y > LJ_PF_HT)
127 return 0;
128 return p->b[y][x] > 1;
129 }
131 int isCollision(const LJField *p, int x, int y, int theta) {
132 LJBlkSpec blocks[4];
133 int piece = p->curPiece[0];
135 expandPieceToBlocks(blocks, p, piece, x, y, theta);
137 for (int blk = 0; blk < 4; ++blk) {
138 if (blocks[blk].conn
139 && isOccupied(p, blocks[blk].x, blocks[blk].y))
140 return 1;
141 }
142 return 0;
143 }
145 /**
146 * Determines whether the piece that just landed was a T-spin.
147 * Must be called just BEFORE lockdown writes the blocks to the
148 * playfield; otherwise TNT will break.
149 */
150 static int isTspin(const LJField *p) {
151 int blks = 0;
152 int x = p->x;
153 int y = p->hardDropY;
155 switch (p->tSpinAlgo) {
156 case LJTS_TNT:
157 if (!isCollision(p, x, y + 1, p->theta)
158 || !isCollision(p, x - 1, y, p->theta)
159 || !isCollision(p, x + 1, y, p->theta)) {
160 return 0;
161 }
162 return p->isSpin;
163 case LJTS_TDS_NO_KICK:
165 // If t-spin involved wall kick, don't count it
166 if (p->isSpin == 2) {
167 return 0;
168 }
170 // otherwise fall through
171 case LJTS_TDS:
172 // 1. T tetromino
173 if ((p->curPiece[0] & LJP_MASK) != LJP_T) {
174 return 0;
175 }
177 // 2. Last move was spin
178 if (!p->isSpin) {
179 return 0;
180 }
182 // 3. At least three cells around the rotation center are full
183 if (isOccupied(p, x, y + 1)) {
184 ++blks;
185 }
186 if (isOccupied(p, x, y + 3)) {
187 ++blks;
188 }
189 if (isOccupied(p, x + 2, y + 1)) {
190 ++blks;
191 }
192 if (isOccupied(p, x + 2, y + 3)) {
193 ++blks;
194 }
195 if (blks < 3) {
196 return 0;
197 }
199 // 3. Last move was spin
200 return p->isSpin;
201 default:
202 return 0;
203 }
204 }
206 /**
207 * Calculates where the active piece in a playfield will fall
208 * if dropped, and writes it back to the playfield.
209 * This value is used for ghost piece, gravity, soft drop, and
210 * hard drop. Call this after the active piece has been spawned,
211 * moved, or rotated.
212 * @param p the playfield
213 */
214 static void updHardDropY(LJField *p) {
215 int x = p->x;
216 int y = ljfixfloor(p->y);
217 int theta = p->theta;
219 if (p->bottomBlocks) {
220 y = -2;
221 while (y < (int)LJ_PF_HT
222 && y < ljfixfloor(p->y)
223 && isCollision(p, x, y, theta)) {
224 ++y;
225 }
226 }
227 else {
228 while (!isCollision(p, x, y - 1, theta)) {
229 --y;
230 }
231 }
232 p->hardDropY = y;
233 }
236 /**
237 * Look for a TNT square in this position.
238 * @param x column of left side, such that 0 <= x <= playfield width - 4
239 * @param y row of bottom block, such that 0 <= y <= playfield height - 4
240 * @param isMulti nonzero for multisquares; 0 for monosquares
241 * @return nonzero if found; 0 if not found
242 */
243 static int isSquareAt(LJField *p, int x, int y, int isMulti)
244 {
245 int firstColor = p->b[y][x] & COLOR_MASK;
247 // Check the frame to make sure it isn't connected to anything else
248 for(int i = 0; i <= 3; i++)
249 {
250 /* don't allow squares within parts of squares */
251 if((p->b[y + i][x] & COLOR_MASK) >= 0x80)
252 return 0;
253 /* the block doesn't connect on the left */
254 if(p->b[y + i][x] & CONNECT_L)
255 return 0;
256 /* the block doesn't connect on the right */
257 if(p->b[y + i][x + 3] & CONNECT_R)
258 return 0;
259 /* the block doesn't connect on the bottom */
260 if(p->b[y][x + i] & CONNECT_D)
261 return 0;
262 /* the block doesn't connect on the top */
263 if(p->b[y + 3][x + i] & CONNECT_U)
264 return 0;
265 }
267 for(int ySub = 0; ySub < 4; ++ySub)
268 {
269 for(int xSub = 0; xSub <= 3; ++xSub)
270 {
271 int blkHere = p->b[y + ySub][x + xSub];
273 /* the square contains no nonexistent blocks */
274 if(!blkHere)
275 return 0;
276 /* the square contains no blocks of garbage or broken pieces */
277 if((blkHere & COLOR_MASK) == 0x80)
278 return 0;
279 /* if looking for monosquares, disallow multisquares */
280 if(isMulti == 0 && (blkHere & COLOR_MASK) != firstColor)
281 return 0;
282 }
283 }
284 return 1;
285 }
287 /**
288 * Replaces the 4x4 blocks with a 4x4 square.
289 * @param x column of left side, such that 0 <= x <= playfield width - 4
290 * @param y row of bottom block, such that 0 <= y <= playfield height - 4
291 * @param isMulti nonzero for multisquares; 0 for monosquares
292 * @return the rows that were changed
293 */
294 static LJBits markSquare(LJField *p, int x, int y, int isMulti)
295 {
296 int baseBlk = (isMulti ? 0xA0 : 0xB0)
297 | CONNECT_MASK;
298 for(int i = 0; i < 4; ++i)
299 {
300 int c;
302 if(i == 0)
303 c = baseBlk & ~CONNECT_D;
304 else if(i == 3)
305 c = baseBlk & ~CONNECT_U;
306 else
307 c = baseBlk;
309 p->b[y + i][x + 0] = c & ~CONNECT_L;
310 p->b[y + i][x + 1] = c;
311 p->b[y + i][x + 2] = c;
312 p->b[y + i][x + 3] = c & ~CONNECT_R;
313 }
315 if (isMulti) {
316 ++p->multisquares;
317 } else {
318 ++p->monosquares;
319 }
321 return 0x0F << y;
322 }
325 /**
326 * Marks all 4x4 squares in the playfield.
327 * In the case that a single tetromino forms multiple overlapping
328 * squares, prefers gold over silver, high over low, left over right.
329 * @param isMulti nonzero for multisquares; 0 for monosquares
330 * @return the rows that were changed
331 */
332 static LJBits findSquares(LJField *p, int isMulti) {
333 LJBits changed = 0;
335 for (int y = LJ_PF_HT - 4; y >= 0; --y) {
336 for (int x = p->leftWall; x <= p->rightWall - 4; ++x) {
337 int baseBlk = p->b[y][x];
339 // If this block is filled in and not connected on the left or right
340 // then do stuff to it
341 if (baseBlk
342 && !(baseBlk & (CONNECT_D | CONNECT_L))
343 && isSquareAt(p, x, y, isMulti)) {
344 changed |= markSquare(p, x, y, isMulti);
345 p->sounds |= LJSND_SQUARE;
346 }
347 }
348 }
349 return changed;
350 }
352 static LJBits stickyGluing(LJField *p) {
353 LJBits changed = 0;
354 int byColor = (p->gluing == LJGLUING_STICKY_BY_COLOR);
356 for (int y = 0; y < LJ_PF_HT; ++y) {
357 for (int x = p->leftWall; x < p->rightWall - 1; ++x) {
358 int l = p->b[y][x];
359 int r = p->b[y][x + 1];
360 if (l && r
361 && (!(l & CONNECT_R) || !(r & CONNECT_L))
362 && (!byColor || !((l ^ r) & COLOR_MASK))) {
363 p->b[y][x] = l | CONNECT_R;
364 p->b[y][x + 1] = r | CONNECT_L;
365 changed |= 1 << y;
366 }
367 }
368 }
370 for (int y = 0; y < LJ_PF_HT - 1; ++y) {
371 for (int x = p->leftWall; x < p->rightWall; ++x) {
372 int b = p->b[y][x];
373 int t = p->b[y + 1][x];
374 if (b && t
375 && (!(b & CONNECT_U) || !(t & CONNECT_D))
376 && (!byColor || !((b ^ t) & COLOR_MASK))) {
377 p->b[y][x] = b | CONNECT_U;
378 p->b[y + 1][x] = t | CONNECT_D;
379 changed |= 3 << y;
380 }
381 }
382 }
384 return changed;
385 }
387 /**
388 * Locks the current tetromino in the playfield.
389 * @param p the playfield
390 * @return the rows in which the tetromino was placed
391 */
392 static LJBits lockPiece(LJField *p) {
393 LJBits rows = 0;
394 int xBase = p->x;
395 int yBase = ljfixfloor(p->y);
396 LJBlkSpec blocks[4];
397 int piece = p->curPiece[0];
398 expandPieceToBlocks(blocks, p, piece, xBase, yBase, p->theta);
400 p->isSpin = isTspin(p);
402 for (int blk = 0; blk < 4; ++blk) {
403 int blkY = blocks[blk].y;
404 int blkX = blocks[blk].x;
405 int blkValue = blocks[blk].conn;
407 if(blkValue && blkY >= 0 && blkY < LJ_PF_HT) {
408 rows |= 1 << blkY;
409 if (blkX >= p->leftWall && blkX < p->rightWall) {
410 p->b[blkY][blkX] = blkValue;
411 }
412 }
413 }
414 p->sounds |= LJSND_LOCK;
415 p->alreadyHeld = 0;
416 p->nLinesThisPiece = 0;
418 return rows;
419 }
421 void shuffleColumns(LJField *p) {
422 unsigned int permu[LJ_PF_WID];
424 for (int x = p->leftWall; x < p->rightWall; ++x) {
425 permu[x] = x;
426 }
427 for (int x = p->rightWall - 1; x > p->rightWall + 1; --x) {
428 int r = ljRand(p) % x;
429 int t = permu[x];
430 permu[x] = permu[r];
431 permu[r] = t;
432 }
434 for (int y = 0; y < LJ_PF_HT; ++y) {
435 unsigned int blk[LJ_PF_WID];
437 // Copy blocks to temporary buffer, eliminating left and right connections
438 for (int x = p->leftWall; x < p->rightWall; ++x) {
439 blk[x] = p->b[y][permu[x]] & ~(CONNECT_L | CONNECT_R);
440 }
441 for (int x = p->leftWall; x < p->rightWall; ++x) {
442 p->b[y][x] = blk[x];
443 }
444 }
445 }
447 /**
448 * Rotates and shifts a new piece per the rotation system.
449 */
450 static void rotateNewPiece(LJField *p) {
451 const LJRotSystem *rs = rotSystems[p->rotationSystem];
452 int shape = p->curPiece[0] & LJP_MASK;
453 int kickData = rs->entryOffset[shape];
454 p->x += WKX(kickData);
455 p->y += ljitofix(WKY(kickData));
456 p->theta = rs->entryTheta[shape];
458 /* Find extents of piece so that it doesn't enter already collided
459 with the walls (otherwise, in Tengen rotation and well width 4,
460 spawning I causes block out) */
462 LJBlkSpec blocks[4];
463 int minX = LJ_PF_WID - 1, maxX = 0, maxY = 0;
465 expandPieceToBlocks(blocks, p, p->curPiece[0],
466 p->x, ljfixfloor(p->y), p->theta);
467 for (int blk = 0; blk < 4; ++blk) {
468 if (blocks[blk].conn) {
469 if (maxY < blocks[blk].y) {
470 maxY = blocks[blk].y;
471 }
472 if (maxX < blocks[blk].x) {
473 maxX = blocks[blk].x;
474 }
475 if (minX > blocks[blk].x) {
476 minX = blocks[blk].x;
477 }
478 }
479 }
481 if (minX < p->leftWall) {
482 p->x += minX - p->leftWall;
483 } else if (maxX >= p->rightWall) {
484 p->x -= (maxX + 1) - p->rightWall;
485 }
486 if (!p->enterAbove && maxY >= p->ceiling) {
487 p->y -= ljitofix(maxY - p->ceiling) + 1;
488 }
489 }
492 /**
493 * Spawns a tetromino onto the playfield.
494 * @param p the playfield
495 * @param hold 0 for spawning from next; nonzero for swapping with hold
496 * @return the rows that were changed by banana effect
497 */
498 static LJBits newPiece(LJField *p, int hold) {
499 LJBits changed = 0;
500 int initial = p->state != LJS_FALLING
501 && p->state != LJS_LANDED;
502 int ihs = initial && hold;
504 if (hold) {
505 if (p->state == LJS_LANDED && p->lockReset == LJLOCK_SPAWN) {
506 p->speed.lockDelay = p->stateTime;
507 }
508 } else {
509 p->upwardKicks = 0;
510 }
511 p->x = (LJ_PF_WID - 4) / 2;
512 p->dropDist = 0;
513 if (!ihs) {
514 p->state = LJS_FALLING;
515 p->stateTime = 0;
516 }
517 p->isSpin = 0;
518 p->y = ljitofix(p->ceiling - 2);
520 /* Note: The gimmick sets the gravity speed after frame() finishes. */
522 if (hold) {
523 int temp;
525 if (p->holdStyle != LJHOLD_TO_NEXT) {
526 temp = p->holdPiece;
527 p->holdPiece = p->curPiece[ihs];
528 } else {
529 temp = p->curPiece[ihs + 1];
530 p->curPiece[ihs + 1] = p->curPiece[ihs];
531 }
532 p->curPiece[ihs] = temp;
533 p->alreadyHeld = 1;
534 p->sounds |= LJSND_HOLD;
536 // If a negative number was swapped into the current piece,
537 // then there was nothing in the hold space (e.g. at the
538 // beginning of a round). In this case, we'll need to fall
539 // through and generate a new piece.
540 if (temp >= 0) {
541 rotateNewPiece(p);
542 return changed;
543 }
544 }
546 // Shift the next pieces down
547 for (int i = 0; i < LJ_NEXT_PIECES; i++) {
548 p->curPiece[i] = p->curPiece[i + 1];
549 }
550 p->sounds |= LJSND_SPAWN;
552 p->curPiece[LJ_NEXT_PIECES] = randomize(p);
553 ++p->nPieces;
554 if (!p->canRotate) {
555 p->theta = (ljRand(p) >> 12) & 0x03;
556 } else {
557 rotateNewPiece(p);
558 }
560 return changed;
561 }
563 void newGame(LJField *p) {
565 // Clear playfield
566 for (int y = 0; y < LJ_PF_HT; y++) {
567 for(int x = 0; x < LJ_PF_WID; x++) {
568 p->b[y][x] = 0;
569 }
570 }
572 for (int y = 0; y < LJ_MAX_LINES_PER_PIECE; ++y) {
573 p->nLineClears[y] = 0;
574 }
576 if (p->holdStyle == LJHOLD_TNT) {
577 initRandomize(p);
578 p->holdPiece = randomize(p);
579 } else {
580 p->holdPiece = -1; // sentinel for no piece in hold box
581 }
583 // Generate pieces
584 initRandomize(p);
585 for(int i = 0; i < LJ_NEXT_PIECES; i++) {
586 newPiece(p, 0);
587 }
588 p->clearedLines = 0;
589 p->nPieces = 0;
590 p->state = LJS_NEW_PIECE;
591 p->stateTime = 1;
592 p->garbage = 0;
593 p->outGarbage = 0;
594 p->score = 0;
595 p->gameTime = 0;
596 p->activeTime = 0;
597 p->lines = 0;
598 p->alreadyHeld = 0;
599 p->chain = 0;
600 p->theta = 0;
601 p->nLinesThisPiece = 0;
602 p->canRotate = 1;
603 p->speed.entryDelay = 0;
604 p->garbageRandomness = 64;
605 p->reloaded = 0;
606 p->monosquares = 0;
607 p->multisquares = 0;
609 p->garbageX = ljRand(p) % (p->rightWall - p->leftWall)
610 + p->leftWall;
611 }
613 /**
614 * Handles scoring for hard and soft drops.
615 * @return 0 for no change or LJ_DIRTY_SCORE for change
616 */
617 LJBits scoreDropRows(LJField *p, LJFixed gravity, LJFixed newY) {
618 LJBits changed = 0;
619 if (gravity > 0) {
620 int fallDist = ljfixfloor(p->y) - ljfixfloor(newY);
622 p->dropDist += fallDist;
624 // Double scoring for hard drop
625 if (p->dropScoreStyle == LJDROP_1S_2H
626 && gravity >= ljitofix(p->ceiling)) {
627 fallDist *= 2;
628 }
629 if (p->dropScoreStyle == LJDROP_1CELL
630 || p->dropScoreStyle == LJDROP_1S_2H) {
631 p->score += fallDist;
632 changed |= LJ_DIRTY_SCORE;
633 }
635 // Handle scoring for continuous drop
636 if (p->dropScoreStyle == LJDROP_NES
637 && newY <= ljitofix(p->hardDropY)) {
638 p->score += p->dropDist;
639 changed |= LJ_DIRTY_SCORE;
640 p->dropDist = 0;
641 }
642 } else {
643 p->dropDist = 0;
644 }
645 return changed;
646 }
648 /**
649 * Handles gravity.
650 * @param p the playfield
651 * @param gravity amount of additional gravity applied by the player
652 * @param otherKeys other LJI_* keys being pressed by the player
653 * (specifically LJI_LOCK)
654 */
655 static LJBits doPieceGravity(LJField *p, LJFixed gravity, LJBits otherKeys) {
656 int changedRows = 0;
658 LJFixed newY = p->y - gravity - p->speed.gravity;
660 // Check for landed
661 if (newY <= ljitofix(p->hardDropY)) {
662 newY = ljitofix(p->hardDropY);
664 // Downward movement does not result in a T-spin
665 if (ljfixfloor(newY) < ljfixfloor(p->y)) {
666 p->isSpin = 0;
667 }
669 changedRows |= scoreDropRows(p, gravity, newY);
670 p->y = newY;
672 if (p->state == LJS_FALLING) {
673 p->state = LJS_LANDED;
674 p->stateTime = p->speed.lockDelay;
675 p->sounds |= LJSND_LAND;
676 }
677 if (p->stateTime > 0 && !(otherKeys & LJI_LOCK)) {
678 // lock delay > 128 is a special case for don't lock at all
679 if (p->setLockDelay < 128) {
680 --p->stateTime;
681 }
682 } else {
683 LJBits lockRows = lockPiece(p);
684 p->state = LJS_LINES;
685 p->stateTime = 0;
686 changedRows |= lockRows | LJ_DIRTY_NEXT;
688 // LOCK OUT rule: If a piece locks
689 // completely above the ceiling, the game is over.
690 if (!(lockRows & ((1 << p->ceiling) - 1))) {
691 p->state = LJS_GAMEOVER;
692 }
693 }
694 } else {
695 changedRows |= scoreDropRows(p, gravity, newY);
696 p->state = LJS_FALLING;
698 // Downward movement does not result in a T-spin
699 if (ljfixfloor(newY) < ljfixfloor(p->y)) {
700 p->isSpin = 0;
701 }
702 }
703 p->y = newY;
704 return changedRows;
705 }
707 static void updateLockDelayOnMove(LJField *p, int isUpwardKick) {
708 if (p->state == LJS_LANDED) {
710 // if tetromino can move down, go back to falling state;
711 // otherwise, reset lock delay.
712 if (!isCollision(p, p->x, ljfixfloor(p->y) - 1, p->theta)) {
713 p->state = LJS_FALLING;
714 if (p->lockReset == LJLOCK_SPAWN) {
715 p->speed.lockDelay = p->stateTime;
716 }
717 p->stateTime = 0;
718 } else {
719 p->state = LJS_LANDED;
720 if (p->lockReset == LJLOCK_MOVE
721 || (p->lockReset == LJLOCK_STEP && isUpwardKick)) {
722 p->stateTime = p->speed.lockDelay;
723 }
724 }
725 }
726 }
728 static int doRotate(LJField *p,
729 int newDir,
730 const WallKickTable *const pieceTable,
731 int withKicks) {
732 int baseX = p->x;
733 int baseY = ljfixfloor(p->y);
735 withKicks = withKicks ? KICK_TABLE_LEN : 1;
737 // allow specifying null tables for O
738 if (!pieceTable) {
739 if (!isCollision(p, baseX, baseY, newDir)) {
740 p->theta = newDir;
741 p->sounds |= LJSND_ROTATE;
742 return 1;
743 }
744 return 0;
745 }
747 const unsigned char *const table = (*pieceTable)[newDir];
748 int baseKickY = -1000; // sentinel for uninitialized
750 for (int kick = 0; kick < withKicks; kick++) {
751 unsigned int kickData = table[kick];
752 if (kickData == WK_END) {
753 break;
754 } else if (kickData == ARIKA_IF_NOT_CENTER) {
756 // Compute the free space position
757 kickData = table[0];
758 int kickX = WKX(kickData) + baseX;
759 int kickY = WKY(kickData) + baseY;
760 LJBlkSpec blocks[4];
761 int allowed = 0;
763 // If a block other than the center column of this position
764 // is occupied, go to the next step (that is,
765 // allow subsequent kicks)
766 expandPieceToBlocks(blocks, p, p->curPiece[0],
767 kickX, kickY, newDir);
769 for (int blk = 0; blk < 4; ++blk) {
770 if (blocks[blk].conn
771 && blocks[blk].x != baseX + 1
772 && isOccupied(p, blocks[blk].x, blocks[blk].y)) {
773 allowed = 1;
774 break;
775 }
776 }
778 // Otherwise, only blocks of the center column are occupied,
779 // and these cannot kick the piece.
780 if (!allowed) {
781 return 0;
782 }
783 } else {
784 int kickX = WKX(kickData) + baseX;
785 int kickY = WKY(kickData) + baseY;
787 // If this is the first
788 if (baseKickY == -1000) {
789 baseKickY = kickY;
790 }
791 if ((kickY <= baseKickY || p->upwardKicks < p->maxUpwardKicks)
792 && !isCollision(p, kickX, kickY, newDir)) {
793 p->theta = newDir;
794 p->x = kickX;
795 if (kickY > baseKickY) {
796 p->y = ljitofix(kickY);
798 // on the FIRST floor kick of a piece, reset lock delay
799 if (p->upwardKicks == 0) {
800 updateLockDelayOnMove(p, 1);
801 }
802 ++p->upwardKicks;
803 } else if (kickY < baseKickY) {
804 p->y = ljitofix(kickY) + 0xFFFF;
805 } else {
806 p->y = ljitofix(kickY) + (p->y & 0xFFFF);
807 }
808 p->sounds |= LJSND_ROTATE;
809 return 1;
810 }
811 }
812 }
813 return 0;
814 }
817 /**
818 * Tries to rotate the current piece 90 degrees counterclockwise,
819 * using the counterclockwise wall kick tables.
820 * @param p the playfield
821 * @param withKicks nonzero for wall kick, zero for none
822 */
823 static int doRotateLeft(LJField *p, int withKicks) {
824 int newDir = (p->theta + 3) & 0x03;
825 const LJRotSystem *rs = rotSystems[p->rotationSystem];
826 signed int tableNo = rs->kicksL[p->curPiece[0] & LJP_MASK];
827 const WallKickTable *pieceTable = tableNo >= 0
828 ? &(rs->kickTables[tableNo])
829 : NULL;
830 return doRotate(p, newDir, pieceTable, withKicks);
831 }
833 /**
834 * Tries to rotate the current piece 90 degrees clockwise,
835 * using the clockwise wall kick tables.
836 * @param p the playfield
837 * @param withKicks nonzero for wall kick, zero for none
838 */
839 static int doRotateRight(LJField *p, int withKicks) {
840 int newDir = (p->theta + 1) & 0x03;
841 const LJRotSystem *rs = rotSystems[p->rotationSystem];
842 signed int tableNo = rs->kicksR[p->curPiece[0] & LJP_MASK];
843 const WallKickTable *pieceTable = tableNo >= 0
844 ? &(rs->kickTables[tableNo])
845 : NULL;
846 return doRotate(p, newDir, pieceTable, withKicks);
847 }
849 static LJBits checkLines(const LJField *p, LJBits checkRows) {
850 LJBits foundLines = 0;
851 for (int y = 0;
852 y < LJ_PF_HT && checkRows != 0;
853 ++y, checkRows >>= 1) {
854 if (checkRows & 1) {
855 const unsigned char *row = p->b[y];
856 int found = 1;
858 for (int x = p->leftWall; x < p->rightWall && found; ++x) {
859 found = found && (row[x] != 0);
860 }
861 if (found) {
862 foundLines |= 1 << y;
863 }
864 }
865 }
867 return foundLines;
868 }
870 static void fillCLoop(LJField *p, int x, int y, unsigned int src, unsigned int dst)
871 {
872 int fillL, fillR, i;
874 fillL = fillR = x;
875 do {
876 p->c[y][fillL] = dst;
877 fillL--;
878 } while ((fillL >= p->leftWall) && (p->c[y][fillL] == src));
879 fillL++;
881 do {
882 p->c[y][fillR] = dst;
883 fillR++;
884 } while ((fillR < p->rightWall) && (p->c[y][fillR] == src));
885 fillR--;
887 for(i = fillL; i <= fillR; i++)
888 {
889 if(y > 0 && p->c[y - 1][i] == src) {
890 fillCLoop(p, i, y - 1, src, dst);
891 }
892 if(y < LJ_PF_HT - 1 && p->c[y + 1][i] == src) {
893 fillCLoop(p, i, y + 1, src, dst);
894 }
895 }
896 }
899 static void fillC(LJField *p, int x, int y, int dstC) {
900 if (p->c[y][x] != dstC) {
901 fillCLoop(p, x, y, p->c[y][x], dstC);
902 }
903 }
905 /**
906 * Locks the block regions that have landed.
907 * @param p the playfield
908 */
909 void lockLandedRegions(LJField *p) {
910 // Look for regions that are on top of ground regions, where
911 // "ground regions" are any block that is solid and whose region ID is 0.
912 for (int landed = 1; landed != 0; ) {
913 landed = 0;
914 // If something hit the ground, erase its floating bit
915 for (int y = 0; y < LJ_PF_HT; ++y) {
916 for (int x = p->leftWall; x < p->rightWall; ++x) {
917 // If there's a floating block here, and a not-floating block below,
918 // erase this block group's floatiness
919 if (p->c[y][x] &&
920 (y == 0 || (!p->c[y - 1][x] && p->b[y - 1][x]))) {
921 fillC(p, x, y, 0);
922 p->sounds |= LJSND_LAND;
923 landed = 1;
924 }
925 }
926 }
927 }
928 }
930 /**
931 * Separates the playfield into regions that shall fall separately.
932 * @param p the playfield
933 * @param byColors Zero: Touching blocks form a region.
934 * Nonzero: Touching blocks of a single color form a region.
935 */
936 static void stickyMark(LJField *p, int byColors) {
937 for (unsigned int y = 0; y < LJ_PF_HT; ++y) {
938 for (unsigned int x = p->leftWall; x < p->rightWall; ++x) {
939 int blkHere = p->b[y][x] & COLOR_MASK;
941 if (!byColors) {
942 blkHere = blkHere ? 0x10 : 0;
943 }
944 p->c[y][x] = blkHere;
945 }
946 }
948 if (byColors) {
949 lockLandedRegions(p);
950 } else {
951 // mark the bottom row as landed
952 for (unsigned int x = p->leftWall; x < p->rightWall; ++x) {
953 if (p->c[0][x]) {
954 fillC(p, x, 0, 0);
955 }
956 }
957 }
959 //p->stateTime = 5;
960 }
963 /**
964 * Sets the color of a piece to gray/garbage (0x80).
965 * @param x column of a block in the piece
966 * @param y row of a block in the piece
967 * @param rgn the region ID
968 */
969 static void cascadeMarkPiece(LJField *p, int x, int y, int rgn) {
970 int blkHere = p->b[y][x];
972 if (blkHere && !p->c[y][x]) {
973 p->c[y][x] = rgn;
974 if((blkHere & CONNECT_D) && y > 0)
975 cascadeMarkPiece(p, x, y - 1, rgn);
976 if((blkHere & CONNECT_U) && y < LJ_PF_HT - 1)
977 cascadeMarkPiece(p, x, y + 1, rgn);
978 if((blkHere & CONNECT_L) && x > p->leftWall)
979 cascadeMarkPiece(p, x - 1, y, rgn);
980 if((blkHere & CONNECT_R) && x < p->rightWall - 1 )
981 cascadeMarkPiece(p, x + 1, y, rgn);
982 }
983 }
985 static void cascadeMark(LJField *p) {
986 int rgn = 0;
988 for (unsigned int y = 0; y < LJ_PF_HT; ++y) {
989 for (unsigned int x = p->leftWall; x < p->rightWall; ++x) {
990 p->c[y][x] = 0;
991 }
992 }
993 for (unsigned int y = 0; y < LJ_PF_HT; ++y) {
994 for (unsigned int x = p->leftWall; x < p->rightWall; ++x) {
995 cascadeMarkPiece(p, x, y, ++rgn);
996 }
997 }
998 lockLandedRegions(p);
999 //p->stateTime = 5;
1000 }
1002 static void breakEverything(LJField *p) {
1003 for (unsigned int y = 0; y < LJ_PF_HT; ++y) {
1004 for (unsigned int x = p->leftWall; x < p->rightWall; ++x) {
1005 if (p->b[y][x]) {
1006 p->c[y][x] = x + 1;
1007 p->b[y][x] = 0x80;
1008 } else {
1009 p->c[y][x] = 0;
1010 }
1011 }
1012 }
1014 // fill bottom row
1015 for (unsigned int x = x = p->leftWall; x < p->rightWall; ++x) {
1016 if (p->c[0][x]) {
1017 fillC(p, x, 0, 0);
1018 }
1019 }
1020 p->stateTime = 5;
1021 }
1023 /**
1024 * Sets the color of a piece to gray/garbage (0x80).
1025 * @param x column of a block in the piece
1026 * @param y row of a block in the piece
1027 */
1028 static LJBits breakPiece(LJField *p, int x, int y) {
1029 LJBits changed = 0;
1030 int blkHere = p->b[y][x];
1031 int colorHere = blkHere & COLOR_MASK;
1032 int connHere = blkHere & CONNECT_MASK;
1034 if (colorHere != 0x80) {
1035 p->b[y][x] = connHere | 0x80;
1036 changed |= 1 << y;
1037 if((blkHere & CONNECT_D) && y > 0)
1038 changed |= breakPiece(p, x, y - 1);
1039 if((blkHere & CONNECT_U) && y < LJ_PF_HT - 1)
1040 changed |= breakPiece(p, x, y + 1);
1041 if((blkHere & CONNECT_L) && x > p->leftWall)
1042 changed |= breakPiece(p, x - 1, y);
1043 if((blkHere & CONNECT_R) && x < p->rightWall - 1 )
1044 changed |= breakPiece(p, x + 1, y);
1045 }
1046 return changed;
1047 }
1049 /**
1050 * Removes blocks in cleared lines from the playfield and marks
1051 * remaining blocks for gravity.
1052 * @param the lines to be cleared
1053 * @return the rows that were changed
1054 */
1055 static LJBits clearLines(LJField *p, LJBits foundLines) {
1056 LJBits changed = foundLines;
1058 p->clearedLines = foundLines;
1059 if (foundLines != 0) {
1060 p->sounds |= LJSND_LINE;
1061 }
1062 for (int y = 0;
1063 y < LJ_PF_HT && foundLines != 0;
1064 ++y, foundLines >>= 1) {
1065 if (foundLines & 1) {
1067 // In square mode, turn broken pieces (but not 4x4 squares)
1068 // into garbage blocks
1069 if (p->gluing == LJGLUING_SQUARE) {
1070 for (int x = p->leftWall; x < p->rightWall; ++x) {
1071 if (p->b[y][x] < 0x80) {
1072 changed |= breakPiece(p, x, y);
1073 } else if ((p->b[y][x] & (0xF0 | CONNECT_R)) == 0xA0) {
1074 p->score += 500;
1075 changed |= LJ_DIRTY_SCORE;
1076 } else if ((p->b[y][x] & (0xF0 | CONNECT_R)) == 0xB0) {
1077 p->score += 1000;
1078 changed |= LJ_DIRTY_SCORE;
1079 }
1080 }
1081 }
1083 for (int x = p->leftWall; x < p->rightWall; ++x) {
1084 p->b[y][x] = 0;
1085 }
1087 // break connections up and down (like Tengen Tetyais)
1088 if (y > 0) {
1089 for (int x = p->leftWall; x < p->rightWall; ++x) {
1090 p->b[y - 1][x] &= ~CONNECT_U;
1091 }
1092 changed |= 1 << (y - 1);
1093 }
1094 if (y < LJ_PF_HT - 1) {
1095 for (int x = p->leftWall; x < p->rightWall; ++x) {
1096 p->b[y + 1][x] &= ~CONNECT_D;
1097 }
1098 changed |= 1 << (y + 1);
1099 }
1100 }
1101 }
1102 if (p->gluing == LJGLUING_SQUARE && p->isSpin) {
1103 breakEverything(p);
1104 changed |= (1 << LJ_PF_HT) - 1;
1105 } else if (p->clearGravity == LJGRAV_STICKY) {
1106 stickyMark(p, 0);
1107 } else if (p->clearGravity == LJGRAV_STICKY_BY_COLOR) {
1108 stickyMark(p, 1);
1109 } else if (p->clearGravity == LJGRAV_CASCADE) {
1110 cascadeMark(p);
1111 } else {
1112 p->stateTime = 0;
1113 }
1115 return changed;
1116 }
1118 static unsigned int stickyFallLines(LJField *p) {
1119 int minY = LJ_PF_HT;
1121 // Move floating stuff down by one block
1122 for (int y = 1; y < LJ_PF_HT; ++y) {
1123 for (int x = p->leftWall; x < p->rightWall; ++x) {
1124 int c = p->c[y][x];
1125 if (c) {
1126 p->c[y - 1][x] = c;
1127 p->c[y][x] = 0;
1128 p->b[y - 1][x] = p->b[y][x];
1129 p->b[y][x] = 0;
1131 if (minY > y) {
1132 minY = y;
1133 }
1134 }
1135 }
1136 }
1138 // If we're done, skip all the rest
1139 if (minY >= LJ_PF_HT) {
1140 return LJ_PF_HT;
1141 }
1143 lockLandedRegions(p);
1144 return minY - 1;
1145 }
1148 unsigned int bfffo(LJBits rowBits) {
1149 unsigned int lineRow = 0;
1151 if (!rowBits) {
1152 return 32;
1153 }
1154 if ((rowBits & 0xFFFF) == 0) {
1155 rowBits >>= 16;
1156 lineRow += 16;
1157 }
1158 if ((rowBits & 0xFF) == 0) {
1159 rowBits >>= 8;
1160 lineRow += 8;
1161 }
1162 if ((rowBits & 0xF) == 0) {
1163 rowBits >>= 4;
1164 lineRow += 4;
1165 }
1166 if ((rowBits & 0x3) == 0) {
1167 rowBits >>= 2;
1168 lineRow += 2;
1169 }
1170 if ((rowBits & 0x1) == 0) {
1171 rowBits >>= 1;
1172 lineRow += 1;
1173 }
1174 return lineRow;
1175 }
1177 static unsigned int fallLines(LJField *p) {
1178 LJBits rowBits = p->tempRows;
1179 unsigned int lineRow = 0;
1181 if (p->clearGravity != LJGRAV_NAIVE
1182 || (p->gluing == LJGLUING_SQUARE && p->isSpin)) {
1183 return stickyFallLines(p);
1184 }
1186 if (rowBits == 0) {
1187 return LJ_PF_HT;
1188 }
1190 lineRow = bfffo(rowBits);
1191 p->tempRows = (p->tempRows & (-2 << lineRow)) >> 1;
1192 if (!(p->tempRows & (1 << lineRow))) {
1193 p->sounds |= LJSND_LAND;
1194 }
1196 // Move stuff down by 1 row
1197 for (int y = lineRow; y < LJ_PF_HT - 1; ++y) {
1198 unsigned char *row0 = p->b[y];
1199 const unsigned char *row1 = p->b[y + 1];
1200 for (int x = p->leftWall; x < p->rightWall; ++x) {
1201 row0[x] = row1[x];
1202 }
1203 }
1205 // Clear top row
1206 for (int x = p->leftWall; x < p->rightWall; ++x) {
1207 p->b[LJ_PF_HT - 1][x] = 0;
1208 }
1210 return lineRow;
1211 }
1213 /**
1214 * Counts the bits in a bit vector that are true (1).
1215 * @param b a bit vector
1216 * @return the number of 1 bits
1217 */
1218 unsigned int countOnes(LJBits b) {
1219 unsigned int ones = 0;
1221 while(b) {
1222 ++ones;
1223 b &= b - 1;
1224 }
1225 return ones;
1226 }
1228 static unsigned int addGarbage(LJField *p) {
1229 // Move stuff up by 1 row
1230 for (int y = LJ_PF_HT - 2; y >= 0; --y) {
1231 unsigned char *row1 = p->b[y + 1];
1232 const unsigned char *row0 = p->b[y];
1233 for (int x = p->leftWall; x < p->rightWall; ++x) {
1234 row1[x] = row0[x];
1235 }
1236 }
1238 // Garbage in bottom row
1239 for (int x = p->leftWall; x < p->rightWall; ++x) {
1240 p->b[0][x] = 0x80;
1241 }
1243 // Randomize location of garbage hole
1244 int r = (ljRand(p) >> 7) & 0xFF;
1245 int garbageX = (r <= p->garbageRandomness)
1246 ? (ljRand(p) % (p->rightWall - p->leftWall)) + p->leftWall
1247 : p->garbageX;
1248 p->b[0][garbageX] = 0;
1249 p->garbageX = garbageX;
1251 // Horizontally connect the blocks that make up garbage in bottom row
1252 for (int x = p->leftWall; x < p->rightWall - 1; ++x) {
1253 if (p->b[0][x] && p->b[0][x + 1]) {
1254 p->b[0][x] |= CONNECT_R;
1255 p->b[0][x + 1] |= CONNECT_L;
1256 }
1257 }
1259 // Vertically connect the blocks that make up garbage in bottom row
1260 for (int x = p->leftWall; x < p->rightWall; ++x) {
1261 if (p->b[0][x]
1262 && ((p->b[1][x] & COLOR_MASK) == 0x80)) {
1263 p->b[0][x] |= CONNECT_U;
1264 p->b[1][x] |= CONNECT_D;
1265 }
1266 }
1268 return (1 << LJ_PF_VIS_HT) - 1;
1269 }
1271 /**
1272 * Computes the score and outgoing garbage for lines
1273 * and adds them to the player's total.
1274 * @param p The playfield
1275 * @param lines Bit array where 1 means a line clear on this row.
1276 */
1277 void addLinesScore(LJField *p, LJBits lines);
1279 /**
1280 * Things to do just before launching a new piece.
1281 */
1282 void prepareForNewPiece(LJField *p) {
1283 int nLines = p->nLinesThisPiece;
1285 // Add to number of clears of each number of lines.
1286 int idx;
1288 if (p->clearGravity == LJGRAV_NAIVE) {
1289 // In naive gravity, T-spin single, double, and triple counts
1290 // are stored in 5-row, 6-row, and 7-row slots, and T-spins
1291 // that clear 0 lines are not counted.
1292 idx = (nLines > 4)
1293 ? 4
1294 : nLines;
1296 if (nLines >= 1 && p->isSpin) {
1297 idx += 4;
1298 }
1299 } else {
1300 idx = (nLines > LJ_MAX_LINES_PER_PIECE)
1301 ? LJ_MAX_LINES_PER_PIECE
1302 : nLines;
1303 }
1305 if (nLines < 4 && !p->isSpin && p->garbageStyle == LJGARBAGE_HRDERBY) {
1306 p->garbage += nLines;
1307 }
1309 ljAssert(p,
1310 idx <= LJ_MAX_LINES_PER_PIECE,
1311 "Number of lines cleared with last piece out of bounds in prepareForNewPiece");
1312 if (idx > 0) {
1313 p->nLineClears[idx - 1] += 1;
1314 }
1316 p->state = LJS_NEW_PIECE;
1317 p->stateTime = p->speed.entryDelay;
1318 }
1320 LJBits frame(LJField *p, const LJInput *in) {
1321 LJBits changedRows = 0;
1322 LJBits tempRows;
1323 int distance;
1324 int moved = 0;
1325 int isFirstFrame = (p->sounds & (LJSND_SPAWN | LJSND_HOLD))
1326 ? 1 : 0;
1328 p->sounds = 0;
1330 // Make hold work at ANY time.
1331 if ((in->other & LJI_HOLD)
1332 && p->holdStyle != LJHOLD_NONE
1333 && !p->alreadyHeld) {
1334 changedRows |= newPiece(p, 1) | LJ_DIRTY_NEXT;
1335 updHardDropY(p);
1336 }
1338 switch(p->state) {
1339 case LJS_NEW_PIECE:
1340 if (p->garbage > 0) {
1341 changedRows |= addGarbage(p);
1342 --p->garbage;
1343 break;
1344 }
1346 // ARE
1347 if (p->stateTime > 0) {
1348 --p->stateTime;
1349 }
1350 if (p->stateTime > 0) {
1351 break;
1352 }
1354 changedRows |= newPiece(p, 0);
1355 updHardDropY(p);
1356 changedRows |= LJ_DIRTY_NEXT;
1358 /* If the piece spawns over blocks, this is a "block out" and a
1359 loss under most rules. But skip checking it now so that
1360 the player can IRS out of block out. */
1362 break;
1364 // the following executes for both falling and landed
1365 case LJS_FALLING:
1366 case LJS_LANDED:
1367 ++p->activeTime;
1368 if (p->canRotate) {
1369 int oldX = p->x;
1370 int oldY = ljfixfloor(p->y);
1371 distance = in->rotation;
1372 for(; distance < 0; ++distance) {
1374 // 0.43: Do not apply wall kicks on the first frame (IRS)
1375 if (doRotateLeft(p, !isFirstFrame)) {
1376 moved = 1;
1378 // isSpin == 1: twist in place
1379 // isSpin == 2: twist with kick
1380 // if (p->tSpinAlgo == LJTS_TDS_NO_KICK)
1381 // then only isSpin == 1 is worth points.
1382 if (p->x == oldX && ljfixfloor(p->y) == oldY) {
1383 p->isSpin = 1;
1384 } else {
1385 p->isSpin = 2;
1386 }
1387 } else {
1388 break;
1389 }
1390 }
1391 for(; distance > 0; --distance) {
1392 if (doRotateRight(p, !isFirstFrame)) {
1393 moved = 1;
1394 if (p->x == oldX && ljfixfloor(p->y) == oldY) {
1395 p->isSpin = 1;
1396 } else {
1397 p->isSpin = 2;
1398 }
1399 } else {
1400 break;
1401 }
1402 }
1403 }
1405 /* If the piece spawns over blocks, this is a "block out" and a
1406 loss under most rules. Check it now, after rotation, so that
1407 the player can IRS out of block out. */
1408 if (isFirstFrame
1409 && isCollision(p, p->x, ljfixfloor(p->y), p->theta)) {
1410 changedRows |= lockPiece(p);
1411 p->state = LJS_GAMEOVER;
1412 }
1414 distance = in->movement;
1415 for(; distance < 0; ++distance) {
1416 if (!isCollision(p, p->x - 1, ljfixfloor(p->y), p->theta)) {
1417 --p->x;
1418 p->sounds |= LJSND_SHIFT;
1419 moved = 1;
1420 p->isSpin = 0;
1421 }
1422 }
1423 for(; distance > 0; --distance) {
1424 if (!isCollision(p, p->x + 1, ljfixfloor(p->y), p->theta)) {
1425 ++p->x;
1426 p->sounds |= LJSND_SHIFT;
1427 moved = 1;
1428 p->isSpin = 0;
1429 }
1430 }
1431 updHardDropY(p);
1432 if (p->state != LJS_GAMEOVER) {
1433 if (moved) {
1434 updateLockDelayOnMove(p, 0);
1435 }
1436 tempRows = doPieceGravity(p, ljitofix(in->gravity) >> 3, in->other);
1437 p->tempRows = tempRows;
1438 changedRows |= tempRows;
1439 }
1441 // At this point, if the piece locked,
1442 // p->tempRows holds the rows in which the piece landed.
1443 break;
1445 case LJS_LINES:
1446 if (p->stateTime > 0) {
1447 --p->stateTime;
1448 }
1449 if (p->stateTime > 0) {
1450 break;
1451 }
1452 if (p->gluing == LJGLUING_SQUARE) {
1453 LJBits gluedRows = findSquares(p, 0);
1454 gluedRows |= findSquares(p, 1);
1455 changedRows |= gluedRows;
1456 if (gluedRows) {
1458 // When a 4x4 block square is formed, a delay
1459 // equal to the line delay is added.
1460 p->stateTime += p->speed.lineDelay;
1461 break;
1462 }
1463 } else if (p->gluing == LJGLUING_STICKY
1464 || p->gluing == LJGLUING_STICKY_BY_COLOR) {
1465 changedRows |= stickyGluing(p);
1466 }
1468 // At this point, p->tempRows holds the rows in which
1469 // a line could possibly have been made.
1470 tempRows = p->tempRows;
1471 tempRows = checkLines(p, tempRows);
1472 p->tempRows = tempRows;
1473 // At this point, p->tempRows holds the rows in which
1474 // a line HAS been made.
1475 addLinesScore(p, tempRows);
1476 changedRows |= LJ_DIRTY_SCORE;
1478 // At this point, p->tempRows holds the rows in which a line
1479 // HAS been made.
1480 p->clearedLines = tempRows;
1481 if (!tempRows) {
1482 prepareForNewPiece(p);
1483 break;
1484 }
1486 changedRows |= clearLines(p, tempRows);
1488 p->state = LJS_LINES_FALLING;
1489 p->stateTime += p->speed.lineDelay;
1490 break;
1492 case LJS_LINES_FALLING:
1493 if (p->stateTime > 0) {
1494 --p->stateTime;
1495 }
1496 if (p->stateTime > 0) {
1497 break;
1498 }
1499 moved = fallLines(p);
1500 if (moved >= LJ_PF_HT) {
1501 p->state = LJS_LINES;
1502 p->tempRows = (1 << LJ_PF_HT) - 1;
1503 }
1504 changedRows |= (~0 << moved) & ((1 << LJ_PF_VIS_HT) - 1);
1505 break;
1507 default:
1508 break;
1510 }
1512 ++p->gameTime;
1513 return changedRows;
1514 }