Mercurial > hg > index.fcgi > lj > lj046
comparison src/ljpc.c @ 3:17286938e22a
change DS alt. rotate key to rotate twice
author | paulo@localhost |
---|---|
date | Wed, 08 Apr 2009 21:50:13 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:18f0b4d2549c |
---|---|
1 /* PC frontend for LOCKJAW, an implementation of the Soviet Mind Game | |
2 | |
3 Copyright (C) 2006-2008 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 "ljpc.h" | |
26 #include "ljreplay.h" | |
27 #include <jpgalleg.h> | |
28 #include <time.h> | |
29 #include "scenario.h" | |
30 #include "ljpath.h" | |
31 | |
32 #if 1 | |
33 #define LJ_VERSION "0.46a ("__DATE__")" | |
34 #else | |
35 #define LJ_VERSION "WIP ("__DATE__")" | |
36 #endif | |
37 | |
38 #define USE_PICS_FOLDER 0 | |
39 #define MAX_PLAYERS 2 | |
40 #define LJ_TICK_RATE 3600 // frames per minute | |
41 | |
42 int bgColor, fgColor = 0, hiliteColor, pfBgColor = 0, pfFgColor; | |
43 static volatile int curTime = 0; | |
44 volatile char redrawWholeScreen = 1; | |
45 static volatile int wantPause = 0; | |
46 const DATAFILE *dat = NULL, *sound_dat = NULL; | |
47 const FONT *aver32 = NULL, *aver16 = NULL; | |
48 char withSound = 0; | |
49 char autoPause; | |
50 int refreshRate = 0; | |
51 char demoFilename[512]; | |
52 | |
53 int withPics = -1; | |
54 | |
55 char skinName[PATH_MAX] = "default.skin"; | |
56 char ljblocksSRSName[PATH_MAX]; | |
57 char ljblocksSegaName[PATH_MAX]; | |
58 char ljconnSRSName[PATH_MAX]; | |
59 char ljconnSegaName[PATH_MAX]; | |
60 char ljbgName[PATH_MAX]; | |
61 char menubgName[PATH_MAX]; | |
62 char bgmName[PATH_MAX]; | |
63 char bgmRhythmName[PATH_MAX]; | |
64 unsigned long int bgmLoopPoint = 0; | |
65 int bgmReadyGo = 0; | |
66 int bgmVolume = 128; | |
67 int skinW = 800; | |
68 int skinH = 600; | |
69 | |
70 | |
71 | |
72 | |
73 static void incCurTime(void) { | |
74 ++curTime; | |
75 } END_OF_FUNCTION(incCurTime); | |
76 | |
77 static void amnesia(void) { | |
78 redrawWholeScreen = 1; | |
79 } END_OF_FUNCTION(amnesia); | |
80 | |
81 static void requestPause(void) { | |
82 wantPause |= autoPause; | |
83 } | |
84 | |
85 | |
86 int getTime(void) { | |
87 return curTime; | |
88 } | |
89 | |
90 void yieldCPU(void) { | |
91 rest(5); | |
92 } | |
93 | |
94 void ljBeginDraw(LJView *v, int sync) { | |
95 vsync(); | |
96 if (redrawWholeScreen) { | |
97 redrawWholeScreen = 0; | |
98 playRedrawScreen(v); | |
99 } | |
100 | |
101 BITMAP *sc = v->plat->skin->fullback; | |
102 | |
103 // Draw shape | |
104 blit(v->plat->skin->bg, sc, 0, 0, 0, 0, 48, 16); | |
105 if (v->control->replayDst) { | |
106 | |
107 // circle: record | |
108 circlefill(sc, 8, 8, 7, fgColor); | |
109 textout_ex(sc, aver16, "Rec", 18, 2, fgColor, -1); | |
110 } else if (v->control->replaySrc) { | |
111 | |
112 // triangle: play | |
113 for (int wid = 0; wid < 7; ++wid) { | |
114 hline(sc, 2, 2 + wid, wid * 2 + 3, fgColor); | |
115 hline(sc, 2, 14 - wid, wid * 2 + 3, fgColor); | |
116 } | |
117 textout_ex(sc, aver16, "Play", 18, 2, fgColor, -1); | |
118 } else { | |
119 | |
120 // square: stop | |
121 rectfill(sc, 2, 2, 14, 14, fgColor); | |
122 } | |
123 blit(sc, screen, 0, 0, 0, 0, 48, 16); | |
124 } | |
125 | |
126 void ljEndDraw(LJView *v) { | |
127 | |
128 } | |
129 | |
130 static void startRecording(LJView *v) { | |
131 withPics = -1; | |
132 | |
133 // close existing playback | |
134 if (v->control->replaySrc) { | |
135 replayClose(v->control->replaySrc); | |
136 v->control->replaySrc = NULL; | |
137 } | |
138 | |
139 // toggle recording | |
140 if (v->control->replayDst) { | |
141 replayClose(v->control->replayDst); | |
142 v->control->replayDst = NULL; | |
143 } else { | |
144 char path[PATH_MAX]; | |
145 | |
146 ljpathFind_w(path, "demo.ljm"); | |
147 v->control->replayDst = newReplay(path, v->field); | |
148 #if USE_PICS_FOLDER | |
149 withPics = 0; | |
150 #endif | |
151 } | |
152 } | |
153 | |
154 static void startPlaying(LJView *v) { | |
155 withPics = -1; | |
156 | |
157 // close existing recording | |
158 if (v->control->replayDst) { | |
159 replayClose(v->control->replayDst); | |
160 v->control->replayDst = NULL; | |
161 } | |
162 | |
163 // toggle playback | |
164 if (v->control->replaySrc) { | |
165 replayClose(v->control->replaySrc); | |
166 v->control->replaySrc = NULL; | |
167 } else { | |
168 char path[PATH_MAX]; | |
169 | |
170 if (ljpathFind_r(path, "demo.ljm")) { | |
171 v->control->replaySrc = openReplay(path, v->field); | |
172 } | |
173 v->nLockTimes = 0; | |
174 #if USE_PICS_FOLDER | |
175 withPics = 0; | |
176 #endif | |
177 } | |
178 v->backDirty = ~0; | |
179 v->field->reloaded = 1; | |
180 } | |
181 | |
182 int ljHandleConsoleButtons(LJView *v) { | |
183 int canceled = 0; | |
184 | |
185 while (keypressed()) { | |
186 int scancode; | |
187 int codepoint = ureadkey(&scancode); | |
188 | |
189 if (scancode == KEY_ESC) { | |
190 wantPause = 1; | |
191 } else if (scancode == KEY_PRTSCR) { | |
192 saveScreen(-1); | |
193 save_bitmap("ljbackbuf.bmp", v->plat->skin->fullback, NULL); | |
194 } else if (codepoint == '[') { | |
195 v->plat->wantRecord = 1; | |
196 } else if (codepoint == ']') { | |
197 v->plat->wantRecord = 0; | |
198 startPlaying(v); | |
199 } | |
200 } | |
201 if (v->plat->wantRecord) { | |
202 v->plat->wantRecord = 0; | |
203 startRecording(v); | |
204 } | |
205 if (wantPause) { | |
206 canceled = pauseGame(v->plat); | |
207 wantPause = 0; | |
208 } | |
209 | |
210 if (wantsClose) { | |
211 canceled = 1; | |
212 } | |
213 | |
214 return canceled; | |
215 } | |
216 | |
217 | |
218 static void drawBlock(const LJPCView *const v, int x, int y, int blk) { | |
219 | |
220 if (x >= 0 && x < LJ_PF_WID && y >= 0 && y < LJ_PF_VIS_HT) { | |
221 const int w = v->skin->blkW; | |
222 const int h = v->skin->blkH; | |
223 const int dstX = w * x; | |
224 const int dstY = h * (LJ_PF_VIS_HT - 1 - y); | |
225 | |
226 if (v->skin->transparentPF && (blk == 0 || blk == 0x100)) { | |
227 | |
228 // Copy background | |
229 const unsigned int srcX = dstX + v->baseX; | |
230 const unsigned int srcY = dstY + v->skin->baseY | |
231 - LJ_PF_VIS_HT * h - v->skin->pfElev; | |
232 blit(v->skin->bg, v->back, srcX, srcY, dstX, dstY, w, h); | |
233 } else if (blk && v->skin->connBlocks) { | |
234 | |
235 // Copy connected block | |
236 const unsigned int srcX = ((blk >> 0) & 15) * w; | |
237 const unsigned int srcY = ((blk >> 4) & 15) * h; | |
238 blit(v->skin->connBlocks, v->back, srcX, srcY, dstX, dstY, w, h); | |
239 } else { | |
240 | |
241 // Copy lone block | |
242 const unsigned int srcX = ((blk >> 4) & 7) * w; | |
243 const unsigned int srcY = ((blk >> 7) & 1) * h; | |
244 blit(v->skin->blocks, v->back, srcX, srcY, dstX, dstY, w, h); | |
245 } | |
246 | |
247 // 0x100: hotline | |
248 if (blk & 0x100) { | |
249 hline(v->back, dstX, dstY + h / 2 - 1, dstX + w - 1, pfFgColor); | |
250 hline(v->back, dstX, dstY + h / 2 , dstX + w - 1, pfFgColor); | |
251 hline(v->back, dstX, dstY + h / 2 + 1, dstX + w - 1, pfBgColor); | |
252 } | |
253 } | |
254 } | |
255 | |
256 /** | |
257 * Draws multiple rows of the playfield | |
258 * @param p the playfield | |
259 * @param rows the rows to be updated (0x00001 on bottom, 0x80000 on top) | |
260 */ | |
261 void updField(const LJView *const v, LJBits rows) { | |
262 const LJField *const p = v->field; | |
263 | |
264 acquire_bitmap(v->plat->back); | |
265 for (int y = 0; | |
266 y < LJ_PF_VIS_HT && rows != 0; | |
267 ++y, rows >>= 1) { | |
268 int blankTile = 0; | |
269 | |
270 // hotline: draw 0x100 as the background | |
271 if (hotlineRows[y] && v->field->scoreStyle == LJSCORE_HOTLINE) { | |
272 blankTile = 0x100; | |
273 } | |
274 // during line clear delay, draw bars to show which lines were cleared | |
275 if (p->state == LJS_LINES_FALLING && p->stateTime > 0 | |
276 && ((1 << y) & p->tempRows)) { | |
277 blankTile = 0x100; | |
278 } | |
279 if (rows & 1) { | |
280 for (int x = p->leftWall; x < p->rightWall; x++) { | |
281 int b = v->hidePF ? 0 : p->b[y][x]; | |
282 drawBlock(v->plat, x, y, b ? b : blankTile); | |
283 } | |
284 } | |
285 } | |
286 release_bitmap(v->plat->back); | |
287 } | |
288 | |
289 | |
290 #define SHADOW_BLOCK 0x00 | |
291 | |
292 /** | |
293 * Draws a tetromino whose lower left corner of the bounding box is at (x, y) | |
294 * @param b the bitmap to draw to | |
295 * @param piece the piece to be drawn | |
296 * @param x distance from to left side of 4x4 box | |
297 * @param y distance from top of bitmap to bottom of 4x4 box | |
298 * @param the rotation state (0: U; 1: R; 2: D; 3: L; 4: Initial position) | |
299 * @param w width of each block | |
300 * @param h height of each block | |
301 * @param color Drawing style | |
302 * color == 0: draw shadow | |
303 * color == 0x10 through 0x70: draw in that color | |
304 * color == 0x80: draw as garbage | |
305 * color == -255 through -1: draw with 255 through 1 percent lighting | |
306 */ | |
307 LJBits drawPiece(LJView *const v, BITMAP *const b, | |
308 int piece, int x, int y, int theta, | |
309 int color, int w, int h) { | |
310 // Don't try to draw the -1 that's the sentinel for no hold piece | |
311 if (piece < 0) | |
312 return 0; | |
313 | |
314 LJBits rows = 0; | |
315 LJBlkSpec blocks[4]; | |
316 const int srcW = v->plat->skin->blkW; | |
317 const int srcH = v->plat->skin->blkH; | |
318 BITMAP *singleBlocks = v->plat->skin->blocks; | |
319 int singleSeries = (color == 0 | |
320 && singleBlocks->h >= 6 * srcH) | |
321 ? 4 : 2; | |
322 | |
323 // color: 0 for shadow, >0 for piece | |
324 BITMAP *connBlocks = (color != SHADOW_BLOCK) | |
325 ? v->plat->skin->connBlocks : NULL; | |
326 | |
327 BITMAP *transTemp = color < 0 | |
328 || (color == SHADOW_BLOCK && v->hideShadow < LJSHADOW_COLORED) | |
329 ? create_bitmap(w, h) : 0; | |
330 | |
331 if (transTemp) { | |
332 set_trans_blender(0, 0, 0, v->hideShadow ? 128 : 64); | |
333 } | |
334 | |
335 expandPieceToBlocks(blocks, v->field, piece, 0, 0, theta); | |
336 acquire_bitmap(b); | |
337 for (int blk = 0; blk < 4; ++blk) { | |
338 int blkValue = blocks[blk].conn; | |
339 | |
340 // If blkValue == 0 then the block is not part of the piece, | |
341 // such as if it's a domino or tromino or (once pentominoes | |
342 // are added) a tetromino. | |
343 if (blkValue) { | |
344 int blkX = blocks[blk].x; | |
345 int blkY = blocks[blk].y; | |
346 const int dstX = x + w * blkX; | |
347 const int dstY = y + h * (-1 - blkY); | |
348 | |
349 if (color > 0) { | |
350 blkValue = color | (blkValue & CONNECT_MASK); | |
351 } else if (color == 0 && v->hideShadow == LJSHADOW_COLORLESS) { | |
352 blkValue = 0; | |
353 } | |
354 | |
355 if (connBlocks) { | |
356 const unsigned int srcX = ((blkValue >> 0) & 15) * srcW; | |
357 const unsigned int srcY = ((blkValue >> 4) & 15) * srcH; | |
358 | |
359 if (transTemp) { | |
360 stretch_blit(connBlocks, transTemp, | |
361 srcX, srcY, srcW, srcH, | |
362 0, 0, w, h); | |
363 if (color < 0) { | |
364 draw_lit_sprite(b, transTemp, dstX, dstY, color + 256); | |
365 } else { | |
366 draw_trans_sprite(b, transTemp, dstX, dstY); | |
367 } | |
368 } else if (srcW != w || srcH != h) { | |
369 stretch_blit(connBlocks, b, | |
370 srcX, srcY, srcW, srcH, | |
371 dstX, dstY, w, h); | |
372 if (dstY > 600) { | |
373 allegro_message("dstY = %d\n", dstY); | |
374 } | |
375 } else { | |
376 blit(connBlocks, b, | |
377 srcX, srcY, | |
378 dstX, dstY, w, h); | |
379 } | |
380 } else { | |
381 const unsigned int srcX = ((blkValue >> 4) & 7) * srcW; | |
382 const unsigned int srcY = (((blkValue >> 7) & 1) + singleSeries) * srcH; | |
383 | |
384 if (transTemp) { | |
385 stretch_blit(singleBlocks, transTemp, | |
386 srcX, srcY, srcW, srcH, | |
387 0, 0, w, h); | |
388 if (color < 0) { | |
389 draw_lit_sprite(b, transTemp, dstX, dstY, color + 256); | |
390 } else { | |
391 draw_trans_sprite(b, transTemp, dstX, dstY); | |
392 } | |
393 } else { | |
394 stretch_blit(singleBlocks, b, | |
395 srcX, srcY, srcW, srcH, | |
396 dstX, dstY, w, h); | |
397 } | |
398 } | |
399 rows |= 1 << blkY; | |
400 } | |
401 } | |
402 release_bitmap(b); | |
403 if (transTemp) { | |
404 solid_mode(); | |
405 destroy_bitmap(transTemp); | |
406 } | |
407 | |
408 return rows; | |
409 } | |
410 | |
411 void drawPieceInPF(LJView *v, int dstX, LJFixed dstY, int theta, int color) { | |
412 LJBits bits = 0; | |
413 BITMAP *b = v->plat->back; | |
414 const LJField *const p = v->field; | |
415 int y = ljfixfloor(dstY); | |
416 const int w = v->plat->skin->blkW; | |
417 const int h = v->plat->skin->blkH; | |
418 int drawnY = fixmul(h, dstY); | |
419 | |
420 bits = drawPiece(v, b, p->curPiece[0], | |
421 w * dstX, h * LJ_PF_VIS_HT - drawnY, | |
422 theta, | |
423 color, w, h); | |
424 bits = (y >= 0) ? bits << y : bits >> -y; | |
425 bits &= (1 << LJ_PF_VIS_HT) - 1; | |
426 if (dstY & 0xFFFF) { | |
427 bits |= bits << 1; | |
428 } | |
429 | |
430 v->backDirty |= bits; | |
431 v->frontDirty |= bits; | |
432 } | |
433 | |
434 | |
435 void drawFallingPiece(LJView *v) { | |
436 const LJField *const p = v->field; | |
437 int piece = p->curPiece[0]; | |
438 int y = v->smoothGravity | |
439 ? p->y | |
440 : ljitofix(ljfixfloor(p->y)); | |
441 const int color = (p->state == LJS_LANDED) | |
442 ? -128 - ((p->stateTime + 1) * 128 / (p->speed.lockDelay + 1)) | |
443 : pieceColors[piece]; | |
444 | |
445 // Draw trails | |
446 if (v->showTrails) { | |
447 | |
448 // trail going up | |
449 while (v->trailY - y < ljitofix(-1)) { | |
450 v->trailY += ljitofix(1); | |
451 drawPieceInPF(v, p->x, v->trailY, p->theta, color); | |
452 } | |
453 | |
454 // trail going down | |
455 while (v->trailY - y > ljitofix(1)) { | |
456 v->trailY -= ljitofix(1); | |
457 drawPieceInPF(v, p->x, v->trailY, p->theta, color); | |
458 } | |
459 } | |
460 drawPieceInPF(v, p->x, y, p->theta, color); | |
461 v->trailY = y; | |
462 } | |
463 | |
464 void drawShadow(LJView *v) { | |
465 const LJField *const p = v->field; | |
466 int y = ljitofix(p->hardDropY); | |
467 const int color = SHADOW_BLOCK; | |
468 drawPieceInPF(v, p->x, y, p->theta, color); | |
469 } | |
470 | |
471 void drawNextPieces(LJView *v) { | |
472 int baseX = v->plat->baseX; | |
473 int baseY = v->plat->skin->baseY - v->plat->skin->pfElev; | |
474 int blkW = v->plat->skin->blkW; | |
475 int blkH = v->plat->skin->blkH; | |
476 int holdPieceColor = v->field->alreadyHeld | |
477 ? 0x80 | |
478 : pieceColors[v->field->holdPiece]; | |
479 int ceil = v->field->ceiling; | |
480 | |
481 BITMAP *sc = v->plat->skin->fullback; | |
482 | |
483 if (v->frontDirty & LJ_DIRTY_NEXT) { | |
484 int holdW = blkW * 2 / 3; | |
485 int holdH = blkH * 2 / 3; | |
486 int holdX = baseX + blkW; | |
487 int holdY = baseY - (ceil - 1) * blkH - 4 * holdH; | |
488 | |
489 // Move the hold piece within the screen | |
490 if (holdY < 0) { | |
491 holdY = 0; | |
492 } | |
493 | |
494 // Draw hold piece | |
495 blit(v->plat->skin->bg, sc, | |
496 holdX, holdY, | |
497 holdX, holdY, | |
498 holdW * 4, holdH * 2); | |
499 drawPiece(v, sc, | |
500 v->field->holdPiece, holdX, holdY + 4 * holdH, 4, | |
501 holdPieceColor, holdW, holdH); | |
502 blit(sc, screen, | |
503 holdX, holdY, | |
504 holdX, holdY, | |
505 holdW * 4, holdH * 2); | |
506 } | |
507 // Draw next pieces | |
508 | |
509 switch (v->plat->skin->nextPos) { | |
510 case LJNEXT_RIGHT: | |
511 case LJNEXT_RIGHT_TAPER: | |
512 if (v->frontDirty & LJ_DIRTY_NEXT) { | |
513 int y = baseY - ceil * blkH; | |
514 int x = baseX + LJ_PF_WID * blkW; | |
515 int w = blkW; | |
516 int h = blkH; | |
517 for (int i = 1; i <= v->nextPieces; ++i) { | |
518 int piece = v->field->curPiece[i]; | |
519 | |
520 blit(v->plat->skin->bg, sc, x, y, x, y, w * 4, h * 2); | |
521 if (!v->hideNext) { | |
522 drawPiece(v, sc, | |
523 piece, x, y + 4 * h, 4, | |
524 pieceColors[piece], w, h); | |
525 } | |
526 blit(sc, screen, x, y, x, y, w * 4, h * 2); | |
527 y += 8 + h * 2; | |
528 if (v->plat->skin->nextPos == LJNEXT_RIGHT_TAPER) { | |
529 --h; | |
530 --w; | |
531 } | |
532 } | |
533 } | |
534 break; | |
535 | |
536 case LJNEXT_TOP: | |
537 if (v->frontDirty & LJ_DIRTY_NEXT) { | |
538 int y = baseY - (ceil + 2) * blkH - 8; | |
539 int x = baseX + 4 * blkW; | |
540 int blitX = x, blitY = y; | |
541 | |
542 blit(v->plat->skin->bg, sc, x, y, x, y, blkW * 8, blkH * 2); | |
543 if (!v->hideNext) { | |
544 if (v->nextPieces >= 1) { | |
545 int piece = v->field->curPiece[1]; | |
546 drawPiece(v, sc, | |
547 piece, x, y + 4 * blkH, 4, | |
548 pieceColors[piece], blkW, blkH); | |
549 } | |
550 if (v->nextPieces >= 2) { | |
551 int piece = v->field->curPiece[2]; | |
552 x += 4 * blkW; | |
553 drawPiece(v, sc, | |
554 piece, x, y + 3 * blkH, 4, | |
555 pieceColors[piece], blkW/ 2, blkH / 2); | |
556 } | |
557 if (v->nextPieces >= 3) { | |
558 int piece = v->field->curPiece[3]; | |
559 x += 2 * blkW; | |
560 drawPiece(v, sc, | |
561 piece, x, y + 3 * blkH, 4, | |
562 pieceColors[piece], blkW / 2, blkH / 2); | |
563 } | |
564 } | |
565 blit(sc, screen, blitX, blitY, blitX, blitY, blkW * 8, blkH * 2); | |
566 } | |
567 break; | |
568 } | |
569 | |
570 if (v->plat->nextAbove && !v->hideNext) { | |
571 int row = (v->field->hardDropY + 4); | |
572 int x = (1 + v->field->x) * blkW; | |
573 for (int i = 1; | |
574 i <= v->plat->nextAbove | |
575 && row <= v->field->ceiling - 2; | |
576 ++i) { | |
577 int y = (LJ_PF_VIS_HT - row) * blkH; | |
578 int piece = v->field->curPiece[i]; | |
579 | |
580 drawPiece(v, v->plat->back, | |
581 piece, x, y, 4, | |
582 pieceColors[piece], blkW / 2, blkH / 2); | |
583 v->backDirty |= 3 << row; | |
584 row += 2; | |
585 } | |
586 } | |
587 v->frontDirty &= ~LJ_DIRTY_NEXT; | |
588 } | |
589 | |
590 void drawScore(LJView *v) { | |
591 int gameTime = v->field->gameTime; | |
592 int seconds = gameTime / 60; | |
593 int minutes = seconds / 60; | |
594 int baseX = v->plat->baseX; | |
595 int tpm = -1; | |
596 int spawnLeft = v->plat->skin->blkW * LJ_SPAWN_X + baseX; | |
597 int pfRight = v->plat->skin->blkW * LJ_PF_WID + baseX; | |
598 BITMAP *sc = v->plat->skin->fullback; | |
599 | |
600 if (withPics >= 0) { | |
601 if (v->field->nPieces != withPics) { | |
602 saveScreen(v->field->nPieces); | |
603 } | |
604 withPics = v->field->nPieces; | |
605 } | |
606 | |
607 if (v->nLockTimes >= 2 ) { | |
608 int time = v->lockTime[0] - v->lockTime[v->nLockTimes - 1]; | |
609 if (time > 0) { | |
610 tpm = 3600 * (v->nLockTimes - 1) / time; | |
611 } | |
612 } | |
613 | |
614 if (v->frontDirty & LJ_DIRTY_SCORE) { | |
615 switch (v->plat->skin->nextPos) { | |
616 case LJNEXT_TOP: | |
617 blit(v->plat->skin->bg, sc, | |
618 pfRight, 72, | |
619 pfRight, 72, | |
620 112, 136); | |
621 | |
622 textout_ex(sc, aver32, "Lines:", pfRight, 72, fgColor, -1); | |
623 textprintf_right_ex(sc, aver32, pfRight + 104, 102, fgColor, -1, | |
624 "%d", v->field->lines); | |
625 textout_ex(sc, aver32, "Score:", pfRight, 142, fgColor, -1); | |
626 textprintf_right_ex(sc, aver32, pfRight + 104, 172, fgColor, -1, | |
627 "%d", v->field->score); | |
628 textout_ex(sc, aver32, "Level:", pfRight, 212, fgColor, -1); | |
629 textprintf_right_ex(sc, aver32, pfRight + 104, 242, fgColor, -1, | |
630 "%d", v->field->speedState.level); | |
631 blit(sc, screen, | |
632 pfRight, 72, | |
633 pfRight, 72, | |
634 112, 136); | |
635 break; | |
636 | |
637 default: | |
638 blit(v->plat->skin->bg, sc, spawnLeft, 12, spawnLeft, 12, 288, 30); | |
639 blit(v->plat->skin->bg, sc, | |
640 spawnLeft, 42, spawnLeft, 42, | |
641 pfRight - spawnLeft, 30); | |
642 textprintf_right_ex(sc, aver32, spawnLeft + 288, 12, fgColor, -1, | |
643 "Lv. %d", v->field->speedState.level); | |
644 textprintf_ex(sc, aver32, spawnLeft, 12, fgColor, -1, | |
645 "Lines: %d", v->field->lines); | |
646 textprintf_ex(sc, aver32, spawnLeft, 42, fgColor, -1, | |
647 "Score: %d", v->field->score); | |
648 blit(sc, screen, spawnLeft, 12, spawnLeft, 12, 288, 60); | |
649 break; | |
650 } | |
651 } | |
652 | |
653 /* If speed is defined, and there is room to draw it, draw it. */ | |
654 if (tpm > 0 && v->nextPieces <= 3) { | |
655 blit(v->plat->skin->bg, sc, | |
656 pfRight, 282, | |
657 pfRight, 282, | |
658 104, 60); | |
659 textout_ex(sc, aver32, "Speed:", pfRight, 282, fgColor, -1); | |
660 textprintf_right_ex(sc, aver32, pfRight + 104, 312, fgColor, -1, | |
661 "%d", tpm); | |
662 blit(sc, screen, | |
663 pfRight, 282, | |
664 pfRight, 282, | |
665 104, 60); | |
666 } | |
667 | |
668 if (v->frontDirty & LJ_DIRTY_NEXT) { | |
669 | |
670 // Erase gimmick | |
671 blit(v->plat->skin->bg, sc, | |
672 baseX, v->plat->skin->baseY + 8, | |
673 baseX, v->plat->skin->baseY + 8, | |
674 v->plat->skin->blkW * LJ_PF_WID, 30); | |
675 // Draw gimmick | |
676 if (v->field->gimmick >= 0 && v->field->gimmick < LJGM_N_GIMMICKS) { | |
677 const char *gimmickName = ljGetFourCCName(gimmickNames[v->field->gimmick]); | |
678 textout_centre_ex(sc, aver32, | |
679 gimmickName ? gimmickName : "Bad gimmick ID", | |
680 baseX + (LJ_PF_WID / 2) * v->plat->skin->blkW, | |
681 v->plat->skin->baseY + 8, | |
682 fgColor, -1); | |
683 blit(sc, screen, | |
684 baseX, v->plat->skin->baseY + 8, | |
685 baseX, v->plat->skin->baseY + 8, | |
686 v->plat->skin->blkW * LJ_PF_WID, 30); | |
687 } | |
688 } | |
689 drawNextPieces(v); | |
690 | |
691 blit(v->plat->skin->bg, sc, pfRight, 42, pfRight, 42, 96, 30); | |
692 #if 0 | |
693 // Use this for DEBUG inspection into a variable | |
694 textprintf_right_ex(sc, aver16, pfRight + 96, 8, fgColor, -1, | |
695 "%d", v->field->bpmCounter); | |
696 #endif | |
697 textprintf_right_ex(sc, aver32, pfRight + 96, 42, fgColor, -1, | |
698 "%d:%02d", minutes, seconds - 60 * minutes); | |
699 blit(sc, screen, pfRight, 42, pfRight, 42, 96, 30); | |
700 | |
701 } | |
702 | |
703 void blitField(LJView *v) { | |
704 int blkH = v->plat->skin->blkH; | |
705 int rowY = v->plat->skin->baseY | |
706 - v->plat->skin->pfElev | |
707 - blkH * v->field->ceiling; | |
708 int x = v->plat->skin->blkW * v->field->leftWall; | |
709 int w = v->plat->skin->blkW * (v->field->rightWall - v->field->leftWall); | |
710 | |
711 // Copy each dirty row | |
712 for (int y = v->field->ceiling - 1; y >= 0; --y) { | |
713 if (v->frontDirty & (1 << y)) { | |
714 int top = (LJ_PF_VIS_HT - y - 1) * blkH; | |
715 int ht = 0; | |
716 | |
717 // Find the height of the contiguous rows to blit | |
718 do { | |
719 ht += blkH; | |
720 --y; | |
721 } while ((y >= 0) | |
722 && (v->frontDirty & (1 << y))); | |
723 blit(v->plat->back, screen, | |
724 x, top, | |
725 x + v->plat->baseX, rowY, | |
726 w, ht); | |
727 rowY += ht; | |
728 } | |
729 rowY += blkH; | |
730 } | |
731 | |
732 v->frontDirty &= (~0) << LJ_PF_HT; | |
733 } | |
734 | |
735 void saveScreen(int n) { | |
736 BITMAP *b = create_bitmap(SCREEN_W, SCREEN_H); | |
737 if (b) { | |
738 PALETTE pal; | |
739 | |
740 get_palette(pal); | |
741 blit(screen, b, 0, 0, 0, 0, SCREEN_W, SCREEN_H); | |
742 if (n < 0) { | |
743 save_bitmap("ljsnap.bmp", b, pal); | |
744 } else { | |
745 char filename[64]; | |
746 sprintf(filename, "pics/lj%05d.bmp", n); | |
747 save_bitmap(filename, b, pal); | |
748 } | |
749 destroy_bitmap(b); | |
750 } | |
751 } | |
752 | |
753 #define PRESETS_PER_COL 6 | |
754 #define N_NONPRESETS 2 | |
755 #define PRESETS_TOP 140 | |
756 #define PRESET_COL_WIDTH 250 | |
757 #define PRESET_ROW_HT 40 | |
758 | |
759 static const char *const nonPresetNames[N_NONPRESETS] = { | |
760 "Full Custom", "Back" | |
761 }; | |
762 | |
763 static void getPresetDrawRow(unsigned int preset, int hilite) { | |
764 unsigned int ht = text_height(aver32); | |
765 const char *txt = preset < nLoadedPresets | |
766 ? loadedPresets[preset].name | |
767 : nonPresetNames[preset - nLoadedPresets]; | |
768 if (!txt) { | |
769 txt = "Bad"; | |
770 } | |
771 unsigned int wid = text_length(aver32, txt); | |
772 | |
773 int buttonCol = preset / PRESETS_PER_COL; | |
774 int buttonX = 20 + buttonCol * PRESET_COL_WIDTH; | |
775 int buttonY = PRESETS_TOP | |
776 + PRESET_ROW_HT * (preset - buttonCol * PRESETS_PER_COL); | |
777 unsigned int buttonWidth = wid + 16; | |
778 | |
779 rectfill(screen, | |
780 buttonX, buttonY, | |
781 buttonX + buttonWidth - 1, buttonY + PRESET_ROW_HT - 1, | |
782 hilite ? hiliteColor : bgColor); | |
783 if (hilite) { | |
784 rect(screen, | |
785 buttonX, buttonY, | |
786 buttonX + buttonWidth - 1, buttonY + PRESET_ROW_HT - 1, | |
787 fgColor); | |
788 } | |
789 textout_ex(screen, aver32, txt, | |
790 8 + buttonX, | |
791 20 + buttonY - ht / 2, | |
792 fgColor, -1); | |
793 } | |
794 | |
795 int getPreset(int lastPreset) { | |
796 LJBits lastKeys = ~0; | |
797 redrawWholeScreen = 1; | |
798 | |
799 clear_keybuf(); | |
800 if (lastPreset < 0) { | |
801 lastPreset += nLoadedPresets + N_NONPRESETS; | |
802 } | |
803 | |
804 for(int done = 0; done == 0; ) { | |
805 if (redrawWholeScreen) { | |
806 redrawWholeScreen = 0; | |
807 clear_to_color(screen, bgColor); | |
808 textout_ex(screen, aver32, "LOCKJAW > Play", 16, 32, fgColor, -1); | |
809 textout_ex(screen, aver32, "Select a scenario:", 16, 90, fgColor, -1); | |
810 | |
811 for (int preset = 0; | |
812 preset < nLoadedPresets + N_NONPRESETS; | |
813 ++preset) { | |
814 getPresetDrawRow(preset, preset == lastPreset); | |
815 } | |
816 textout_ex(screen, aver16, "Arrows: move; Rotate Right: start", | |
817 16, PRESETS_TOP + 40 * (PRESETS_PER_COL + 1), fgColor, -1); | |
818 textout_ex(screen, aver16, "Coming soon: an editor for these!", | |
819 16, PRESETS_TOP + 40 * (PRESETS_PER_COL + 1) + 20, fgColor, -1); | |
820 } | |
821 while (keypressed()) { | |
822 int scancode; | |
823 ureadkey(&scancode); | |
824 if (scancode == KEY_PRTSCR) { | |
825 saveScreen(-1); | |
826 } | |
827 } | |
828 | |
829 int preset = lastPreset; | |
830 LJBits keys = menuReadPad(); | |
831 LJBits newKeys = keys & ~lastKeys; | |
832 | |
833 if ((newKeys & VKEY_ROTL) || wantsClose) { | |
834 preset = nLoadedPresets + N_NONPRESETS - 1; | |
835 ezPlaySample("rotate_wav", 128); | |
836 } | |
837 if ((newKeys & VKEY_ROTR) || wantsClose) { | |
838 done = 1; | |
839 ezPlaySample("line_wav", 128); | |
840 } | |
841 | |
842 if (newKeys & VKEY_UP) { | |
843 if (preset <= 0) { | |
844 preset = nLoadedPresets + N_NONPRESETS - 1; | |
845 } else { | |
846 --preset; | |
847 } | |
848 ezPlaySample("shift_wav", 128); | |
849 } | |
850 if (newKeys & VKEY_DOWN) { | |
851 ++preset; | |
852 if (preset >= nLoadedPresets + N_NONPRESETS) { | |
853 preset = 0; | |
854 } | |
855 ezPlaySample("shift_wav", 128); | |
856 } | |
857 | |
858 if (newKeys & VKEY_LEFT) { | |
859 if (preset < PRESETS_PER_COL) { | |
860 preset += (((nLoadedPresets + N_NONPRESETS) | |
861 / PRESETS_PER_COL) | |
862 ) | |
863 * PRESETS_PER_COL; | |
864 if (preset >= nLoadedPresets + N_NONPRESETS) { | |
865 preset -= PRESETS_PER_COL; | |
866 } | |
867 } else { | |
868 preset -= PRESETS_PER_COL; | |
869 } | |
870 ezPlaySample("shift_wav", 128); | |
871 } | |
872 if (newKeys & VKEY_RIGHT) { | |
873 preset += PRESETS_PER_COL; | |
874 if (preset >= nLoadedPresets + N_NONPRESETS) { | |
875 preset %= PRESETS_PER_COL; | |
876 } | |
877 ezPlaySample("shift_wav", 128); | |
878 } | |
879 | |
880 if (preset != lastPreset) { | |
881 vsync(); | |
882 getPresetDrawRow(lastPreset, 0); | |
883 getPresetDrawRow(preset, 1); | |
884 lastPreset = preset; | |
885 } else { | |
886 rest(30); | |
887 } | |
888 lastKeys = keys; | |
889 } | |
890 | |
891 // Wrap the nonpresets into the negative numbers. | |
892 if (lastPreset >= nLoadedPresets) { | |
893 lastPreset -= (nLoadedPresets + N_NONPRESETS); | |
894 } | |
895 | |
896 return lastPreset; | |
897 } | |
898 | |
899 /** | |
900 * Pauses the game, returning nonzero if the player wants to quit. | |
901 */ | |
902 int pauseGame(LJPCView *v) { | |
903 int escCount = 0; | |
904 int quit = 0; | |
905 int escHold = curTime; | |
906 redrawWholeScreen = 1; | |
907 | |
908 LJMusic_pause(v->skin->bgm, 1); | |
909 while (escCount < 2 && !quit) { | |
910 LJMusic_poll(v->skin->bgm); | |
911 if (redrawWholeScreen) { | |
912 redrawWholeScreen = 0; | |
913 clear_to_color(screen, pfBgColor); | |
914 textout_centre_ex(screen, aver32, "LOCKJAW: GAME PAUSED", | |
915 SCREEN_W / 2, 200, pfFgColor, -1); | |
916 textout_centre_ex(screen, aver32, "Press Esc to continue", | |
917 SCREEN_W / 2, 250, pfFgColor, -1); | |
918 textout_centre_ex(screen, aver32, "or hold Esc to quit", | |
919 SCREEN_W / 2, 300, pfFgColor, -1); | |
920 } | |
921 | |
922 if (key[KEY_ESC]) { | |
923 if (escHold == 0) { | |
924 escHold = curTime; | |
925 } | |
926 if (curTime - escHold >= 60) { | |
927 quit = 1; | |
928 } | |
929 } else { | |
930 if (escHold) { | |
931 ++escCount; | |
932 } | |
933 escHold = 0; | |
934 } | |
935 rest(30); | |
936 if (wantsClose) { | |
937 quit = 1; | |
938 } | |
939 | |
940 } | |
941 LJMusic_pause(v->skin->bgm, 0); | |
942 redrawWholeScreen = 1; | |
943 clear_keybuf(); | |
944 return quit; | |
945 } | |
946 | |
947 | |
948 void pcInit(LJView *v, struct LJPrefs *prefs) { | |
949 v->plat->b2bcd1 = 0; | |
950 v->plat->b2bcd2 = 0; | |
951 v->plat->baseX = v->plat->skin->baseX; | |
952 | |
953 // If the player has chosen to use more next pieces than the | |
954 // next piece position can handle, set the number of | |
955 // next pieces for correctness of debrief(). | |
956 if (v->nextPieces > 3 && v->plat->skin->nextPos == LJNEXT_TOP) { | |
957 v->nextPieces = 3; | |
958 } | |
959 } | |
960 | |
961 /** | |
962 * Redraws everything on the screen. | |
963 * Called when needs redraw. | |
964 */ | |
965 void playRedrawScreen(LJView *v) { | |
966 blit(v->plat->skin->bg, screen, | |
967 0, 0, | |
968 0, 0, | |
969 SCREEN_W, SCREEN_H); | |
970 v->frontDirty = ~0; | |
971 } | |
972 | |
973 #if 0 | |
974 void startingAniWantsSkip(LJView *v) { | |
975 LJInput unusedIn; | |
976 addKeysToInput(&unusedIn, readPad(), v->field, v->control); | |
977 } | |
978 #endif | |
979 | |
980 void playSampleForTetromino(int piece); | |
981 | |
982 void restPollingMusic(int nFrames, LJMusic *bgm) { | |
983 nFrames += curTime; | |
984 while (curTime - nFrames < 0) { | |
985 if (bgm) { | |
986 LJMusic_poll(bgm); | |
987 } | |
988 } | |
989 } | |
990 | |
991 extern int bgmReadyGo; | |
992 void startingAnimation(LJView *v) { | |
993 int readyGoX = v->plat->baseX + v->plat->skin->blkW * LJ_PF_WID / 2; | |
994 int readyGoY = v->plat->skin->baseY | |
995 - v->plat->skin->pfElev | |
996 - v->plat->skin->blkH | |
997 * v->field->ceiling * 3 / 5; | |
998 | |
999 clear_keybuf(); | |
1000 v->backDirty = 0; | |
1001 | |
1002 playRedrawScreen(v); | |
1003 blitField(v); | |
1004 textout_centre_ex(screen, aver32, "Ready", | |
1005 readyGoX, readyGoY, pfFgColor, -1); | |
1006 | |
1007 ezPlaySample("ready_wav", 128); | |
1008 restPollingMusic(36, bgmReadyGo ? v->plat->skin->bgm : NULL); | |
1009 v->frontDirty = ~0; | |
1010 | |
1011 if (!wantsClose) { | |
1012 blitField(v); | |
1013 textout_centre_ex(screen, aver32, "GO!", | |
1014 readyGoX, readyGoY, pfFgColor, -1); | |
1015 drawScore(v); | |
1016 | |
1017 ezPlaySample("go_wav", 128); | |
1018 v->frontDirty = ~0; | |
1019 restPollingMusic(12, bgmReadyGo ? v->plat->skin->bgm : NULL); | |
1020 } | |
1021 if (!wantsClose) { | |
1022 playSampleForTetromino(v->field->curPiece[1]); | |
1023 restPollingMusic(24, bgmReadyGo ? v->plat->skin->bgm : NULL); | |
1024 } | |
1025 } | |
1026 | |
1027 | |
1028 static void gameOverAnimation(const LJPCView *const v, const LJField *p, int won) { | |
1029 int ceiling = p->ceiling; | |
1030 int left = v->baseX + p->leftWall * v->skin->blkW; | |
1031 int right = v->baseX + p->rightWall * v->skin->blkW - 1; | |
1032 | |
1033 ezPlaySample("sectionup_wav", 0); | |
1034 if (!won) { | |
1035 ezPlaySample("gameover_wav", 256); | |
1036 } else { | |
1037 ezPlaySample("win_wav", 256); | |
1038 } | |
1039 | |
1040 for (int t = ceiling + v->skin->blkH - 2; t >= 0; --t) { | |
1041 | |
1042 // FIXME: vsync doesn't work on Vista | |
1043 vsync(); | |
1044 for (int row = ceiling - 1; row >= 0; --row) { | |
1045 int ysub = t - row; | |
1046 | |
1047 if (ysub >= 0 && ysub < v->skin->blkH) { | |
1048 int y = v->skin->baseY - v->skin->pfElev | |
1049 - row * v->skin->blkH - ysub - 1; | |
1050 hline(screen, left, y, right, pfBgColor); | |
1051 } | |
1052 } | |
1053 if (wantsClose) { | |
1054 t = 0; | |
1055 } | |
1056 } | |
1057 } | |
1058 | |
1059 #define MENU_COPR_NOTICE_LINES 4 | |
1060 const char *const menuCoprNotice[MENU_COPR_NOTICE_LINES] = { | |
1061 "Copr. 2006-2008 Damian Yerrick", | |
1062 "Not sponsored or endorsed by The Tetris Company.", | |
1063 "LOCKJAW comes with ABSOLUTELY NO WARRANTY. This is free software, and you are", | |
1064 "welcome to redistribute it under certain conditions as described in GPL.txt." | |
1065 }; | |
1066 | |
1067 static BITMAP *buildTitleScreen(void) { | |
1068 BITMAP *back = create_system_bitmap(SCREEN_W, SCREEN_H); | |
1069 if (!back) { | |
1070 return NULL; | |
1071 } | |
1072 | |
1073 // Gradient from (0, 0, 0) to (0, 0, 153) | |
1074 for (int y = 0; y < 192; ++y) { | |
1075 for (int x = -((y * 13) & 0x1F); | |
1076 x < SCREEN_W; | |
1077 x += 32) { | |
1078 int colValue = y + ((rand() & 0x7000) >> 12); | |
1079 int c = makecol(0, 0, 153 * colValue / 192); | |
1080 hline(back, x, y, x + 31, c); | |
1081 } | |
1082 } | |
1083 | |
1084 // Gradient from (102, 51, 0) to (204, 102, 0) | |
1085 for (int y = 192; y < 384; ++y) { | |
1086 for (int x = -((y * 13) & 0x1F); | |
1087 x < SCREEN_W; | |
1088 x += 32) { | |
1089 int colValue = y + ((rand() & 0x7800) >> 11); | |
1090 int c = makecol(102 * colValue / 192, 51 * colValue / 192, 0); | |
1091 hline(back, x, y, x + 31, c); | |
1092 } | |
1093 } | |
1094 | |
1095 // Gradient from (204, 102, 0) to (255, 128, 0) | |
1096 for (int y = 384; y < SCREEN_H; ++y) { | |
1097 for (int x = -((y * 13) & 0x1F); | |
1098 x < SCREEN_W; | |
1099 x += 32) { | |
1100 int colValue = y - 400 + ((rand() & 0x7C00) >> 10); | |
1101 if (colValue > 600 - 384) { | |
1102 colValue = 600 - 384; | |
1103 } | |
1104 int c = makecol(204 + 50 * colValue / (600 - 384), | |
1105 102 + 25 * colValue / (600 - 384), | |
1106 0); | |
1107 hline(back, x, y, x + 31, c); | |
1108 } | |
1109 } | |
1110 | |
1111 DATAFILE *obj = find_datafile_object(dat, "arttitle_bmp"); | |
1112 BITMAP *logo = obj ? obj->dat : NULL; | |
1113 obj = find_datafile_object(dat, "arttitle_pal"); | |
1114 const RGB *pal = obj ? obj->dat : NULL; | |
1115 | |
1116 if (logo && pal) { | |
1117 set_palette(pal); | |
1118 draw_sprite(back, logo, | |
1119 (SCREEN_W - logo->w) / 2, (384 - logo->h) / 2); | |
1120 //unselect_palette(); | |
1121 } | |
1122 | |
1123 textout_centre_ex(back, aver32, "Arrows: change; Enter: choose", | |
1124 SCREEN_W / 2, 440, | |
1125 0, -1); | |
1126 | |
1127 textout_centre_ex(back, aver32, "LOCKJAW: The Reference "LJ_VERSION, | |
1128 SCREEN_W / 2, SCREEN_H - 40, | |
1129 0, -1); | |
1130 | |
1131 return back; | |
1132 } | |
1133 | |
1134 enum { | |
1135 TITLE_EXIT = 0, | |
1136 TITLE_PLAY, | |
1137 TITLE_REPLAY, | |
1138 TITLE_OPTIONS, | |
1139 TITLE_SKIN, | |
1140 TITLE_KEYS, | |
1141 N_TITLE_ACTIONS | |
1142 }; | |
1143 | |
1144 static const char *titleActions[N_TITLE_ACTIONS] = { | |
1145 [TITLE_EXIT] = "Exit", | |
1146 [TITLE_PLAY] = "Play", | |
1147 [TITLE_REPLAY] = "Replay", | |
1148 [TITLE_SKIN] = "Skin...", | |
1149 [TITLE_OPTIONS] = "Options...", | |
1150 [TITLE_KEYS] = "Game Keys..." | |
1151 }; | |
1152 /* | |
1153 0: Exit | |
1154 1: Play | |
1155 2 | |
1156 */ | |
1157 int title(void) { | |
1158 | |
1159 // don't even draw if the player is trying to close the window | |
1160 if (wantsClose) { | |
1161 return 0; | |
1162 } | |
1163 | |
1164 BITMAP *back = buildTitleScreen(); | |
1165 LJBits lastKeys = ~0; | |
1166 int redraw = 1; | |
1167 int choice = 1; | |
1168 | |
1169 if (!back) { | |
1170 alert("Not enough memory to display the title screen.", | |
1171 "(If you don't even have RAM for a title screen,", | |
1172 "then what do you have RAM for?)", | |
1173 "Exit", 0, 13, 0); | |
1174 return 0; | |
1175 } | |
1176 | |
1177 redrawWholeScreen = 1; | |
1178 | |
1179 for(int done = 0; done == 0; ) { | |
1180 if (redrawWholeScreen) { | |
1181 redrawWholeScreen = 0; | |
1182 blit(back, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); | |
1183 redraw = 1; | |
1184 } | |
1185 if (redraw) { | |
1186 int dehilite = makecol(221, 153, 85); | |
1187 int white = makecol(255, 255, 255); | |
1188 redraw = 0; | |
1189 vsync(); | |
1190 blit(back, screen, 0, 400, 0, 400, SCREEN_W, 30); | |
1191 textout_centre_ex(screen, aver32, | |
1192 titleActions[choice], | |
1193 SCREEN_W / 2, 400, white, -1); | |
1194 textout_centre_ex(screen, aver32, | |
1195 titleActions[choice == 0 ? N_TITLE_ACTIONS - 1 : choice - 1], | |
1196 SCREEN_W / 2 - 160, 400, dehilite, -1); | |
1197 textout_centre_ex(screen, aver32, | |
1198 titleActions[choice == N_TITLE_ACTIONS - 1 ? 0 : choice + 1], | |
1199 SCREEN_W / 2 + 160, 400, dehilite, -1); | |
1200 } | |
1201 | |
1202 while (keypressed()) { | |
1203 int scancode; | |
1204 ureadkey(&scancode); | |
1205 if (scancode == KEY_PRTSCR) { | |
1206 saveScreen(-1); | |
1207 } | |
1208 } | |
1209 | |
1210 LJBits keys = menuReadPad(); | |
1211 LJBits newKeys = keys & ~lastKeys; | |
1212 | |
1213 if (newKeys & VKEY_LEFT) { | |
1214 --choice; | |
1215 redraw = 1; | |
1216 ezPlaySample("shift_wav", 128); | |
1217 } | |
1218 if (newKeys & VKEY_RIGHT) { | |
1219 ++choice; | |
1220 redraw = 1; | |
1221 ezPlaySample("shift_wav", 128); | |
1222 } | |
1223 if (newKeys & VKEY_ROTL) { | |
1224 choice = 0; | |
1225 redraw = 1; | |
1226 ezPlaySample("rotate_wav", 128); | |
1227 } | |
1228 if (newKeys & VKEY_ROTR) { | |
1229 done = 1; | |
1230 ezPlaySample("line_wav", 128); | |
1231 } | |
1232 if (choice < 0) { | |
1233 choice += N_TITLE_ACTIONS; | |
1234 } | |
1235 if (choice >= N_TITLE_ACTIONS) { | |
1236 choice -= N_TITLE_ACTIONS; | |
1237 } | |
1238 | |
1239 lastKeys = keys; | |
1240 | |
1241 if (!redraw) { | |
1242 rest(30); | |
1243 } | |
1244 if (wantsClose) { | |
1245 done = 1; | |
1246 choice = 0; | |
1247 } | |
1248 } | |
1249 destroy_bitmap(back); | |
1250 return choice; | |
1251 } | |
1252 | |
1253 | |
1254 void setupWindow(void) { | |
1255 set_window_title("LOCKJAW"); | |
1256 bgColor = makecol(255, 255, 255); | |
1257 pfFgColor = bgColor; | |
1258 hiliteColor = makecol(255, 255, 204); | |
1259 refreshRate = get_refresh_rate(); | |
1260 } | |
1261 | |
1262 void drawCoprNotice(void) { | |
1263 clear_to_color(screen, pfBgColor); | |
1264 for (int i = 0; i < MENU_COPR_NOTICE_LINES; ++i) { | |
1265 textout_ex(screen, font, menuCoprNotice[i], | |
1266 16, 580 + 12 * (i - MENU_COPR_NOTICE_LINES), | |
1267 pfFgColor, -1); | |
1268 } | |
1269 } | |
1270 | |
1271 | |
1272 /** | |
1273 * Destroys the back buffers for both playfields. | |
1274 */ | |
1275 static void destroyBackBuf(LJPCView *plat) { | |
1276 for (int i = 0; i < MAX_PLAYERS; ++i) { | |
1277 if (plat->back) { | |
1278 destroy_bitmap(plat->back); | |
1279 plat->back = NULL; | |
1280 } | |
1281 } | |
1282 if (plat->skin->fullback) { | |
1283 destroy_bitmap(plat->skin->fullback); | |
1284 plat->skin->fullback = NULL; | |
1285 } | |
1286 } | |
1287 | |
1288 /** | |
1289 * Creates the back buffers for both playfields. | |
1290 */ | |
1291 static int createBackBuf(LJView *v) { | |
1292 v->plat->skin->fullback = create_system_bitmap(SCREEN_W, SCREEN_H); | |
1293 if(!v->plat->skin->fullback) { | |
1294 allegro_message("Could not create back buffer.\n"); | |
1295 return 0; | |
1296 } | |
1297 | |
1298 int blkW = v->plat->skin->blkW; | |
1299 int blkH = v->plat->skin->blkH; | |
1300 int y = v->plat->skin->baseY | |
1301 - v->plat->skin->pfElev | |
1302 - blkH * v->field->ceiling; | |
1303 | |
1304 for (int i = 0; i < MAX_PLAYERS; ++i) { | |
1305 int x = v->plat->skin->baseX | |
1306 + blkW * v[i].field->leftWall; | |
1307 | |
1308 v[i].plat->back = create_sub_bitmap(v->plat->skin->fullback, | |
1309 x + SCREEN_W / 2 * i, | |
1310 y, | |
1311 v->plat->skin->blkW * LJ_PF_WID, | |
1312 v->plat->skin->blkH * LJ_PF_VIS_HT); | |
1313 if (!v[i].plat->back) { | |
1314 destroyBackBuf(v->plat); | |
1315 return 0; | |
1316 } | |
1317 } | |
1318 return 1; | |
1319 } | |
1320 | |
1321 /** | |
1322 * Destroys all system bitmaps that a given view owns. | |
1323 * Useful before changing screen mode. | |
1324 */ | |
1325 void destroySystemBitmaps(LJPCView *plat) { | |
1326 destroyBackBuf(plat); | |
1327 if (plat->skin->connBlocks) { | |
1328 destroy_bitmap(plat->skin->connBlocks); | |
1329 plat->skin->connBlocks = NULL; | |
1330 } | |
1331 } | |
1332 | |
1333 int openWindow(int windowed) | |
1334 { | |
1335 int depth = desktop_color_depth(); | |
1336 int card = windowed ? GFX_AUTODETECT_WINDOWED : GFX_AUTODETECT_FULLSCREEN; | |
1337 | |
1338 /* Reference implementation for Allegro is not compatible with | |
1339 indexed color. */ | |
1340 if (depth < 15) { | |
1341 depth = 16; | |
1342 } | |
1343 | |
1344 // Full screen procedure | |
1345 set_color_depth(depth); | |
1346 if (set_gfx_mode(card, skinW, skinH, 0, 0) == 0) { | |
1347 setupWindow(); | |
1348 return 0; | |
1349 } | |
1350 | |
1351 // Windows can't tell 16 bit from 15 bit. If desktop color depth is reported as 16, try 15 too. | |
1352 if (depth == 16) { | |
1353 set_color_depth(15); | |
1354 if (set_gfx_mode(card, skinW, skinH, 0, 0) == 0) { | |
1355 setupWindow(); | |
1356 return 0; | |
1357 } | |
1358 } | |
1359 | |
1360 return -1; | |
1361 } | |
1362 | |
1363 BITMAP *loadConnections(const char *filename, int blkW, int blkH) { | |
1364 BITMAP *src = load_bitmap(filename, NULL); | |
1365 | |
1366 if (!src) { | |
1367 return NULL; | |
1368 } | |
1369 BITMAP *dst = create_system_bitmap(blkW*16, blkH*16); | |
1370 if (!dst) { | |
1371 destroy_bitmap(src); | |
1372 return NULL; | |
1373 } | |
1374 acquire_bitmap(dst); | |
1375 for (unsigned int col = 0; col < 16; ++col) { | |
1376 unsigned int srcXBase = (col & 0x03) * blkW * 2; | |
1377 unsigned int srcYBase = (col >> 2) * blkH * 2; | |
1378 unsigned int dstYBase = col * blkH; | |
1379 for (unsigned int conn = 0; conn < 16; ++conn) { | |
1380 unsigned int dstXBase = conn * blkW; | |
1381 unsigned int topSegY = (conn & CONNECT_U) ? blkH : 0; | |
1382 unsigned int botSegY = (conn & CONNECT_D) ? blkH/2 : 3*blkH/2; | |
1383 unsigned int leftSegX = (conn & CONNECT_L) ? blkW : 0; | |
1384 unsigned int rightSegX = (conn & CONNECT_R) ? blkW/2 : 3*blkW/2; | |
1385 blit(src, dst, | |
1386 srcXBase + leftSegX, srcYBase + topSegY, | |
1387 dstXBase + 0, dstYBase + 0, | |
1388 blkW/2, blkH/2); | |
1389 blit(src, dst, | |
1390 srcXBase + rightSegX, srcYBase + topSegY, | |
1391 dstXBase + blkW/2, dstYBase + 0, | |
1392 blkW/2, blkH/2); | |
1393 blit(src, dst, | |
1394 srcXBase + leftSegX, srcYBase + botSegY, | |
1395 dstXBase + 0, dstYBase + blkH/2, | |
1396 blkW/2, blkH/2); | |
1397 blit(src, dst, | |
1398 srcXBase + rightSegX, srcYBase + botSegY, | |
1399 dstXBase + blkW/2, dstYBase + blkH/2, | |
1400 blkW/2, blkH/2); | |
1401 } | |
1402 } | |
1403 release_bitmap(dst); | |
1404 destroy_bitmap(src); | |
1405 return dst; | |
1406 } | |
1407 | |
1408 void closeWindow(void) { | |
1409 set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); | |
1410 } | |
1411 | |
1412 static void mainCleanup(LJPCView *v) { | |
1413 destroyBackBuf(v); | |
1414 if (v->skin->blocks) { | |
1415 destroy_bitmap(v->skin->blocks); | |
1416 } | |
1417 if (v->skin->connBlocks) { | |
1418 destroy_bitmap(v->skin->connBlocks); | |
1419 } | |
1420 LJMusic_delete(v->skin->bgm); | |
1421 if (withSound) { | |
1422 remove_sound(); | |
1423 } | |
1424 closeWindow(); | |
1425 } | |
1426 | |
1427 /** | |
1428 * Resets all skin settings to their initial values | |
1429 * so that skins can override them. | |
1430 */ | |
1431 void loadSkinDefaults(LJPCSkin *s) { | |
1432 ustrzcpy(ljblocksSRSName, sizeof(ljblocksSRSName) - 1, | |
1433 "ljblocks.bmp"); | |
1434 ustrzcpy(ljblocksSegaName, sizeof(ljblocksSegaName) - 1, | |
1435 "ljblocks-sega.bmp"); | |
1436 ustrzcpy(ljconnSRSName, sizeof(ljconnSRSName) - 1, | |
1437 "ljconn.bmp"); | |
1438 ustrzcpy(ljconnSegaName, sizeof(ljconnSegaName) - 1, | |
1439 "ljconn-sega.bmp"); | |
1440 ustrzcpy(ljbgName, sizeof(ljbgName) - 1, | |
1441 "ljbg.jpg"); | |
1442 ustrzcpy(menubgName, sizeof(ljbgName) - 1, | |
1443 "menubg.jpg"); | |
1444 ustrzcpy(bgmName, sizeof(bgmName) - 1, | |
1445 "bgm.s3m"); | |
1446 ustrzcpy(bgmRhythmName, sizeof(bgmRhythmName) - 1, | |
1447 "bgm-rhythm.s3m"); | |
1448 bgmLoopPoint = 0; | |
1449 bgmReadyGo = 0; | |
1450 bgmVolume = 128; | |
1451 bgColor = makecol(255, 255, 255); | |
1452 fgColor = makecol(0, 0, 0); | |
1453 hiliteColor = makecol(255, 255, 204); | |
1454 pfBgColor = makecol(0, 0, 0); | |
1455 pfFgColor = makecol(255, 255, 255); | |
1456 s->blkW = 24; | |
1457 s->blkH = 24; | |
1458 s->transparentPF = 0; | |
1459 s->shiftScale = 0; | |
1460 s->baseX = 0; | |
1461 s->nextPos = 0; | |
1462 } | |
1463 | |
1464 /** | |
1465 * Converts a single hexadecimal digit to its value. | |
1466 * @param in USASCII/Unicode value of hex digit character | |
1467 * @return value | |
1468 */ | |
1469 int hexDigitValue(int in) { | |
1470 if (in >= '0' && in <= '9') { | |
1471 return in - '0'; | |
1472 } else if (in >= 'A' && in <= 'F') { | |
1473 return in - 'A' + 10; | |
1474 } else if (in >= 'a' && in <= 'f') { | |
1475 return in - 'a' + 10; | |
1476 } else { | |
1477 return -1; | |
1478 } | |
1479 } | |
1480 | |
1481 int translateComponent(const char **in, size_t nDigits) { | |
1482 const char *here = *in; | |
1483 int hi = hexDigitValue(*here++); | |
1484 int lo = nDigits > 3 ? hexDigitValue(*here++) : hi; | |
1485 *in = here; | |
1486 if (hi >= 0 && lo >= 0) { | |
1487 return hi * 16 + lo; | |
1488 } else { | |
1489 return -1; | |
1490 } | |
1491 } | |
1492 | |
1493 /** | |
1494 * Interprets a hexadecimal color specifier. | |
1495 * @param in hexadecimal color specifier, in #ABC or #AABBCC format | |
1496 * @return Allegro device-dependent color, in makecol() format | |
1497 */ | |
1498 int translateColor(const char *in) { | |
1499 size_t nDigits = 0; | |
1500 | |
1501 // Verify and skip # | |
1502 if (*in != '#') { | |
1503 return -1; | |
1504 } | |
1505 ++in; | |
1506 | |
1507 // Determine whether we have a 3-digit color or a 6-digit color | |
1508 for (const char *here = in; | |
1509 hexDigitValue(*here) >= 0; | |
1510 ++here) { | |
1511 ++nDigits; | |
1512 } | |
1513 if (nDigits != 3 && nDigits != 6) { | |
1514 return -1; | |
1515 } | |
1516 | |
1517 int red = translateComponent(&in, nDigits); | |
1518 int green = translateComponent(&in, nDigits); | |
1519 int blue = translateComponent(&in, nDigits); | |
1520 if (red >= 0 && green >= 0 && blue >= 0) { | |
1521 return makecol(red, green, blue); | |
1522 } else { | |
1523 return -1; | |
1524 } | |
1525 }; | |
1526 | |
1527 /** | |
1528 * Loads skin parameters from the file. | |
1529 * @param skinName Name of skin ini file | |
1530 */ | |
1531 int loadSkinFile(LJPCSkin *s, const char *skinName) { | |
1532 | |
1533 // Don't use ljfopen here because lj.ini specifies the absolute skin file | |
1534 FILE *fp = fopen(skinName, "rt"); | |
1535 char key[1024], var[1024], val[1024], input_buf[1024]; | |
1536 | |
1537 key[0] = 0; | |
1538 var[0] = 0; | |
1539 val[0] = 0; | |
1540 loadSkinDefaults(s); | |
1541 | |
1542 if (!fp) return 0; | |
1543 while(1) { | |
1544 int rval; | |
1545 | |
1546 if(!fgets (input_buf, sizeof(input_buf), fp)) | |
1547 break; | |
1548 rval = parse_ini_line(input_buf, key, var, val); | |
1549 | |
1550 if(!ustrcmp ("ljblocksSRS", var)) { | |
1551 ustrzcpy(ljblocksSRSName, sizeof(ljblocksSRSName) - 1, val); | |
1552 } | |
1553 else if(!ustrcmp ("ljblocksSega", var)) { | |
1554 ustrzcpy(ljblocksSegaName, sizeof(ljblocksSegaName) - 1, val); | |
1555 } | |
1556 else if(!ustrcmp ("ljconnSRS", var)) { | |
1557 ustrzcpy(ljconnSRSName, sizeof(ljconnSRSName) - 1, val); | |
1558 } | |
1559 else if(!ustrcmp ("ljconnSega", var)) { | |
1560 ustrzcpy(ljconnSegaName, sizeof(ljconnSegaName) - 1, val); | |
1561 } | |
1562 else if(!ustrcmp ("bgm", var)) { | |
1563 ustrzcpy(bgmName, sizeof(bgmName) - 1, val); | |
1564 } | |
1565 else if(!ustrcmp ("bgmRhythm", var)) { | |
1566 ustrzcpy(bgmRhythmName, sizeof(bgmRhythmName) - 1, val); | |
1567 } | |
1568 else if(!ustrcmp ("ljbg", var)) { | |
1569 ustrzcpy(ljbgName, sizeof(ljbgName) - 1, val); | |
1570 } | |
1571 else if(!ustrcmp ("bgmLoopPoint", var)) { | |
1572 unsigned long int c = strtoul(val, NULL, 0); | |
1573 if (c >= 0) { | |
1574 bgmLoopPoint = c; | |
1575 } | |
1576 } | |
1577 else if(!ustrcmp ("bgmVolume", var)) { | |
1578 unsigned long int c = strtol(val, NULL, 0); | |
1579 if (c >= 0) { | |
1580 bgmVolume = c; | |
1581 } | |
1582 } | |
1583 else if(!ustrcmp ("bgmReadyGo", var)) { | |
1584 unsigned long int c = strtoul(val, NULL, 0); | |
1585 if (c >= 0) { | |
1586 bgmReadyGo = c; | |
1587 } | |
1588 } | |
1589 else if(!ustrcmp ("pfbgcolor", var)) { | |
1590 int c = translateColor(val); | |
1591 if (c >= 0) { | |
1592 pfBgColor = c; | |
1593 } | |
1594 } | |
1595 else if(!ustrcmp ("pfcolor", var)) { | |
1596 int c = translateColor(val); | |
1597 if (c >= 0) { | |
1598 pfFgColor = c; | |
1599 } | |
1600 } | |
1601 else if(!ustrcmp ("bgcolor", var)) { | |
1602 int c = translateColor(val); | |
1603 if (c >= 0) { | |
1604 bgColor = c; | |
1605 } | |
1606 } | |
1607 else if(!ustrcmp ("color", var)) { | |
1608 int c = translateColor(val); | |
1609 if (c >= 0) { | |
1610 fgColor = c; | |
1611 } | |
1612 } | |
1613 else if(!ustrcmp ("hilitecolor", var)) { | |
1614 int c = translateColor(val); | |
1615 if (c >= 0) { | |
1616 hiliteColor = c; | |
1617 } | |
1618 } | |
1619 else if(!ustrcmp ("blkW", var)) { | |
1620 int c = strtol(val, NULL, 0); | |
1621 if (c >= 0) { | |
1622 s->blkW = c; | |
1623 } | |
1624 } | |
1625 else if(!ustrcmp ("blkH", var)) { | |
1626 int c = strtol(val, NULL, 0); | |
1627 if (c >= 0) { | |
1628 s->blkH = c; | |
1629 } | |
1630 } | |
1631 else if(!ustrcmp ("transparentPF", var)) { | |
1632 int c = atoi(val); | |
1633 if (c >= 0) { | |
1634 s->transparentPF = c; | |
1635 } | |
1636 } | |
1637 else if(!ustrcmp ("shiftScale", var)) { | |
1638 int c = atoi(val); | |
1639 if (c >= 0) { | |
1640 s->shiftScale = c; | |
1641 } | |
1642 } | |
1643 else if(!ustrcmp ("baseX", var)) { | |
1644 int c = atoi(val); | |
1645 if (c >= 0) { | |
1646 s->baseX = c; | |
1647 } | |
1648 } | |
1649 else if(!ustrcmp ("nextPos", var)) { | |
1650 int c = atoi(val); | |
1651 if (c >= 0) { | |
1652 s->nextPos = c; | |
1653 } | |
1654 } | |
1655 else if(!ustrcmp ("wndW", var)) { | |
1656 unsigned long int c = strtol(val, NULL, 0); | |
1657 if (c >= 0) { | |
1658 skinW = c; | |
1659 } | |
1660 } | |
1661 else if(!ustrcmp ("wndH", var)) { | |
1662 unsigned long int c = strtoul(val, NULL, 0); | |
1663 if (c >= 0) { | |
1664 skinH = c; | |
1665 } | |
1666 } | |
1667 | |
1668 } | |
1669 fclose(fp); | |
1670 ljpathSetSkinFolder(skinName); | |
1671 return 0; | |
1672 } | |
1673 | |
1674 static void drawProgressSegment(int min, int max) { | |
1675 min = min * SCREEN_W / 100; | |
1676 max = max * SCREEN_W / 100; | |
1677 int blue = makecol(0, 0, 255); | |
1678 rectfill(screen, min, SCREEN_H - 8, max - 1, SCREEN_H - 5, blue); | |
1679 int orange = makecol(255, 128, 0); | |
1680 rectfill(screen, min, SCREEN_H - 4, max - 1, SCREEN_H - 1, orange); | |
1681 } | |
1682 | |
1683 static int loadSkin(LJView *v, const char *skinName) { | |
1684 BITMAP *bmp; | |
1685 const LJRotSystem *rs = rotSystems[v->field->rotationSystem]; | |
1686 int colorScheme = rs->colorScheme; | |
1687 | |
1688 rectfill(screen, 0, 592, 799, 599, 0); | |
1689 loadSkinFile(v->plat->skin, skinName); | |
1690 | |
1691 destroyBackBuf(v->plat); | |
1692 if (!createBackBuf(v)) { | |
1693 allegro_message("Could not create back buffer.\n"); | |
1694 return 1; | |
1695 } | |
1696 | |
1697 drawProgressSegment(0, 20); | |
1698 | |
1699 // Load background image | |
1700 char path[PATH_MAX]; | |
1701 bmp = ljpathFind_r(path, ljbgName) | |
1702 ? load_bitmap(path, NULL) : NULL; | |
1703 if (v->plat->skin->bg) { | |
1704 destroy_bitmap(v->plat->skin->bg); | |
1705 } | |
1706 if (bmp) { | |
1707 | |
1708 // If the image size doesn't match the window size, resize it | |
1709 if (bmp->w != SCREEN_W || bmp->h != SCREEN_H) { | |
1710 BITMAP *resized = create_bitmap(SCREEN_W, SCREEN_H); | |
1711 | |
1712 if (resized) { | |
1713 stretch_blit(bmp, resized, 0, 0, bmp->w, bmp->h, | |
1714 0, 0, SCREEN_W, SCREEN_H); | |
1715 destroy_bitmap(bmp); | |
1716 } | |
1717 if (bmp) { | |
1718 bmp = resized; | |
1719 } else { | |
1720 allegro_message("Background image \"%s\" resize failed.\n", | |
1721 ljbgName); | |
1722 } | |
1723 } | |
1724 } | |
1725 if(!bmp) { | |
1726 bmp = create_bitmap(SCREEN_W, SCREEN_H); | |
1727 if (bmp) { | |
1728 allegro_message("Background image \"%s\" not found.\n" | |
1729 "Using plain background instead.\n", | |
1730 ljbgName); | |
1731 clear_to_color(bmp, bgColor); | |
1732 } else { | |
1733 allegro_message("Background image \"%s\" not found.\n", | |
1734 ljbgName); | |
1735 return 0; | |
1736 } | |
1737 } | |
1738 v->plat->skin->bg = bmp; | |
1739 drawProgressSegment(20, 40); | |
1740 | |
1741 // load block images | |
1742 if (v->plat->skin->blocks) { | |
1743 destroy_bitmap(v->plat->skin->blocks); | |
1744 } | |
1745 bmp = ljpathFind_r(path, colorScheme | |
1746 ? ljblocksSegaName | |
1747 : ljblocksSRSName) | |
1748 ? load_bitmap(path, NULL) : NULL; | |
1749 v->plat->skin->blocks = bmp; | |
1750 if(!v->plat->skin->blocks) { | |
1751 allegro_message("Background image \"%s\" not found.\n", | |
1752 ljbgName); | |
1753 return 0; | |
1754 } | |
1755 drawProgressSegment(40, 60); | |
1756 | |
1757 // load connected block images | |
1758 if (v->plat->skin->connBlocks) { | |
1759 destroy_bitmap(v->plat->skin->connBlocks); | |
1760 } | |
1761 bmp = ljpathFind_r(path, colorScheme | |
1762 ? ljconnSegaName | |
1763 : ljconnSRSName) | |
1764 ? loadConnections(path, | |
1765 v->plat->skin->blkW, | |
1766 v->plat->skin->blkH) | |
1767 : NULL; | |
1768 v->plat->skin->connBlocks = bmp; | |
1769 drawProgressSegment(60, 80); | |
1770 | |
1771 // load music | |
1772 int isRhythm = (v->field->speedState.curve == LJSPD_RHYTHM); | |
1773 if (ljpathFind_r(path, isRhythm ? bgmRhythmName : bgmName)) { | |
1774 LJMusic_load(v->plat->skin->bgm, path); | |
1775 } | |
1776 if (!isRhythm) { | |
1777 LJMusic_setLoop(v->plat->skin->bgm, bgmLoopPoint); | |
1778 } | |
1779 drawProgressSegment(80, 100); | |
1780 return 1; | |
1781 } | |
1782 | |
1783 void drop_mouse(void) { | |
1784 while (mouse_y < SCREEN_H - 25) { | |
1785 position_mouse(mouse_x, mouse_y + 20); | |
1786 rest(10); | |
1787 } | |
1788 ezPlaySample("land_wav", 192); | |
1789 position_mouse(mouse_x, SCREEN_H - 5); | |
1790 ezPlaySample("lock_wav", 192); | |
1791 } | |
1792 | |
1793 int pickReplay(void) { | |
1794 FONT *oldFont = font; | |
1795 font = (FONT *)aver16; | |
1796 install_mouse(); | |
1797 int got = file_select_ex("Choose a demo:", demoFilename, "ljm", sizeof(demoFilename), 600, 400); | |
1798 drop_mouse(); | |
1799 remove_mouse(); | |
1800 font = oldFont; | |
1801 return got ? 0 : -1; | |
1802 } | |
1803 | |
1804 void badReplay(void) { | |
1805 acquire_screen(); | |
1806 clear_to_color(screen, bgColor); | |
1807 textout_ex(screen, aver32, "The demo", 100, 100, fgColor, -1); | |
1808 textout_ex(screen, aver16, demoFilename, 100, 130, fgColor, -1); | |
1809 textout_ex(screen, aver32, "could not be played because it was", 100, 150, fgColor, -1); | |
1810 textout_ex(screen, aver32, "recorded with a different version", 100, 180, fgColor, -1); | |
1811 textout_ex(screen, aver32, "of LOCKJAW software.", 100, 210, fgColor, -1); | |
1812 release_screen(); | |
1813 | |
1814 LJBits lastKeys = ~0; | |
1815 LJBits keys, newKeys = 0; | |
1816 | |
1817 do { | |
1818 keys = menuReadPad(); | |
1819 newKeys = keys & ~lastKeys; | |
1820 lastKeys = keys; | |
1821 rest(30); | |
1822 } while (!(newKeys & (VKEY_ROTL | VKEY_ROTR))); | |
1823 } | |
1824 | |
1825 int pickSkin(void) { | |
1826 FONT *oldFont = font; | |
1827 font = (FONT *)aver16; | |
1828 install_mouse(); | |
1829 int got = file_select_ex("Choose a skin:", skinName, "skin", sizeof(skinName), 600, 400); | |
1830 drop_mouse(); | |
1831 remove_mouse(); | |
1832 font = oldFont; | |
1833 return got ? 0 : -1; | |
1834 } | |
1835 | |
1836 void calcElev(LJView *v) { | |
1837 int blkH = v->plat->skin->blkH; | |
1838 int ceiling = v->field->ceiling; | |
1839 int elev = (LJ_PF_VIS_HT - ceiling) * blkH; | |
1840 | |
1841 if (elev > 480 - ceiling * blkH) { | |
1842 elev = 480 - ceiling * blkH; | |
1843 } | |
1844 if (elev < 0) { | |
1845 elev = 0; | |
1846 } | |
1847 v->plat->skin->pfElev = elev; | |
1848 } | |
1849 | |
1850 int main(const int argc, const char *const *argv) { | |
1851 const char *cmdLineDemo = NULL; | |
1852 int lastPreset = -2; // start out with full custom | |
1853 LJPCSkin skin = { | |
1854 .baseY = 552, | |
1855 .blkW = 24, | |
1856 .blkH = 24 | |
1857 }; | |
1858 LJField p[MAX_PLAYERS]; | |
1859 LJControl control[2] = { | |
1860 { | |
1861 .replaySrc = 0, | |
1862 .replayDst = 0 | |
1863 } | |
1864 }; | |
1865 LJPCView platView[MAX_PLAYERS] = { | |
1866 { | |
1867 .skin = &skin | |
1868 }, | |
1869 { | |
1870 .skin = &skin | |
1871 } | |
1872 }; | |
1873 LJView mainView[MAX_PLAYERS] = { | |
1874 { | |
1875 .field = &p[0], | |
1876 .control = &control[0], | |
1877 .plat = &platView[0], | |
1878 .backDirty = ~0 | |
1879 }, | |
1880 { | |
1881 .field = &p[1], | |
1882 .control = &control[1], | |
1883 .plat = &platView[1], | |
1884 .backDirty = ~0, | |
1885 } | |
1886 }; | |
1887 | |
1888 struct LJPrefs prefs = { | |
1889 .number = { | |
1890 [OPTIONS_TRAILS] = 1, | |
1891 [OPTIONS_AUTO_PAUSE] = 1, | |
1892 [OPTIONS_AUTO_RECORD] = 0, | |
1893 [OPTIONS_WINDOWED] = 1 | |
1894 } | |
1895 }; | |
1896 | |
1897 // as of 0.46, we're starting to make it a bit more 2-player-clean | |
1898 int nPlayers = 1; | |
1899 | |
1900 allegro_init(); | |
1901 ljpathInit(argc > 0 ? argv[0] : "."); | |
1902 install_timer(); | |
1903 initOptions(prefs.number); | |
1904 loadOptions(&prefs); | |
1905 loadSkinFile(&skin, skinName); | |
1906 | |
1907 if (argc > 1) { | |
1908 if (argv[1][0] == '-') { | |
1909 if (!ustrcmp("--help", argv[1]) | |
1910 || !ustrcmp("-h", argv[1])) { | |
1911 allegro_message("Usage: lj [DEMOFILE]\n"); | |
1912 return 0; | |
1913 } | |
1914 } else { | |
1915 cmdLineDemo = argv[1]; | |
1916 } | |
1917 } | |
1918 | |
1919 if (openWindow(prefs.number[OPTIONS_WINDOWED]) != 0) { | |
1920 allegro_message("LOCKJAW fatal error: Could not open an %dx%d pixel %s.\n" | |
1921 "Trying %s next time.\n", | |
1922 skinW, skinH, | |
1923 prefs.number[OPTIONS_WINDOWED] ? "window": "screen mode", | |
1924 prefs.number[OPTIONS_WINDOWED] ? "the full screen": "a window"); | |
1925 prefs.number[OPTIONS_WINDOWED] = !prefs.number[OPTIONS_WINDOWED]; | |
1926 saveOptions(&prefs); | |
1927 return EXIT_FAILURE; | |
1928 } | |
1929 drawCoprNotice(); | |
1930 LOCK_FUNCTION(incCurTime); | |
1931 LOCK_VARIABLE(curTime); | |
1932 install_int_ex(incCurTime, BPM_TO_TIMER(LJ_TICK_RATE)); | |
1933 | |
1934 jpgalleg_init(); | |
1935 set_color_conversion(COLORCONV_NONE); | |
1936 { | |
1937 char path[PATH_MAX]; | |
1938 if (ljpathFind_r(path, "lj.dat")) { | |
1939 dat = load_datafile(path); | |
1940 } | |
1941 } | |
1942 set_color_conversion(COLORCONV_TOTAL); | |
1943 if(!dat) { | |
1944 closeWindow(); | |
1945 allegro_message("LOCKJAW fatal error: Could not load datafile lj.dat\n"); | |
1946 return 1; | |
1947 } | |
1948 | |
1949 { | |
1950 const DATAFILE *aver16dat = find_datafile_object(dat, "Aver16_bmp"); | |
1951 aver16 = aver16dat ? aver16dat->dat : font; | |
1952 const DATAFILE *aver32dat = find_datafile_object(dat, "Aver32_bmp"); | |
1953 aver32 = aver32dat ? aver32dat->dat : aver16; | |
1954 } | |
1955 | |
1956 LOCK_FUNCTION(amnesia); | |
1957 LOCK_VARIABLE(redrawWholeScreen); | |
1958 | |
1959 // If we can be notified on switching out, take this notification. | |
1960 if (set_display_switch_mode(SWITCH_BACKGROUND) >= 0 | |
1961 || set_display_switch_mode(SWITCH_BACKAMNESIA) >= 0) { | |
1962 set_display_switch_callback(SWITCH_OUT, requestPause); | |
1963 } | |
1964 set_display_switch_callback(SWITCH_IN, amnesia); | |
1965 | |
1966 install_keyboard(); | |
1967 initKeys(); | |
1968 | |
1969 for (int i = 0; i < MAX_PLAYERS; ++i) { | |
1970 p[i].seed = time(NULL) + 123456789*i; | |
1971 } | |
1972 | |
1973 reserve_voices(8, 0); | |
1974 set_volume_per_voice(0); | |
1975 #ifdef ALLEGRO_WINDOWS | |
1976 // Under Windows, use the Allegro mixer because on my machine | |
1977 // and probably others, the built-in mixer will replace the very | |
1978 // beginning of one sound with the end of the last sound played | |
1979 // on that voice. | |
1980 withSound = !install_sound(DIGI_DIRECTAMX(0), MIDI_NONE, NULL); | |
1981 #else | |
1982 withSound = !install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL); | |
1983 #endif | |
1984 skin.bgm = LJMusic_new(); | |
1985 { | |
1986 char path[PATH_MAX]; | |
1987 if (ljpathFind_r(path, "sound.dat")) { | |
1988 sound_dat = load_datafile(path); | |
1989 } | |
1990 } | |
1991 | |
1992 for (int i = 0; i < MAX_PLAYERS; ++i) { | |
1993 unpackOptions(&mainView[i], &prefs); | |
1994 } | |
1995 if (loadSkin(&mainView[0], skinName) < 0) { | |
1996 mainCleanup(platView); | |
1997 return EXIT_FAILURE; | |
1998 } else { | |
1999 | |
2000 } | |
2001 | |
2002 if(!skin.blocks) { | |
2003 mainCleanup(platView); | |
2004 allegro_message("Blocks image \"%s\" not found.\n", | |
2005 p[0].rotationSystem | |
2006 ? ljblocksSegaName | |
2007 : ljblocksSRSName); | |
2008 return 1; | |
2009 } | |
2010 | |
2011 srand(time(NULL)); | |
2012 | |
2013 // Wait for copyright notice to be displayed "conspicuously" | |
2014 if (curTime < 180) { | |
2015 rest(3100 - curTime * 16); | |
2016 } | |
2017 | |
2018 for (int action = cmdLineDemo ? TITLE_REPLAY : title(); | |
2019 action > TITLE_EXIT && !wantsClose; | |
2020 action = title()) { | |
2021 switch (action) { | |
2022 case TITLE_PLAY: | |
2023 for (int preset = getPreset(lastPreset); | |
2024 preset != -1 && !wantsClose; | |
2025 preset = getPreset(lastPreset)) | |
2026 { | |
2027 lastPreset = preset; | |
2028 for (int i = 0; i < MAX_PLAYERS; ++i) { | |
2029 unpackOptions(&mainView[i], &prefs); | |
2030 } | |
2031 if (preset >= 0) { | |
2032 presetStart(); | |
2033 presetAdd(preset); | |
2034 for (int i = 0; i < MAX_PLAYERS; ++i) { | |
2035 unpackOptions(&mainView[i], &prefs); | |
2036 presetFinish(&mainView[i]); | |
2037 } | |
2038 } | |
2039 // reload the skin | |
2040 if (loadSkin(&mainView[0], skinName) < 0) { | |
2041 mainCleanup(platView); | |
2042 return EXIT_FAILURE; | |
2043 }; | |
2044 for (int i = 0; i < nPlayers; ++i) { | |
2045 calcElev(&mainView[0]); | |
2046 pcInit(&mainView[0], &prefs); | |
2047 } | |
2048 wantPause = 0; | |
2049 platView[0].wantRecord = prefs.number[OPTIONS_AUTO_RECORD]; | |
2050 LJMusic_start(skin.bgm, | |
2051 4096, // mix buffer size | |
2052 bgmVolume); // volume scale | |
2053 | |
2054 LJView *const players[MAX_PLAYERS] = {&mainView[0], &mainView[1]}; | |
2055 play(players, nPlayers); | |
2056 LJMusic_stop(skin.bgm); | |
2057 if (!wantsClose) { | |
2058 gameOverAnimation(&platView[0], &p[0], | |
2059 control[0].countdown <= 0); | |
2060 debrief(&mainView[0]); | |
2061 } | |
2062 } | |
2063 break; | |
2064 | |
2065 case TITLE_REPLAY: | |
2066 { | |
2067 if (cmdLineDemo) { | |
2068 ustrzcpy(demoFilename, sizeof(demoFilename) - 1, | |
2069 cmdLineDemo); | |
2070 cmdLineDemo = NULL; | |
2071 } else if (pickReplay() < 0) { | |
2072 break; | |
2073 } | |
2074 | |
2075 unpackOptions(&mainView[0], &prefs); | |
2076 calcElev(&mainView[0]); | |
2077 pcInit(&mainView[0], &prefs); | |
2078 wantPause = 0; | |
2079 platView[0].wantRecord = 0; | |
2080 LJMusic_start(skin.bgm, | |
2081 4096, // mix buffer size | |
2082 bgmVolume); // volume scale | |
2083 | |
2084 LJView *const players[2] = {&mainView[0], &mainView[1]}; | |
2085 | |
2086 p->gimmick = -1; // gimmick must be < 0 to activate replay | |
2087 play(players, 1); | |
2088 LJMusic_stop(skin.bgm); | |
2089 if (p[0].gimmick < 0) { | |
2090 badReplay(); | |
2091 } else { | |
2092 if (!wantsClose) { | |
2093 gameOverAnimation(&platView[0], &p[0], | |
2094 control[0].countdown <= 0); | |
2095 debrief(&mainView[0]); | |
2096 } | |
2097 } | |
2098 } | |
2099 break; | |
2100 | |
2101 case TITLE_SKIN: | |
2102 pickSkin(); | |
2103 | |
2104 // if resolution changed, reopen the window | |
2105 { | |
2106 int oldW = skinW; | |
2107 int oldH = skinH; | |
2108 loadSkinFile(&skin, skinName); | |
2109 if (skinH != oldH || skinW != oldW) { | |
2110 destroySystemBitmaps(&platView[0]); | |
2111 openWindow(prefs.number[OPTIONS_WINDOWED]); | |
2112 } | |
2113 } | |
2114 | |
2115 // reload the skin | |
2116 if (loadSkin(&mainView[0], skinName) < 0) { | |
2117 mainCleanup(platView); | |
2118 return EXIT_FAILURE; | |
2119 }; | |
2120 | |
2121 // save options | |
2122 saveOptions(&prefs); | |
2123 break; | |
2124 | |
2125 case TITLE_OPTIONS: | |
2126 { | |
2127 int oldWindowed = prefs.number[OPTIONS_WINDOWED]; | |
2128 options(&mainView[0], prefs.number); | |
2129 if (oldWindowed != prefs.number[OPTIONS_WINDOWED]) { | |
2130 destroySystemBitmaps(&platView[0]); | |
2131 openWindow(prefs.number[OPTIONS_WINDOWED]); | |
2132 } | |
2133 } | |
2134 saveOptions(&prefs); | |
2135 | |
2136 // reload the skin if the player changed the rotation system | |
2137 unpackOptions(&mainView[0], &prefs); | |
2138 if (wantsClose) { | |
2139 break; | |
2140 } | |
2141 if (loadSkin(&mainView[0], skinName) < 0) { | |
2142 mainCleanup(platView); | |
2143 return EXIT_FAILURE; | |
2144 }; | |
2145 break; | |
2146 | |
2147 case TITLE_KEYS: | |
2148 configureKeys(); | |
2149 break; | |
2150 | |
2151 } | |
2152 } | |
2153 | |
2154 mainCleanup(platView); | |
2155 return 0; | |
2156 } END_OF_MAIN(); |