comparison src/gimmicks.c @ 0:c84446dfb3f5

initial add
author paulo@localhost
date Fri, 13 Mar 2009 00:39:12 -0700
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:2752665fbc9c
1 /* Gimmick code for LOCKJAW, an implementation of the Soviet Mind Game
2
3 Copyright (C) 2006-2007 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 #include "lj.h"
26 #include "ljcontrol.h"
27
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
32
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 }
44
45 }
46
47 LJBits gimmicks(LJField *p, LJControl *c) {
48 LJBits changed = 0;
49
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;
55
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 }
63
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 }
72
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 }
81
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 }
89
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 }
101
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 }
111
112
113
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 };
122
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};
128
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};
132
133
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;
144
145 p->lines += nLines;
146 if (updLevelAfterLines(p, nLines)) {
147 p->sounds |= LJSND_SECTIONUP;
148 }
149
150 switch (p->scoreStyle) {
151
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;
162
163 case LJSCORE_NES:
164 {
165 int garbageLevel = (nLines > 4) ? 4 : nLines;
166 int value = nesScoreTable[garbageLevel];
167
168 p->score += 10 * tdsSection * value;
169 if (nLines >= 4) {
170 p->sounds |= LJSND_SETB2B;
171 }
172 } break;
173
174 case LJSCORE_TDS:
175 {
176 // Garbage based scoring system.
177 int garbageLevel = (nLines > 4) ? 4 : nLines;
178 int chain, value;
179
180 if (p->isSpin) {
181 chain = (nLines >= 1);
182 value = tdsTSScoreTable[garbageLevel];
183
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 }
196
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;
211
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;
218
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;
241
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;
248
249 }
250
251 p->nLinesThisPiece += nLines;
252 }
253
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;
263
264 if (height > p->ceiling) {
265 height = p->ceiling;
266 }
267
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) {
271
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 }
280
281 if (hole == p->rightWall - 1) {
282 delta = -1;
283 } else if (hole == p->leftWall) {
284 delta = 1;
285 }
286 hole += delta;
287 }
288
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 }