comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:b12f6ce100f0
1 /* Engine of LOCKJAW, an implementation of the Soviet Mind Game
2
3 Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net>
4
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
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.
22
23
24 */
25
26 #define LJ_INTERNAL
27 #include "lj.h"
28
29 unsigned int ljRand(LJField *p) {
30 p->seed = p->seed * 2147001325 + 715136305;
31 return p->seed >> 17;
32 }
33
34 static inline void ljAssert(LJField *p, int shouldBeTrue, const char *reason) {
35 if (!shouldBeTrue) {
36 p->state = LJS_GAMEOVER;
37 }
38 }
39
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 };
52
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 };
65
66 const char pieceColors[N_PIECE_SHAPES] = {
67 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
68 0x40, 0x10, 0x20
69 };
70
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 };
83
84 static inline int pieceToFieldBlock(int piece, int conn) {
85 return conn | pieceColors[piece];
86 }
87
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;
94
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 }
105
106 const char *xBl = xShapes[shape][theta];
107 const char *yBl = yShapes[shape][theta];
108
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 }
122
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 }
130
131 int isCollision(const LJField *p, int x, int y, int theta) {
132 LJBlkSpec blocks[4];
133 int piece = p->curPiece[0];
134
135 expandPieceToBlocks(blocks, p, piece, x, y, theta);
136
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 }
144
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;
154
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:
164
165 // If t-spin involved wall kick, don't count it
166 if (p->isSpin == 2) {
167 return 0;
168 }
169
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 }
176
177 // 2. Last move was spin
178 if (!p->isSpin) {
179 return 0;
180 }
181
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 }
198
199 // 3. Last move was spin
200 return p->isSpin;
201 default:
202 return 0;
203 }
204 }
205
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;
218
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 }
234
235
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;
246
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 }
266
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];
272
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 }
286
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;
301
302 if(i == 0)
303 c = baseBlk & ~CONNECT_D;
304 else if(i == 3)
305 c = baseBlk & ~CONNECT_U;
306 else
307 c = baseBlk;
308
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 }
314
315 if (isMulti) {
316 ++p->multisquares;
317 } else {
318 ++p->monosquares;
319 }
320
321 return 0x0F << y;
322 }
323
324
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;
334
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];
338
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 }
351
352 static LJBits stickyGluing(LJField *p) {
353 LJBits changed = 0;
354 int byColor = (p->gluing == LJGLUING_STICKY_BY_COLOR);
355
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 }
369
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 }
383
384 return changed;
385 }
386
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);
399
400 p->isSpin = isTspin(p);
401
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;
406
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;
417
418 return rows;
419 }
420
421 void shuffleColumns(LJField *p) {
422 unsigned int permu[LJ_PF_WID];
423
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 }
433
434 for (int y = 0; y < LJ_PF_HT; ++y) {
435 unsigned int blk[LJ_PF_WID];
436
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 }
446
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];
457
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) */
461
462 LJBlkSpec blocks[4];
463 int minX = LJ_PF_WID - 1, maxX = 0, maxY = 0;
464
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 }
480
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 }
490
491
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;
503
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);
519
520 /* Note: The gimmick sets the gravity speed after frame() finishes. */
521
522 if (hold) {
523 int temp;
524
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;
535
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 }
545
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;
551
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 }
559
560 return changed;
561 }
562
563 void newGame(LJField *p) {
564
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 }
571
572 for (int y = 0; y < LJ_MAX_LINES_PER_PIECE; ++y) {
573 p->nLineClears[y] = 0;
574 }
575
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 }
582
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;
608
609 p->garbageX = ljRand(p) % (p->rightWall - p->leftWall)
610 + p->leftWall;
611 }
612
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);
621
622 p->dropDist += fallDist;
623
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 }
634
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 }
647
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;
657
658 LJFixed newY = p->y - gravity - p->speed.gravity;
659
660 // Check for landed
661 if (newY <= ljitofix(p->hardDropY)) {
662 newY = ljitofix(p->hardDropY);
663
664 // Downward movement does not result in a T-spin
665 if (ljfixfloor(newY) < ljfixfloor(p->y)) {
666 p->isSpin = 0;
667 }
668
669 changedRows |= scoreDropRows(p, gravity, newY);
670 p->y = newY;
671
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;
687
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;
697
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 }
706
707 static void updateLockDelayOnMove(LJField *p, int isUpwardKick) {
708 if (p->state == LJS_LANDED) {
709
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 }
727
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);
734
735 withKicks = withKicks ? KICK_TABLE_LEN : 1;
736
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 }
746
747 const unsigned char *const table = (*pieceTable)[newDir];
748 int baseKickY = -1000; // sentinel for uninitialized
749
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) {
755
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;
762
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);
768
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 }
777
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;
786
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);
797
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 }
815
816
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 }
832
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 }
848
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;
857
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 }
866
867 return foundLines;
868 }
869
870 static void fillCLoop(LJField *p, int x, int y, unsigned int src, unsigned int dst)
871 {
872 int fillL, fillR, i;
873
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++;
880
881 do {
882 p->c[y][fillR] = dst;
883 fillR++;
884 } while ((fillR < p->rightWall) && (p->c[y][fillR] == src));
885 fillR--;
886
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 }
897
898
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 }
904
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 }
929
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;
940
941 if (!byColors) {
942 blkHere = blkHere ? 0x10 : 0;
943 }
944 p->c[y][x] = blkHere;
945 }
946 }
947
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 }
958
959 //p->stateTime = 5;
960 }
961
962
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];
971
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 }
984
985 static void cascadeMark(LJField *p) {
986 int rgn = 0;
987
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 }
1001
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 }
1013
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 }
1022
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;
1033
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 }
1048
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;
1057
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) {
1066
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 }
1082
1083 for (int x = p->leftWall; x < p->rightWall; ++x) {
1084 p->b[y][x] = 0;
1085 }
1086
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 }
1114
1115 return changed;
1116 }
1117
1118 static unsigned int stickyFallLines(LJField *p) {
1119 int minY = LJ_PF_HT;
1120
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;
1130
1131 if (minY > y) {
1132 minY = y;
1133 }
1134 }
1135 }
1136 }
1137
1138 // If we're done, skip all the rest
1139 if (minY >= LJ_PF_HT) {
1140 return LJ_PF_HT;
1141 }
1142
1143 lockLandedRegions(p);
1144 return minY - 1;
1145 }
1146
1147
1148 unsigned int bfffo(LJBits rowBits) {
1149 unsigned int lineRow = 0;
1150
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 }
1176
1177 static unsigned int fallLines(LJField *p) {
1178 LJBits rowBits = p->tempRows;
1179 unsigned int lineRow = 0;
1180
1181 if (p->clearGravity != LJGRAV_NAIVE
1182 || (p->gluing == LJGLUING_SQUARE && p->isSpin)) {
1183 return stickyFallLines(p);
1184 }
1185
1186 if (rowBits == 0) {
1187 return LJ_PF_HT;
1188 }
1189
1190 lineRow = bfffo(rowBits);
1191 p->tempRows = (p->tempRows & (-2 << lineRow)) >> 1;
1192 if (!(p->tempRows & (1 << lineRow))) {
1193 p->sounds |= LJSND_LAND;
1194 }
1195
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 }
1204
1205 // Clear top row
1206 for (int x = p->leftWall; x < p->rightWall; ++x) {
1207 p->b[LJ_PF_HT - 1][x] = 0;
1208 }
1209
1210 return lineRow;
1211 }
1212
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;
1220
1221 while(b) {
1222 ++ones;
1223 b &= b - 1;
1224 }
1225 return ones;
1226 }
1227
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 }
1237
1238 // Garbage in bottom row
1239 for (int x = p->leftWall; x < p->rightWall; ++x) {
1240 p->b[0][x] = 0x80;
1241 }
1242
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;
1250
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 }
1258
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 }
1267
1268 return (1 << LJ_PF_VIS_HT) - 1;
1269 }
1270
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);
1278
1279 /**
1280 * Things to do just before launching a new piece.
1281 */
1282 void prepareForNewPiece(LJField *p) {
1283 int nLines = p->nLinesThisPiece;
1284
1285 // Add to number of clears of each number of lines.
1286 int idx;
1287
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;
1295
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 }
1304
1305 if (nLines < 4 && !p->isSpin && p->garbageStyle == LJGARBAGE_HRDERBY) {
1306 p->garbage += nLines;
1307 }
1308
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 }
1315
1316 p->state = LJS_NEW_PIECE;
1317 p->stateTime = p->speed.entryDelay;
1318 }
1319
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;
1327
1328 p->sounds = 0;
1329
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 }
1337
1338 switch(p->state) {
1339 case LJS_NEW_PIECE:
1340 if (p->garbage > 0) {
1341 changedRows |= addGarbage(p);
1342 --p->garbage;
1343 break;
1344 }
1345
1346 // ARE
1347 if (p->stateTime > 0) {
1348 --p->stateTime;
1349 }
1350 if (p->stateTime > 0) {
1351 break;
1352 }
1353
1354 changedRows |= newPiece(p, 0);
1355 updHardDropY(p);
1356 changedRows |= LJ_DIRTY_NEXT;
1357
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. */
1361
1362 break;
1363
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) {
1373
1374 // 0.43: Do not apply wall kicks on the first frame (IRS)
1375 if (doRotateLeft(p, !isFirstFrame)) {
1376 moved = 1;
1377
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 }
1404
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 }
1413
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 }
1440
1441 // At this point, if the piece locked,
1442 // p->tempRows holds the rows in which the piece landed.
1443 break;
1444
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) {
1457
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 }
1467
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;
1477
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 }
1485
1486 changedRows |= clearLines(p, tempRows);
1487
1488 p->state = LJS_LINES_FALLING;
1489 p->stateTime += p->speed.lineDelay;
1490 break;
1491
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;
1506
1507 default:
1508 break;
1509
1510 }
1511
1512 ++p->gameTime;
1513 return changedRows;
1514 }
1515
1516