Mercurial > hg > index.fcgi > lj > lj046-2players
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 } |