view src/gimmicks.c @ 0:c84446dfb3f5

initial add
author paulo@localhost
date Fri, 13 Mar 2009 00:39:12 -0700
parents
children
line source
1 /* Gimmick code for LOCKJAW, an implementation of the Soviet Mind Game
3 Copyright (C) 2006-2007 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.
23 */
25 #include "lj.h"
26 #include "ljcontrol.h"
28 void initSpeed(LJField *p); // in speed.c
29 void setSpeed(LJField *p, LJControl *c); // in speed.c
30 int updLevelAfterPiece(LJField *p); // in speed.c
31 int updLevelAfterLines(LJField *p, size_t nLines); // in speed.c
33 void initGimmicks(LJField *p) {
34 initSpeed(p);
35 p->speed.entryDelay = 0; /* new pieces will increase this */
36 p->speed.lineDelay = 0;
37 if (p->garbageStyle == LJGARBAGE_DRILL
38 || p->gimmick == LJGM_DRILL) {
39 p->garbageRandomness = 255;
40 p->garbage = p->ceiling - 2;
41 } else if (p->garbageStyle == LJGARBAGE_ZIGZAG) {
42 setupZigzagField(p, p->ceiling * 3 / 4);
43 }
45 }
47 LJBits gimmicks(LJField *p, LJControl *c) {
48 LJBits changed = 0;
50 // In rhythm, lock the tetromino if the player
51 // isn't keeping up with the rate
52 if (p->speedState.curve == LJSPD_RHYTHM
53 || p->speedState.curve == LJSPD_RHYTHMZERO) {
54 p->bpmCounter += p->speedState.level;
56 if (p->bpmCounter >= 0) {
57 if (p->state == LJS_LANDED) {
58 p->stateTime = 0;
59 } else if (p->state == LJS_FALLING) {
60 p->speed.gravity = ljitofix(LJ_PF_HT);
61 }
62 }
64 // In rhythm, increase BPM periodically
65 p->speedupCounter += p->speedState.level;
66 if(p->speedupCounter >= 60 * 60 * 64) {
67 p->speedupCounter -= 60 * 60 * 64;
68 p->speedState.level += 10;
69 p->sounds |= LJSND_SECTIONUP;
70 }
71 }
73 // For each piece, set the entry and line delays.
74 // Don't set it twice when spawning to replace the
75 // first held piece (both LJSND_SPAWN and LJSND_HOLD
76 // on the same piece).
77 if ((p->sounds & (LJSND_SPAWN | LJSND_HOLD))
78 == LJSND_SPAWN) {
79 setSpeed(p, c);
80 }
82 if (p->sounds & LJSND_LOCK) {
83 if (p->gimmick == LJGM_ITEMS) {
84 if (p->nPieces >= 7) {
85 p->canRotate = 0;
86 p->speed.gravity = ljitofix(1);
87 }
88 }
90 // Garbage in simulated multiplayer
91 int simGarbage = p->nPieces >= 7
92 && p->garbageStyle >= LJGARBAGE_1
93 && p->garbageStyle <= LJGARBAGE_4
94 && (p->curPiece[1] == LJP_I
95 || ((p->pieceSet == LJRAND_SZ
96 || p->pieceSet == LJRAND_JLOSTZ)
97 && p->nPieces % 7 == 3));
98 if (simGarbage) {
99 p->garbage += p->garbageStyle;
100 }
102 // Banana attack in "Vs. with Items" gimmick
103 if (p->gimmick == LJGM_ITEMS
104 && (ljRand(p) & 0xF00) == 0xF00) {
105 shuffleColumns(p);
106 changed |= (1 << LJ_PF_HT) - 1;
107 }
108 }
109 return changed;
110 }
114 const char hotlineRows[LJ_PF_HT] =
115 {
116 0, 0, 0, 0, 1,
117 0, 0, 0, 0, 2,
118 0, 0, 0, 3, 0,
119 0, 4, 0, 5, 6,
120 0, 0, 0, 0
121 };
123 static const char garbageScoreTable[] = { 0, 0, 1, 2, 4 };
124 static const char tSpinGarbageScoreTable[] = { 0, 2, 4, 6, 6 };
125 static const unsigned char squareScoreTable[] =
126 {1, 1, 1, 2, 3, 5, 8, 13,
127 21, 34, 55, 89, 144, 200};
129 static const char tdsScoreTable[] = {0, 1, 3, 5, 8};
130 static const char tdsTSScoreTable[] = {4, 8, 12, 16};
131 static const char nesScoreTable[] = {0, 4, 10, 30, 120};
134 /**
135 * Computes the score and outgoing garbage for lines
136 * and adds them to the player's total.
137 * @param p The playfield
138 * @param lines Bit array where 1 means a line clear on this row.
139 */
140 void addLinesScore(LJField *p, LJBits lines) {
141 const int nLines = countOnes(lines);
142 int oldLines = p->nLinesThisPiece;
143 int tdsSection = p->lines / 10 + 1;
145 p->lines += nLines;
146 if (updLevelAfterLines(p, nLines)) {
147 p->sounds |= LJSND_SECTIONUP;
148 }
150 switch (p->scoreStyle) {
152 case LJSCORE_TNT64:
153 if (nLines < 1) {
154 return;
155 }
156 for (int i = nLines; i > 0; --i) {
157 if (oldLines > sizeof(squareScoreTable) - 1) {
158 oldLines = sizeof(squareScoreTable) - 1;
159 }
160 p->score += 100 * squareScoreTable[oldLines++];
161 } break;
163 case LJSCORE_NES:
164 {
165 int garbageLevel = (nLines > 4) ? 4 : nLines;
166 int value = nesScoreTable[garbageLevel];
168 p->score += 10 * tdsSection * value;
169 if (nLines >= 4) {
170 p->sounds |= LJSND_SETB2B;
171 }
172 } break;
174 case LJSCORE_TDS:
175 {
176 // Garbage based scoring system.
177 int garbageLevel = (nLines > 4) ? 4 : nLines;
178 int chain, value;
180 if (p->isSpin) {
181 chain = (nLines >= 1);
182 value = tdsTSScoreTable[garbageLevel];
184 // TDS gives fewer points for a T-spin single
185 // that involves a wall kick.
186 if (p->isSpin == 2 && nLines < 2) {
187 value >>= 2;
188 }
189 } else {
190 chain = (nLines >= 4);
191 value = tdsScoreTable[garbageLevel];
192 }
193 if (chain && p->chain && nLines >= 1) { // b2b
194 value = value * 3 / 2;
195 }
197 if (tdsSection > 20) {
198 tdsSection = 20;
199 }
200 p->score += 100 * tdsSection * value;
201 if (nLines >= 1) {
202 if (chain) {
203 p->sounds |= LJSND_SETB2B;
204 if (p->chain) {
205 p->sounds |= LJSND_B2B;
206 }
207 }
208 p->chain = chain;
209 }
210 } break;
212 case LJSCORE_LJ:
213 case LJSCORE_LJ_NERF:
214 if (nLines >= 1) {
215 // Garbage based scoring system.
216 int garbageLevel = (nLines > 4) ? 4 : nLines;
217 unsigned int chain, garbage;
219 if (p->isSpin) {
220 chain = (nLines >= 1);
221 garbage = tSpinGarbageScoreTable[garbageLevel];
222 if (p->scoreStyle == LJSCORE_LJ_NERF) {
223 garbage /= 2;
224 }
225 garbage += (chain && p->chain);
226 } else {
227 chain = (nLines >= 4);
228 garbage = garbageScoreTable[garbageLevel]
229 + (chain && p->chain);
230 }
231 p->score += 100 * nLines + 200 * garbage;
232 p->outGarbage += garbage;
233 if (chain) {
234 p->sounds |= LJSND_SETB2B;
235 if (p->chain) {
236 p->sounds |= LJSND_B2B;
237 }
238 }
239 p->chain = chain;
240 } break;
242 case LJSCORE_HOTLINE:
243 for (int y = 0; y < 24; ++y) {
244 if (lines & (1 << y)) {
245 p->score += 100 * hotlineRows[y];
246 }
247 } break;
249 }
251 p->nLinesThisPiece += nLines;
252 }
254 /**
255 * Puts a zigzag pattern of garbage into the playfield.
256 * Useful as a test suite for calcZigzagGrade() in debrief.c.
257 * @param p the playfield to set up
258 * @param height the number of rows to set up
259 */
260 void setupZigzagField(LJField *p, size_t height) {
261 unsigned int hole = p->leftWall;
262 int delta = 1;
264 if (height > p->ceiling) {
265 height = p->ceiling;
266 }
268 // set up example pattern
269 for (size_t y = 0; y < height; ++y) {
270 for (size_t x = p->leftWall; x < p->rightWall; ++x) {
272 // A block should be in all cells of the row
273 // except for the hole cell, which should be empty.
274 if (x == hole) {
275 p->b[y][x] = 0;
276 } else {
277 p->b[y][x] = 0x80;
278 }
279 }
281 if (hole == p->rightWall - 1) {
282 delta = -1;
283 } else if (hole == p->leftWall) {
284 delta = 1;
285 }
286 hole += delta;
287 }
289 // clear rows above pattern
290 for (size_t y = height; y < LJ_PF_HT; ++y) {
291 for (size_t x = p->leftWall; x < p->rightWall; ++x) {
292 p->b[y][x] = 0;
293 }
294 }
295 }