rev |
line source |
paulo@0
|
1 /* PC frontend for LOCKJAW, an implementation of the Soviet Mind Game
|
paulo@0
|
2
|
paulo@0
|
3 Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net>
|
paulo@0
|
4
|
paulo@0
|
5 This work is free software; you can redistribute it and/or modify
|
paulo@0
|
6 it under the terms of the GNU General Public License as published by
|
paulo@0
|
7 the Free Software Foundation; either version 2 of the License, or
|
paulo@0
|
8 (at your option) any later version.
|
paulo@0
|
9
|
paulo@0
|
10 This program is distributed in the hope that it will be useful,
|
paulo@0
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
paulo@0
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
paulo@0
|
13 GNU General Public License for more details.
|
paulo@0
|
14
|
paulo@0
|
15 You should have received a copy of the GNU General Public License
|
paulo@0
|
16 along with this program; if not, write to the Free Software
|
paulo@0
|
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
paulo@0
|
18
|
paulo@0
|
19 Original game concept and design by Alexey Pajitnov.
|
paulo@0
|
20 The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg,
|
paulo@0
|
21 or The Tetris Company LLC.
|
paulo@0
|
22
|
paulo@0
|
23 */
|
paulo@0
|
24 #include "ljplay.h"
|
paulo@0
|
25 #include "options.h"
|
paulo@0
|
26 #include <stdio.h>
|
paulo@0
|
27 #ifndef NO_DATETIME
|
paulo@0
|
28 #include <time.h>
|
paulo@0
|
29 #endif
|
paulo@0
|
30
|
paulo@0
|
31 #define USING_DEBRIEF_PAGE 1
|
paulo@0
|
32 #define MIN_RECTUM_ROWS 4
|
paulo@0
|
33 #define MIN_ZIGZAG_ROWS 2
|
paulo@0
|
34
|
paulo@0
|
35 // On newlib, use a variant of sprintf that doesn't handle floating
|
paulo@0
|
36 // point formats. On other C libraries, use standard sprintf.
|
paulo@0
|
37 #ifdef HAS_FPU
|
paulo@0
|
38 #define siprintf sprintf
|
paulo@0
|
39 #ifndef HAS_FOPEN
|
paulo@0
|
40 #define HAS_FOPEN
|
paulo@0
|
41 #endif
|
paulo@0
|
42 #define DEBRIEF_TO_STDOUT
|
paulo@0
|
43 #endif
|
paulo@0
|
44
|
paulo@0
|
45 #ifdef HAS_FOPEN
|
paulo@0
|
46 #include "ljpath.h" // for ljfopen
|
paulo@0
|
47 #endif
|
paulo@0
|
48
|
paulo@0
|
49 const char *const debriefBoolNames[2] = {
|
paulo@0
|
50 "Off", "On"
|
paulo@0
|
51 };
|
paulo@0
|
52
|
paulo@0
|
53 typedef struct TimeResultMSF {
|
paulo@0
|
54 unsigned int pieces_100m;
|
paulo@0
|
55 unsigned int garbage_100m;
|
paulo@0
|
56 unsigned int minutes;
|
paulo@0
|
57 unsigned char seconds;
|
paulo@0
|
58 unsigned char frames;
|
paulo@0
|
59 } TimeResultMSF;
|
paulo@0
|
60
|
paulo@0
|
61 void frames2msf(unsigned int gameTime,
|
paulo@0
|
62 unsigned int nPieces,
|
paulo@0
|
63 unsigned int outGarbage,
|
paulo@0
|
64 TimeResultMSF *out) {
|
paulo@0
|
65 unsigned int gameSeconds = gameTime / 60U;
|
paulo@0
|
66 unsigned int gameMinutes = gameSeconds / 60U;
|
paulo@0
|
67 out->pieces_100m = gameTime
|
paulo@0
|
68 ? 360000ULL * nPieces / gameTime
|
paulo@0
|
69 : 0;
|
paulo@0
|
70 out->garbage_100m = gameTime
|
paulo@0
|
71 ? 360000ULL * outGarbage / gameTime
|
paulo@0
|
72 : 0;
|
paulo@0
|
73 out->minutes = gameMinutes;
|
paulo@0
|
74 out->seconds = gameSeconds - gameMinutes * 60U;
|
paulo@0
|
75 out->frames = gameTime - gameSeconds * 60U;
|
paulo@0
|
76 }
|
paulo@0
|
77
|
paulo@0
|
78 static unsigned int countBlocksLeft(const LJField *p) {
|
paulo@0
|
79 unsigned int n = 0;
|
paulo@0
|
80
|
paulo@0
|
81 for (int y = 0; y < LJ_PF_HT; ++y) {
|
paulo@0
|
82 for (int x = 0; x < LJ_PF_WID; ++x) {
|
paulo@0
|
83 if (p->b[y][x]) {
|
paulo@0
|
84 ++n;
|
paulo@0
|
85 }
|
paulo@0
|
86 }
|
paulo@0
|
87 }
|
paulo@0
|
88 return n;
|
paulo@0
|
89 }
|
paulo@0
|
90
|
paulo@0
|
91 /**
|
paulo@0
|
92 * Calculates the number of rows starting at the bottom that
|
paulo@0
|
93 * conform to a zigzag pattern.
|
paulo@0
|
94 * @param p the playfield to test
|
paulo@0
|
95 * @return the number of rows successfully completed
|
paulo@0
|
96 */
|
paulo@0
|
97 unsigned int calcZigzagGrade(const LJField *p) {
|
paulo@0
|
98 unsigned int hole = p->leftWall;
|
paulo@0
|
99 int delta = 1;
|
paulo@0
|
100
|
paulo@0
|
101 for (size_t y = 0; y < p->ceiling; ++y) {
|
paulo@0
|
102
|
paulo@0
|
103 // invariant:
|
paulo@0
|
104 // at this point, y equals the number of rows known to conform
|
paulo@0
|
105 for (size_t x = p->leftWall; x < p->rightWall; ++x) {
|
paulo@0
|
106 unsigned int blk = p->b[y][x];
|
paulo@0
|
107
|
paulo@0
|
108 // A block should be in all cells of the row
|
paulo@0
|
109 // except for the hole cell, which should be empty.
|
paulo@0
|
110 if (x == hole) {
|
paulo@0
|
111
|
paulo@0
|
112 // if there's a block where a hole should be, this row
|
paulo@0
|
113 // doesn't conform, but all the rows below do conform
|
paulo@0
|
114 if (blk) {
|
paulo@0
|
115 return y;
|
paulo@0
|
116 }
|
paulo@0
|
117 } else {
|
paulo@0
|
118
|
paulo@0
|
119 // if there's a hole where a block should be, this row
|
paulo@0
|
120 // doesn't conform, but all the rows below do conform
|
paulo@0
|
121 if (!blk) {
|
paulo@0
|
122 return y;
|
paulo@0
|
123 }
|
paulo@0
|
124 }
|
paulo@0
|
125 }
|
paulo@0
|
126
|
paulo@0
|
127 // if this hole isn't covered up on the next row,
|
paulo@0
|
128 // this row doesn't conform either
|
paulo@0
|
129 // changed in 0.43 after a clarification from Kitaru
|
paulo@0
|
130 if (!p->b[y + 1][hole]) {
|
paulo@0
|
131 return y;
|
paulo@0
|
132 }
|
paulo@0
|
133
|
paulo@0
|
134 // by now we know that the row conforms,
|
paulo@0
|
135 // so move the hole for the next row
|
paulo@0
|
136 if (hole == p->rightWall - 1) {
|
paulo@0
|
137 delta = -1;
|
paulo@0
|
138 } else if (hole == p->leftWall) {
|
paulo@0
|
139 delta = 1;
|
paulo@0
|
140 }
|
paulo@0
|
141 hole += delta;
|
paulo@0
|
142
|
paulo@0
|
143 }
|
paulo@0
|
144 return p->ceiling;
|
paulo@0
|
145 }
|
paulo@0
|
146
|
paulo@0
|
147
|
paulo@0
|
148 /**
|
paulo@0
|
149 * Calculates the number of rows in a playfield that were prepared
|
paulo@0
|
150 * for an I tetromino.
|
paulo@0
|
151 * Rule for this pattern:
|
paulo@0
|
152 * 1. Only one hole on the bottom row.
|
paulo@0
|
153 * 2. This column must be unoccupied below the ceiling.
|
paulo@0
|
154 * 3. Conforming rows must be full except for the hole.
|
paulo@0
|
155 *
|
paulo@0
|
156 * Easy test procedure for this pattern:
|
paulo@0
|
157 * Level 4 garbage
|
paulo@0
|
158 * @return the number of rows that conform to constraint 3, or 0 if the
|
paulo@0
|
159 * field does not conform to constraint 1 or 2.
|
paulo@0
|
160 */
|
paulo@0
|
161 unsigned int calcRectumGrade(const LJField *p) {
|
paulo@0
|
162 unsigned int hole = LJ_PF_WID;
|
paulo@0
|
163
|
paulo@0
|
164 // search for the first hole on the bottom row
|
paulo@0
|
165 for (unsigned int x = p->leftWall;
|
paulo@0
|
166 x < p->rightWall;
|
paulo@0
|
167 ++x) {
|
paulo@0
|
168 if (!p->b[0][x]) {
|
paulo@0
|
169 hole = x;
|
paulo@0
|
170 break;
|
paulo@0
|
171 }
|
paulo@0
|
172 }
|
paulo@0
|
173
|
paulo@0
|
174 // If there is no hole in the bottom row, then 0 rows conform.
|
paulo@0
|
175 // This shouldn't happen in standard smg because the line would be
|
paulo@0
|
176 // cleared, but eventually LJ will support games other than SMG,
|
paulo@0
|
177 // and checking the array bounds is O(1), so why not?
|
paulo@0
|
178 if (hole >= p->rightWall) {
|
paulo@0
|
179 return 0;
|
paulo@0
|
180 }
|
paulo@0
|
181
|
paulo@0
|
182 // make sure that the row is clear through the whole visible
|
paulo@0
|
183 // portion of the playfield
|
paulo@0
|
184 for (unsigned int y = 0; y < p->ceiling; ++y) {
|
paulo@0
|
185
|
paulo@0
|
186 // If this column isn't empty, the whole field doesn't conform.
|
paulo@0
|
187 if (p->b[y][hole]) {
|
paulo@0
|
188 return 0;
|
paulo@0
|
189 }
|
paulo@0
|
190 }
|
paulo@0
|
191
|
paulo@0
|
192 for (unsigned int y = 0; y < p->ceiling; ++y) {
|
paulo@0
|
193
|
paulo@0
|
194 // At this point, the bottom y rows conform.
|
paulo@0
|
195 for (unsigned int x = p->leftWall; x < p->rightWall; ++x) {
|
paulo@0
|
196
|
paulo@0
|
197 // Disregarding the hole column, if there's an empty cell
|
paulo@0
|
198 // in this row, then this row does not conform.
|
paulo@0
|
199 if (x != hole && !p->b[y][x]) {
|
paulo@0
|
200 return y;
|
paulo@0
|
201 }
|
paulo@0
|
202 }
|
paulo@0
|
203 }
|
paulo@0
|
204 return p->ceiling;
|
paulo@0
|
205 }
|
paulo@0
|
206
|
paulo@0
|
207 /**
|
paulo@0
|
208 * Converts a rank number (0-19) to a text rank.
|
paulo@0
|
209 * 0-9: 10-1 kyu; 10-99: 1-90 dan; 100+: grandmaster
|
paulo@0
|
210 * @return number of characters written
|
paulo@0
|
211 */
|
paulo@0
|
212 static size_t printSecretGrade(char *dst, int rank) {
|
paulo@0
|
213 if (rank >= 100) {
|
paulo@0
|
214 return siprintf(dst, "GM");
|
paulo@0
|
215 } else if (rank >= 10) {
|
paulo@0
|
216 return siprintf(dst, "S%d", rank - 9);
|
paulo@0
|
217 } else {
|
paulo@0
|
218 return siprintf(dst, "%d", 10 - rank);
|
paulo@0
|
219 }
|
paulo@0
|
220 }
|
paulo@0
|
221
|
paulo@0
|
222 static size_t printFracG(char *dst, unsigned int value) {
|
paulo@0
|
223 if (value == 0) {
|
paulo@0
|
224 return siprintf(dst, "Instant");
|
paulo@0
|
225 } else if (value == 1) {
|
paulo@0
|
226 return siprintf(dst, "1G");
|
paulo@0
|
227 } else {
|
paulo@0
|
228 return siprintf(dst, "1/%dG", value);
|
paulo@0
|
229 }
|
paulo@0
|
230 }
|
paulo@0
|
231
|
paulo@0
|
232 #include <string.h> // remove once fourcc transition is complete
|
paulo@0
|
233 static size_t printFourCC(char *dst, FourCC f) {
|
paulo@0
|
234 const char *data = ljGetFourCCName(f);
|
paulo@0
|
235 if (data) {
|
paulo@0
|
236 int pos = 0;
|
paulo@0
|
237 for(pos = 0; *data; ++pos) {
|
paulo@0
|
238 dst[pos] = *data++;
|
paulo@0
|
239 }
|
paulo@0
|
240 return pos;
|
paulo@0
|
241 } else {
|
paulo@0
|
242 int pos = 0;
|
paulo@0
|
243 for(pos = 0; f.c[pos] && pos < 4; ++pos) {
|
paulo@0
|
244 dst[pos] = f.c[pos];
|
paulo@0
|
245 }
|
paulo@0
|
246 return pos;
|
paulo@0
|
247 }
|
paulo@0
|
248 }
|
paulo@0
|
249
|
paulo@0
|
250 size_t buildOptionsReportPage(char *dst, const LJView *v) {
|
paulo@0
|
251 size_t pos = 0;
|
paulo@0
|
252
|
paulo@0
|
253
|
paulo@0
|
254 #if USING_DEBRIEF_PAGE
|
paulo@0
|
255 const LJField *p = v->field;
|
paulo@0
|
256
|
paulo@0
|
257 pos += siprintf(dst + pos,
|
paulo@0
|
258 "Options\n\n"
|
paulo@0
|
259 "Well: %dx%d%s, Enter: %s\n"
|
paulo@0
|
260 "Speed: ",
|
paulo@0
|
261 p->rightWall - p->leftWall,
|
paulo@0
|
262 p->ceiling,
|
paulo@0
|
263 v->hidePF ? " invisible" : "",
|
paulo@0
|
264 p->enterAbove ? "Above" : "Below");
|
paulo@0
|
265 pos += printFourCC(dst + pos,
|
paulo@0
|
266 optionsSpeedCurveNames[(size_t)p->speedState.curve]);
|
paulo@0
|
267 pos += siprintf(dst + pos,
|
paulo@0
|
268 ", ARE: %d ms\n"
|
paulo@0
|
269 "Randomizer: ",
|
paulo@0
|
270 v->field->areStyle * 50 / 3);
|
paulo@0
|
271 pos += printFourCC(dst + pos,
|
paulo@0
|
272 optionsRandNames[(size_t)p->randomizer]);
|
paulo@0
|
273 pos += siprintf(dst + pos, " of ");
|
paulo@0
|
274 pos += printFourCC(dst + pos,
|
paulo@0
|
275 optionsPieceSetNames[(size_t)p->pieceSet]);
|
paulo@0
|
276 pos += siprintf(dst + pos, "\nHold: ");
|
paulo@0
|
277 pos += printFourCC(dst + pos,
|
paulo@0
|
278 optionsHoldStyleNames[(size_t)p->holdStyle]);
|
paulo@0
|
279 pos += siprintf(dst + pos, ", Rotation: ");
|
paulo@0
|
280 pos += printFourCC(dst + pos,
|
paulo@0
|
281 optionsRotNames[(size_t)p->rotationSystem]);
|
paulo@0
|
282 if (p->maxUpwardKicks < 20) {
|
paulo@0
|
283 pos += siprintf(dst + pos,
|
paulo@0
|
284 " %d FK",
|
paulo@0
|
285 (int)p->maxUpwardKicks);
|
paulo@0
|
286 }
|
paulo@0
|
287 if (v->control->initialRotate) {
|
paulo@0
|
288 pos += siprintf(dst + pos, "+initial");
|
paulo@0
|
289 }
|
paulo@0
|
290
|
paulo@0
|
291 pos += siprintf(dst + pos,
|
paulo@0
|
292 "\nLock: ");
|
paulo@0
|
293 if (p->setLockDelay >= 128) {
|
paulo@0
|
294 pos += siprintf(dst + pos, "Never");
|
paulo@0
|
295 } else {
|
paulo@0
|
296 if (p->setLockDelay > 0) {
|
paulo@0
|
297 pos += siprintf(dst + pos,
|
paulo@0
|
298 "%d ms ",
|
paulo@0
|
299 p->setLockDelay * 50 / 3);
|
paulo@0
|
300 }
|
paulo@0
|
301 pos += printFourCC(dst + pos,
|
paulo@0
|
302 optionsLockdownNames[(size_t)p->lockReset]);
|
paulo@0
|
303 }
|
paulo@0
|
304 pos += siprintf(dst + pos,
|
paulo@0
|
305 ", Deep: %s\n",
|
paulo@0
|
306 debriefBoolNames[p->bottomBlocks]);
|
paulo@0
|
307
|
paulo@0
|
308 pos += siprintf(dst + pos,
|
paulo@0
|
309 "Line clear: %d ms ",
|
paulo@0
|
310 p->speed.lineDelay * 50 / 3);
|
paulo@0
|
311 pos += printFourCC(dst + pos,
|
paulo@0
|
312 optionsGravNames[(size_t)p->clearGravity]);
|
paulo@0
|
313 pos += siprintf(dst + pos,
|
paulo@0
|
314 ", Gluing: ");
|
paulo@0
|
315 pos += printFourCC(dst + pos,
|
paulo@0
|
316 optionsGluingNames[(size_t)p->gluing]);
|
paulo@0
|
317 pos += siprintf(dst + pos,
|
paulo@0
|
318 "\nDrop score: ");
|
paulo@0
|
319 pos += printFourCC(dst + pos,
|
paulo@0
|
320 optionsDropScoringNames[(size_t)p->dropScoreStyle]);
|
paulo@0
|
321 pos += siprintf(dst + pos,
|
paulo@0
|
322 ", T-spin: ");
|
paulo@0
|
323 pos += printFourCC(dst + pos,
|
paulo@0
|
324 optionsTspinNames[(size_t)p->tSpinAlgo]);
|
paulo@0
|
325 pos += siprintf(dst + pos,
|
paulo@0
|
326 "\nGarbage: ");
|
paulo@0
|
327 pos += printFourCC(dst + pos,
|
paulo@0
|
328 optionsGarbageNames[(size_t)p->garbageStyle]);
|
paulo@0
|
329 pos += siprintf(dst + pos,
|
paulo@0
|
330 ", DAS: %d ms ",
|
paulo@0
|
331 v->control->dasDelay * 50 / 3);
|
paulo@0
|
332 pos += printFracG(dst + pos, v->control->dasSpeed);
|
paulo@0
|
333 pos += siprintf(dst + pos,
|
paulo@0
|
334 "\nSoft drop: ");
|
paulo@0
|
335 pos += printFracG(dst + pos, v->control->softDropSpeed + 1);
|
paulo@0
|
336 dst[pos++] = ' ';
|
paulo@0
|
337 pos += printFourCC(dst + pos,
|
paulo@0
|
338 optionsZangiNames[v->control->softDropLock]);
|
paulo@0
|
339 pos += siprintf(dst + pos,
|
paulo@0
|
340 ", Hard drop: ");
|
paulo@0
|
341 pos += printFourCC(dst + pos,
|
paulo@0
|
342 optionsZangiNames[v->control->hardDropLock]);
|
paulo@0
|
343 pos += siprintf(dst + pos, "\nShadow: ");
|
paulo@0
|
344 pos += printFourCC(dst + pos,
|
paulo@0
|
345 optionsShadowNames[v->hideShadow]);
|
paulo@0
|
346 pos += siprintf(dst + pos,
|
paulo@0
|
347 ", Next: %d\n",
|
paulo@0
|
348 v->nextPieces);
|
paulo@0
|
349 #else
|
paulo@0
|
350 pos += siprintf(dst, "\n\nDebrief disabled.\n");
|
paulo@0
|
351 #endif
|
paulo@0
|
352
|
paulo@0
|
353 dst[pos] = 0;
|
paulo@0
|
354 return pos;
|
paulo@0
|
355 }
|
paulo@0
|
356
|
paulo@0
|
357 #ifdef DEBRIEF_SHORT
|
paulo@0
|
358 static const char *const naiveGravity[8] = {
|
paulo@0
|
359 "1L", "2L", "3L", "4L",
|
paulo@0
|
360 "T1", "T2", "T3"
|
paulo@0
|
361 };
|
paulo@0
|
362 static const char *const cascadeGravity[8] = {
|
paulo@0
|
363 "1L", "2L", "3L", "4L",
|
paulo@0
|
364 "5L", "6L", "7L", "8L+"
|
paulo@0
|
365 };
|
paulo@0
|
366 #else
|
paulo@0
|
367 static const char *const naiveGravity[8] = {
|
paulo@0
|
368 "single", "double", "triple", "home run",
|
paulo@0
|
369 "T single", "T double", "T triple"
|
paulo@0
|
370 };
|
paulo@0
|
371 static const char *const cascadeGravity[8] = {
|
paulo@0
|
372 "single", "double", "triple", "quad",
|
paulo@0
|
373 "5L", "6L", "7L", "8L+"
|
paulo@0
|
374 };
|
paulo@0
|
375 #endif
|
paulo@0
|
376
|
paulo@0
|
377 static size_t printLineCounts(char *dst, const LJField *p) {
|
paulo@0
|
378 size_t pos = 0;
|
paulo@0
|
379 const char *const *names = (p->clearGravity == LJGRAV_NAIVE)
|
paulo@0
|
380 ? naiveGravity : cascadeGravity;
|
paulo@0
|
381 dst[pos++] = '(';
|
paulo@0
|
382 for (int i = 0; i < 8; ++i) {
|
paulo@0
|
383 if (names[i]) {
|
paulo@0
|
384 pos += siprintf(dst + pos,
|
paulo@0
|
385 "%s%s: %u",
|
paulo@0
|
386 i ? "; " : "",
|
paulo@0
|
387 names[i],
|
paulo@0
|
388 p->nLineClears[i]);
|
paulo@0
|
389 }
|
paulo@0
|
390 }
|
paulo@0
|
391 dst[pos++] = ')';
|
paulo@0
|
392 dst[pos++] = '\n';
|
paulo@0
|
393 return pos;
|
paulo@0
|
394 }
|
paulo@0
|
395
|
paulo@0
|
396
|
paulo@0
|
397 size_t buildDebriefPage(char *dst, const LJView *v) {
|
paulo@0
|
398 size_t pos = 0;
|
paulo@0
|
399
|
paulo@0
|
400 #if USING_DEBRIEF_PAGE
|
paulo@0
|
401 #ifndef NO_DATETIME
|
paulo@0
|
402 const time_t finishTimeUNIX = time(NULL);
|
paulo@0
|
403 const struct tm *finishTime = localtime(&finishTimeUNIX);
|
paulo@0
|
404 char finishTimeStr[64];
|
paulo@0
|
405
|
paulo@0
|
406 /* I would have used
|
paulo@0
|
407 strftime(finishTimeStr, sizeof(finishTimeStr), "%Y-%m-%d at %H:%M", finishTime);
|
paulo@0
|
408 but it brings in the floating-point library on GBA/DS. */
|
paulo@0
|
409 siprintf(finishTimeStr,
|
paulo@0
|
410 "%04d-%02d-%02d at %02d:%02d",
|
paulo@0
|
411 finishTime->tm_year + 1900,
|
paulo@0
|
412 finishTime->tm_mon + 1, finishTime->tm_mday,
|
paulo@0
|
413 finishTime->tm_hour, finishTime->tm_min);
|
paulo@0
|
414 #endif
|
paulo@0
|
415
|
paulo@0
|
416 const LJField *p = v->field;
|
paulo@0
|
417 unsigned long int keys_100m = p->nPieces
|
paulo@0
|
418 ? 100U * v->control->presses / p->nPieces
|
paulo@0
|
419 : 666;
|
paulo@0
|
420 TimeResultMSF gameMSF, activeMSF;
|
paulo@0
|
421 const char *wordPieces = (p->nPieces != 1) ? "tetrominoes" : "tetromino";
|
paulo@0
|
422 if (p->pieceSet == LJRAND_234BLK) {
|
paulo@0
|
423 wordPieces = (p->nPieces != 1) ? "pieces" : "piece";
|
paulo@0
|
424 }
|
paulo@0
|
425
|
paulo@0
|
426 /* Secret grades */
|
paulo@0
|
427 unsigned int nBlocksLeft = countBlocksLeft(p);
|
paulo@0
|
428 unsigned int nZigzagRows = calcZigzagGrade(p);
|
paulo@0
|
429 unsigned int nRectumRows = calcRectumGrade(p);
|
paulo@0
|
430
|
paulo@0
|
431 pos += siprintf(dst + pos,
|
paulo@0
|
432 "Result:\n\n%s ",
|
paulo@0
|
433 v->control->countdown <= 0 ? "Cleared" : "Stopped");
|
paulo@0
|
434 pos += printFourCC(dst + pos, gimmickNames[p->gimmick]);
|
paulo@0
|
435 pos += siprintf(dst + pos,
|
paulo@0
|
436 " at level %d\n",
|
paulo@0
|
437 p->speedState.level);
|
paulo@0
|
438 #if !defined(NO_DATETIME) || defined(WITH_REPLAY)
|
paulo@0
|
439 #ifndef NO_DATETIME
|
paulo@0
|
440 pos += siprintf(dst + pos, "on %s", finishTimeStr);
|
paulo@0
|
441 #endif
|
paulo@0
|
442 #ifdef WITH_REPLAY
|
paulo@0
|
443 if (p->reloaded) {
|
paulo@0
|
444 pos += siprintf(dst + pos, " from saved state");
|
paulo@0
|
445 }
|
paulo@0
|
446 #endif
|
paulo@0
|
447 dst[pos++] = '\n';
|
paulo@0
|
448 #endif
|
paulo@0
|
449
|
paulo@0
|
450 frames2msf(p->gameTime, p->nPieces, p->outGarbage, &gameMSF);
|
paulo@0
|
451 frames2msf(p->activeTime, p->nPieces, p->outGarbage, &activeMSF);
|
paulo@0
|
452
|
paulo@0
|
453 pos += siprintf(dst + pos,
|
paulo@0
|
454 "Played %d %s in %u:%02u.%02u (%u.%02u/min)\n",
|
paulo@0
|
455 p->nPieces,
|
paulo@0
|
456 wordPieces,
|
paulo@0
|
457 gameMSF.minutes,
|
paulo@0
|
458 (unsigned int)gameMSF.seconds,
|
paulo@0
|
459 gameMSF.frames * 5U / 3U,
|
paulo@0
|
460 (unsigned int) (gameMSF.pieces_100m / 100),
|
paulo@0
|
461 (unsigned int) (gameMSF.pieces_100m % 100));
|
paulo@0
|
462 pos += siprintf(dst + pos,
|
paulo@0
|
463 "(active time only: %u:%02u.%02u, %u.%02u/min)\n",
|
paulo@0
|
464 activeMSF.minutes,
|
paulo@0
|
465 (unsigned int)activeMSF.seconds,
|
paulo@0
|
466 activeMSF.frames * 5U / 3U,
|
paulo@0
|
467 (unsigned int) (activeMSF.pieces_100m / 100),
|
paulo@0
|
468 (unsigned int) (activeMSF.pieces_100m % 100));
|
paulo@0
|
469 pos += siprintf(dst + pos,
|
paulo@0
|
470 "Pressed %u keys (%u.%02u/piece)\n\n",
|
paulo@0
|
471 v->control->presses,
|
paulo@0
|
472 (unsigned int) (keys_100m / 100),
|
paulo@0
|
473 (unsigned int) (keys_100m % 100));
|
paulo@0
|
474
|
paulo@0
|
475 pos += siprintf(dst + pos,
|
paulo@0
|
476 "Made %u lines",
|
paulo@0
|
477 p->lines);
|
paulo@0
|
478 if (p->gluing == LJGLUING_SQUARE) {
|
paulo@0
|
479 pos += siprintf(dst + pos,
|
paulo@0
|
480 " and %u pure + %u squares",
|
paulo@0
|
481 p->monosquares, p->multisquares);
|
paulo@0
|
482 }
|
paulo@0
|
483 dst[pos++] = '\n';
|
paulo@0
|
484 pos += printLineCounts(dst + pos, p);
|
paulo@0
|
485 pos += siprintf(dst + pos,
|
paulo@0
|
486 "Sent %u garbage (%u.%02u per minute)\n",
|
paulo@0
|
487 p->outGarbage,
|
paulo@0
|
488 (unsigned int) (gameMSF.garbage_100m / 100),
|
paulo@0
|
489 (unsigned int) (gameMSF.garbage_100m % 100));
|
paulo@0
|
490 if (nZigzagRows >= MIN_ZIGZAG_ROWS) {
|
paulo@0
|
491 pos += siprintf(dst + pos,
|
paulo@0
|
492 "Made %u rows of > for grade ",
|
paulo@0
|
493 nZigzagRows);
|
paulo@0
|
494 pos += printSecretGrade(dst + pos,
|
paulo@0
|
495 nZigzagRows >= 19 ? 100 : nZigzagRows);
|
paulo@0
|
496 } else if (nRectumRows >= MIN_RECTUM_ROWS) {
|
paulo@0
|
497 pos += siprintf(dst + pos,
|
paulo@0
|
498 "Made %u-row rectum for grade ",
|
paulo@0
|
499 nRectumRows);
|
paulo@0
|
500 pos += printSecretGrade(dst + pos,
|
paulo@0
|
501 nRectumRows >= 20 ? 100 : nRectumRows - 1);
|
paulo@0
|
502 } else if (nBlocksLeft >= 110 && nBlocksLeft <= 111) {
|
paulo@0
|
503 pos += siprintf(dst + pos,
|
paulo@0
|
504 "Secret grade: Eleventy%s",
|
paulo@0
|
505 (nBlocksLeft & 1) ? "-one" : "");
|
paulo@0
|
506 } else {
|
paulo@0
|
507 pos += siprintf(dst + pos,
|
paulo@0
|
508 "Left %d blocks behind",
|
paulo@0
|
509 nBlocksLeft);
|
paulo@0
|
510 }
|
paulo@0
|
511 dst[pos++] = '\n';
|
paulo@0
|
512 dst[pos++] = '\n';
|
paulo@0
|
513 pos += printFourCC(dst + pos, optionsScoringNames[p->scoreStyle]);
|
paulo@0
|
514
|
paulo@0
|
515 pos += siprintf(dst + pos,
|
paulo@0
|
516 " score: %d\n",
|
paulo@0
|
517 p->score);
|
paulo@0
|
518
|
paulo@0
|
519 /*
|
paulo@0
|
520 tod stats not in lj
|
paulo@0
|
521
|
paulo@0
|
522 points/line, 40 lines score, silver squares, gold squares
|
paulo@0
|
523
|
paulo@0
|
524 */
|
paulo@0
|
525
|
paulo@0
|
526 #else
|
paulo@0
|
527 pos += siprintf(dst, "\n\nDebrief disabled. Score: %u\n", v->field->score);
|
paulo@0
|
528 #endif
|
paulo@0
|
529 return pos;
|
paulo@0
|
530 }
|
paulo@0
|
531
|
paulo@0
|
532 void debriefDrawPage(const char *page, size_t pageNumber);
|
paulo@0
|
533 LJBits debriefHandleKeys(void);
|
paulo@0
|
534 extern volatile char redrawWholeScreen;
|
paulo@0
|
535
|
paulo@0
|
536
|
paulo@0
|
537 /**
|
paulo@0
|
538 * Reports the player's performance.
|
paulo@0
|
539 */
|
paulo@0
|
540 void debrief(LJView *v) {
|
paulo@0
|
541 char pageData[2000]; // current length is under 1000 chars
|
paulo@0
|
542 char *page[2] = {pageData, "Page coming soon!"};
|
paulo@0
|
543 int curPage = 0;
|
paulo@0
|
544
|
paulo@0
|
545 {
|
paulo@0
|
546 size_t pos;
|
paulo@0
|
547
|
paulo@0
|
548 pos = buildDebriefPage(page[0], v);
|
paulo@0
|
549 page[1] = page[0] + (++pos);
|
paulo@0
|
550 pos += buildOptionsReportPage(page[1], v);
|
paulo@0
|
551 }
|
paulo@0
|
552 #ifdef HAS_FOPEN
|
paulo@0
|
553 FILE *logFile = ljfopen("lj-scores.txt", "at");
|
paulo@0
|
554 if (logFile) {
|
paulo@0
|
555 fputs("\n\n\n", logFile);
|
paulo@0
|
556 fputs(page[0], logFile);
|
paulo@0
|
557 fputs(page[1], logFile);
|
paulo@0
|
558 #ifdef DEBRIEF_TO_STDOUT
|
paulo@0
|
559 fputs(page[0], stdout);
|
paulo@0
|
560 fputs(page[1], stdout);
|
paulo@0
|
561 #endif
|
paulo@0
|
562 fclose(logFile);
|
paulo@0
|
563 }
|
paulo@0
|
564 #endif
|
paulo@0
|
565
|
paulo@0
|
566 LJBits lastKeys = ~0;
|
paulo@0
|
567 redrawWholeScreen = 1;
|
paulo@0
|
568 debriefHandleKeys(); // call once to clear key buffer
|
paulo@0
|
569
|
paulo@0
|
570 for (;;) {
|
paulo@0
|
571 LJBits sounds = 0;
|
paulo@0
|
572
|
paulo@0
|
573 if (redrawWholeScreen) {
|
paulo@0
|
574 redrawWholeScreen = 0;
|
paulo@0
|
575 debriefDrawPage(page[curPage], curPage);
|
paulo@0
|
576 }
|
paulo@0
|
577 LJBits keys = debriefHandleKeys();
|
paulo@0
|
578 LJBits newKeys = keys & ~lastKeys;
|
paulo@0
|
579
|
paulo@0
|
580 if (newKeys & VKEY_LEFT) {
|
paulo@0
|
581 if (curPage > 0) {
|
paulo@0
|
582 curPage -= 1;
|
paulo@0
|
583 redrawWholeScreen = 1;
|
paulo@0
|
584 sounds |= LJSND_HOLD;
|
paulo@0
|
585 }
|
paulo@0
|
586 }
|
paulo@0
|
587
|
paulo@0
|
588 if (newKeys & VKEY_RIGHT) {
|
paulo@0
|
589 if (curPage + 1 < sizeof(page) / sizeof(page[0])) {
|
paulo@0
|
590 curPage += 1;
|
paulo@0
|
591 redrawWholeScreen = 1;
|
paulo@0
|
592 sounds |= LJSND_HOLD;
|
paulo@0
|
593 }
|
paulo@0
|
594 }
|
paulo@0
|
595
|
paulo@0
|
596 if (newKeys & (VKEY_ROTR | VKEY_ROTL)) {
|
paulo@0
|
597 break;
|
paulo@0
|
598 } else {
|
paulo@0
|
599 lastKeys = keys;
|
paulo@0
|
600 }
|
paulo@0
|
601 playSoundEffects(v, sounds, 100);
|
paulo@0
|
602 }
|
paulo@0
|
603 }
|