view src/ljgbads.inc @ 2:80a2761bd3a4

change DS keys (add alt. rotate)
author paulo@localhost
date Mon, 23 Mar 2009 01:19:12 -0700
parents
children
line source
1 /* DS/GBA frontend for LOCKJAW, an implementation of the Soviet Mind Game
3 Copyright (C) 2006-2007 Damian Yerrick <tepples+lj@spamcop.net>
5 This work is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 Original game concept and design by Alexey Pajitnov.
20 The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg,
21 or The Tetris Company LLC.
23 */
25 #include "fontdraw.h"
27 unsigned char wantPause;
28 volatile char redrawWholeScreen = 0;
30 void finishSprites();
32 void cls(void) {
33 for (int y = 0; y < SCREEN_H; ++y) {
34 for (int x = 0; x < SCREEN_W; ++x) {
35 MAP[31][y][x] = ' ';
36 }
37 }
38 }
40 void drawFrame(LJView *v) {
41 int left = v->field->leftWall;
42 int right = v->field->rightWall;
43 cls();
44 if (DS_PFTOP > 0) {
45 for (int x = left - 1; x < right + 1; ++x) {
46 MAP[31][DS_PFTOP - 1][DS_PFLEFT + x] = 0x8005;
47 }
48 }
49 if (DS_PFTOP + LJ_PF_VIS_HT < SCREEN_H) {
50 for (int x = left - 1; x < right + 1; ++x) {
51 MAP[31][DS_PFTOP + LJ_PF_VIS_HT][DS_PFLEFT + x] = 0x8004;
52 }
53 }
54 for (int i = 0; i < LJ_PF_VIS_HT; ++i) {
55 MAP[31][i + DS_PFTOP][DS_PFLEFT + left - 1] = 0x8003;
56 MAP[31][i + DS_PFTOP][DS_PFLEFT + right] = 0x8003;
57 }
58 textout("Score", 1, 6 + DS_PFTOP, 0);
59 textout("Lines", 1, 8 + DS_PFTOP, 0);
60 textout("Speed", 1, 10 + DS_PFTOP, 0);
61 }
63 static void drawBlock(struct LJPCView *unused, int x, int y, int b) {
64 if (x >= 0 && x < LJ_PF_WID && y >= 0 && y < LJ_PF_VIS_HT) {
65 int c;
67 if (b == 0x100) {
68 c = '-';
69 } else if (b == 0x110) {
70 c = 0x8005; // ceiling tile
71 } else if (b >= 0xA0 && b < 0xC0) {
72 c = (b & 0xF0) << 8 | (b & 0x0F) | 0x100;
73 } else if (b >= 0x10) {
74 c = (b & 0xF0) << 8 | (b & 0x0F) | 0x10;
75 } else {
76 c = ' ';
77 }
78 MAP[31][DS_PFTOP + LJ_PF_VIS_HT - 1 - y][DS_PFLEFT + x] = c;
79 }
80 }
82 void updField(const LJView *const v, LJBits rows) {
83 const LJField *const p = v->field;
85 for (int y = 0;
86 y < LJ_PF_VIS_HT && rows != 0;
87 ++y, rows >>= 1) {
88 int blankTile = 0;
90 if (y == p->ceiling) {
91 blankTile = 0x110;
92 } else if (hotlineRows[y] && v->field->scoreStyle == LJSCORE_HOTLINE) {
93 blankTile = 0x100;
94 }
95 if (p->state == LJS_LINES_FALLING && p->stateTime > 0
96 && ((1 << y) & p->tempRows)) {
97 blankTile = 0x100;
98 }
99 if (rows & 1) {
100 for (int x = p->leftWall; x < p->rightWall; x++) {
101 int b = v->hidePF ? 0 : p->b[y][x];
102 drawBlock(v->plat, x, y, b ? b : blankTile);
103 }
104 }
105 }
106 }
108 void blitField(LJView *v) {
110 }
112 int getTime() {
113 return curTime;
114 }
116 #if !defined(DISP_VBLANK_IRQ)
117 #define DISP_VBLANK_IRQ LCDC_VBL
118 #endif
119 #if !defined(IRQ_HANDLER)
120 #define IRQ_HANDLER INT_VECTOR
121 #endif
123 void install_timer(void)
124 {
126 // Turn off interrupts before doing anything
127 REG_IME = 0;
129 // Overwrite the ISR
130 IRQ_HANDLER = isr;
132 // Hook up the interrupt destination
133 REG_IE = IRQ_VBLANK;
135 // Acknowledge all pending interrupts
136 REG_IF = ~0;
138 // Set up an interrupt source
139 REG_DISPSTAT = DISP_VBLANK_IRQ;
141 // Turn interrupts back on
142 REG_IME = 1;
143 }
145 void yieldCPU(void) {
146 // we're not multitasking so we don't need this
147 // on the GBA and DS, vsync() does all the waiting we need
148 }
150 static void upcvt_4bit(void *dst, const u8 *src, size_t len)
151 {
152 u32 *out = dst;
154 for(; len > 0; len--)
155 {
156 u32 dst_bits = 0;
157 u32 src_bits = *src++;
158 u32 x;
160 for(x = 0; x < 8; x++)
161 {
162 dst_bits <<= 4;
163 dst_bits |= src_bits & 1;
164 src_bits >>= 1;
165 }
166 *out++ = dst_bits;
167 }
168 }
170 extern const unsigned char text_chr[];
171 extern const unsigned int text_chr_size;
172 extern const unsigned char gbablk_chr[];
173 extern const unsigned int gbablk_chr_size;
175 static void loadOneConnection(void *in_dst, const void *in_src) {
176 u16 *dst = in_dst;
177 const u16 *src = in_src;
178 for (unsigned int conn = 0; conn < 16; ++conn) {
179 unsigned int topSegY = (conn & CONNECT_U) ? 32 : 0;
180 unsigned int botSegY = (conn & CONNECT_D) ? 8 : 40;
181 unsigned int leftSegX = (conn & CONNECT_L) ? 16 : 0;
182 unsigned int rightSegX = (conn & CONNECT_R) ? 1 : 17;
183 for (unsigned int i = 0; i < 8; i += 2) {
184 *dst++ = src[leftSegX + topSegY + i];
185 *dst++ = src[rightSegX + topSegY + i];
186 }
187 for (unsigned int i = 0; i < 8; i += 2) {
188 *dst++ = src[leftSegX + botSegY + i];
189 *dst++ = src[rightSegX + botSegY + i];
190 }
191 }
192 }
194 static void loadConnections(void) {
195 loadOneConnection(PATRAM4(0, 16), gbablk_chr + 8*32);
196 loadOneConnection(PATRAM4(0, 256), gbablk_chr + 12*32);
197 }
199 static void load_font(void) {
200 upcvt_4bit(PATRAM4(0, 0), text_chr, text_chr_size);
201 memcpy(PATRAM4(0, 0), gbablk_chr, 8*32);
202 memcpy(SPR_VRAM(0), gbablk_chr, 8*32);
203 loadConnections();
204 }
206 void textout(const char *str, int x, int y, int c) {
207 u16 *dst = &(MAP[31][y][x]);
208 int spacesLeft = SCREEN_W - x;
210 c <<= 12;
211 while (*str != 0 && spacesLeft > 0) {
212 *dst++ = c | *(unsigned char *)str++;
213 --spacesLeft;
214 }
215 }
217 static const u16 srsColors[12] = {
218 RGB5(2, 2, 2),
219 RGB5(0, 3, 3),
220 RGB5(0, 0, 3),
221 RGB5(3, 2, 0),
222 RGB5(3, 3, 0),
223 RGB5(0, 3, 0),
224 RGB5(2, 0, 3),
225 RGB5(3, 0, 0),
226 RGB5(2, 2, 2),
227 RGB5(3, 0, 0),
228 RGB5(2, 2, 2),
229 RGB5(3, 2, 1),
230 };
232 static const u16 arsColors[12] = {
233 RGB5(2, 2, 2),
234 RGB5(3, 1, 0),
235 RGB5(0, 0, 3),
236 RGB5(3, 2, 0),
237 RGB5(3, 3, 0),
238 RGB5(2, 0, 3),
239 RGB5(0, 3, 3),
240 RGB5(0, 3, 0),
241 RGB5(2, 2, 2),
242 RGB5(3, 0, 0),
243 RGB5(2, 2, 2),
244 RGB5(3, 2, 1),
245 };
247 void setupPalette(const u16 *colors) {
248 for (int i = 1; i < 12; ++i) {
249 int c = colors[i];
251 BG_PALETTE[i * 16 + 1] = RGB5(22,22,22) + 3 * c;
252 BG_PALETTE[i * 16 + 2] = RGB5(13,13,13) + 6 * c;
253 BG_PALETTE[i * 16 + 3] = RGB5( 4, 4, 4) + 9 * c;
254 BG_PALETTE[i * 16 + 4] = RGB5( 4, 4, 4) + 7 * c;
255 BG_PALETTE[i * 16 + 5] = RGB5( 4, 4, 4) + 5 * c;
256 BG_PALETTE[i * 16 + 6] = RGB5( 4, 4, 4) + 3 * c;
257 }
258 memcpy(SPRITE_PALETTE, BG_PALETTE, 12 * 32);
259 }
261 // libnds style wrapper around libgba header
262 #ifndef DISPLAY_BG0_ACTIVE
263 #define DISPLAY_BG0_ACTIVE BG0_ON
264 #define DISPLAY_SPR_ACTIVE OBJ_ON
265 #define MODE_0_2D MODE_0
266 #define DISPLAY_SPR_1D_LAYOUT OBJ_1D_MAP
267 static inline void videoSetMode(int x) {
268 REG_DISPCNT = x;
269 }
271 #endif
273 void waitForStart(void) {
274 LJBits lastJ = ~0;
275 LJBits jnew = 0;
277 do {
278 LJBits j = ~REG_KEYINPUT;
279 jnew = j & ~lastJ;
280 lastJ = j;
281 vsync();
282 } while(!(jnew & (KEY_A | KEY_START)));
283 }
286 static const char *const coprNoticeLines[] = {
287 "LOCKJAW: The Reference",
288 "Version "LJ_VERSION,
289 NULL,
290 "© 2008 Damian Yerrick",
291 "Not sponsored or endorsed by Nintendo",
292 "or Tetris Holding.",
293 "Comes with ABSOLUTELY NO WARRANTY.",
294 "This is free software, and you are welcome",
295 "to share it under the conditions described",
296 "in GPL.txt."
297 };
299 void coprNotice(void) {
300 videoSetMode(MODE_0_2D);
301 BGCTRL[0] = BG_TILE_BASE(0) | BG_MAP_BASE(31);
302 BG_OFFSET[0].x = 0;
303 BG_OFFSET[0].y = 0;
305 BG_PALETTE[0] = RGB5(31,31,31);
306 BG_PALETTE[1] = RGB5(20,20,20);
307 BG_PALETTE[2] = RGB5( 0, 0, 0);
308 vwfWinInit(&vwfTop);
309 for (int i = 0, y = 8;
310 i < sizeof(coprNoticeLines) / sizeof(coprNoticeLines[0]);
311 ++i) {
312 if (coprNoticeLines[i]) {
313 vwfPuts(&vwfTop, coprNoticeLines[i], 8, y);
314 y += 12;
315 } else {
316 y += 6;
317 }
318 }
319 vwfPuts(&vwfTop, "Press Start", 8, SCREEN_H * 8 - 16);
320 videoSetMode(MODE_0_2D | DISPLAY_BG0_ACTIVE);
321 waitForStart();
322 }
324 LJBits menuReadPad(void) {
325 LJBits keys = readPad(0);
326 if (keys & VKEY_START) {
327 keys |= VKEY_ROTR;
328 }
329 return keys;
330 }
332 void ljBeginDraw(LJView *v, int sync) {
333 vsync();
334 finishSprites();
335 }
337 void ljEndDraw(LJView *v) {
339 }
341 /* Replay stubs */
342 void replayRecord(struct LJReplay *r, LJBits keys, const LJInput *in) {
344 }
346 void replayClose(struct LJReplay *r) {
348 }
350 int getReplayFrame(struct LJReplay *r, LJInput *d) {
351 return 0;
352 }
354 #define READY_GO_LINE 13
356 void startingAnimation(LJView *v) {
357 vsync();
358 gba_poll_sound(v->plat);
359 setupPalette(rotSystems[v->field->rotationSystem]->colorScheme ? arsColors : srsColors);
360 videoSetMode(MODE_0_2D);
361 cls();
362 load_font();
363 BG_PALETTE[0] = RGB5(31,31,31);
364 BG_PALETTE[1] = RGB5( 0, 0, 0);
365 drawFrame(v);
366 finishSprites();
367 vsync();
368 gba_poll_sound(v->plat);
369 videoSetMode(MODE_0_2D
370 | DISPLAY_BG0_ACTIVE);
371 BGCTRL[0] = BG_TILE_BASE(0) | BG_MAP_BASE(31);
372 BG_OFFSET[0].x = 0;
373 BG_OFFSET[0].y = 0;
375 textout("Ready",
376 (LJ_PF_WID - 5) / 2 + DS_PFLEFT,
377 DS_PFTOP + LJ_PF_VIS_HT - 1 - READY_GO_LINE,
378 0);
379 for (int i = 0; i < 30; ++i) {
380 vsync();
381 gba_poll_sound(v->plat);
382 }
383 v->backDirty = ~0;
384 updField(v, ~0);
385 videoSetMode(MODE_0_2D
386 | DISPLAY_BG0_ACTIVE
387 | DISPLAY_SPR_1D_LAYOUT
388 | DISPLAY_SPR_ACTIVE);
389 drawScore(v);
390 finishSprites();
392 textout(" GO! ",
393 (LJ_PF_WID - 5) / 2 + DS_PFLEFT,
394 DS_PFTOP + LJ_PF_VIS_HT - 1 - READY_GO_LINE,
395 0);
396 for (int i = 0; i < 30; ++i) {
397 vsync();
398 gba_poll_sound(v->plat);
399 }
400 drawFrame(v);
401 wantPause = 0;
403 #ifdef ARM9
404 tb.cmd = TALKBACK_PLAY_MUSIC;
405 #endif
406 }
408 int pauseGame(struct LJPCView *v) {
409 LJBits lastKeys = ~0;
410 int unpaused = 0;
411 int canceled = 0;
413 // hide playfield
414 for (int y = DS_PFTOP; y < DS_PFTOP + LJ_PF_VIS_HT; ++y) {
415 for (int x = DS_PFLEFT;
416 x < DS_PFLEFT + LJ_PF_WID;
417 ++x) {
418 MAP[31][y][x] = ' ';
419 }
420 }
421 textout("Game", 2 + DS_PFLEFT, 6 + DS_PFTOP, 0);
422 textout("Paused", 2 + DS_PFLEFT, 7 + DS_PFTOP, 0);
423 textout("Start:", 2 + DS_PFLEFT, 10 + DS_PFTOP, 0);
424 textout("Resume", 2 + DS_PFLEFT, 11 + DS_PFTOP, 0);
425 textout("Select:", 2 + DS_PFLEFT, 13 + DS_PFTOP, 0);
426 textout("Exit", 2 + DS_PFLEFT, 14 + DS_PFTOP, 0);
428 #ifdef ARM9
429 tb.cmd = TALKBACK_PAUSE_MUSIC;
430 #endif
431 while (!unpaused || (lastKeys & (KEY_SELECT | KEY_START))) {
432 int keys = ~REG_KEYINPUT;
433 if (keys & ~lastKeys & KEY_START) {
434 unpaused = 1;
435 }
436 if (keys & ~lastKeys & KEY_SELECT) {
437 unpaused = 1;
438 canceled = 1;
439 }
440 finishSprites();
441 vsync();
442 gba_poll_sound(v);
443 lastKeys = keys;
444 }
445 #ifdef ARM9
446 tb.cmd = TALKBACK_PLAY_MUSIC;
447 #endif
448 return canceled;
449 }
451 int ljHandleConsoleButtons(LJView *v) {
452 LJBits keys = ~REG_KEYINPUT;
453 int canceled = 0;
455 wantPause |= !!(keys & KEY_START);
456 if (wantPause) {
457 canceled = pauseGame(v->plat);
458 wantPause = 0;
459 drawFrame(v);
460 v->backDirty = ~0;
461 }
462 return canceled;
463 }
465 LJBits drawPiece(LJView *const v, void *const b,
466 int piece, int x, int y, int theta,
467 int color, int w, int h);
469 void drawFallingPiece(LJView *v) {
470 LJBits bits = 0;
471 const LJField *const p = v->field;
472 int piece = p->curPiece[0];
473 int y = ljfixfloor(p->y);
474 const int w = 8;
475 const int h = 8;
476 int drawnY = v->smoothGravity ? ljfixfloor(h * p->y) : h * y;
477 const int color = (p->state == LJS_LANDED)
478 ? -128 - ((p->stateTime + 1) * 128 / (p->speed.lockDelay + 1))
479 : pieceColors[piece];
481 bits = drawPiece(v, NULL, piece,
482 w * (p->x + DS_PFLEFT),
483 h * (LJ_PF_VIS_HT + DS_PFTOP) - drawnY,
484 p->theta,
485 color, w, h);
486 bits = (y >= 0) ? bits << y : bits >> -y;
487 bits &= (1 << LJ_PF_VIS_HT) - 1;
489 v->backDirty |= bits | (bits << 1);
490 v->frontDirty |= bits | (bits << 1);
491 }
493 #define SHADOW_BLOCK 0x00
495 void drawShadow(LJView *v) {
496 LJBits bits = 0;
497 const LJField *const p = v->field;
498 int piece = p->curPiece[0];
499 int y = p->hardDropY;
500 const int w = 8;
501 const int h = 8;
503 bits = drawPiece(v, NULL, piece,
504 w * (p->x + DS_PFLEFT),
505 h * (LJ_PF_VIS_HT + DS_PFTOP - y),
506 p->theta,
507 SHADOW_BLOCK, w, h);
508 bits = (y >= 0) ? bits << y : bits >> -y;
509 bits &= (1 << LJ_PF_VIS_HT) - 1;
511 v->backDirty |= bits;
512 v->frontDirty |= bits;
513 }
515 void drawNextPieces(LJView *v) {
516 int holdPieceColor = v->field->alreadyHeld
517 ? 0x80
518 : pieceColors[v->field->holdPiece];
520 // Draw hold piece
521 drawPiece(v, NULL,
522 v->field->holdPiece,
523 (DS_PFLEFT - 5) * 8, (DS_PFTOP + 5) * 8, 4,
524 holdPieceColor, 8, 8);
526 // Draw next pieces
527 int y = 32 + 8 * DS_PFTOP;
528 int x = (DS_PFLEFT + LJ_PF_WID) * 8;
529 for(int i = 1; i <= v->nextPieces; ++i) {
530 int piece = v->field->curPiece[i];
532 if (!v->hideNext) {
533 drawPiece(v, NULL,
534 piece, x, y, 4,
535 pieceColors[piece], 8, 8);
536 }
537 y += 20;
538 }
539 v->frontDirty &= (1 << LJ_PF_VIS_HT) - 1;
540 }
542 void drawScore(LJView *v) {
543 char txt[16];
544 int tpm = -1;
545 int lvl = v->field->speedState.level;
547 siprintf(txt, "%8u", v->field->score);
548 textout(txt, 0, 7 + DS_PFTOP, 0);
549 siprintf(txt, "%8u", v->field->lines);
550 textout(txt, 0, 9 + DS_PFTOP, 0);
552 if (lvl > 0) {
553 textout("Level:", 1, SCREEN_H - 3, 0);
554 siprintf(txt, "%9u", lvl);
555 textout(txt, 0, SCREEN_H - 2, 0);
556 }
558 if (v->nLockTimes >= 2) {
559 int time = v->lockTime[0] - v->lockTime[v->nLockTimes - 1];
560 if (time > 0) {
561 tpm = 3600 * (v->nLockTimes - 1) / time;
562 }
563 }
564 if (tpm > 0) {
565 siprintf(txt, "%8d", tpm);
566 textout(txt, 0, 11 + DS_PFTOP, 0);
567 } else {
568 textout(" ---", 0, 11 + DS_PFTOP, 0);
569 }
571 {
572 int seconds = v->field->gameTime / 60;
573 int minutes = seconds / 60;
574 seconds -= minutes * 60;
575 siprintf(txt, "%6d:%02d", minutes, seconds);
576 textout(txt, 0, SCREEN_H - 1, 0);
577 }
578 drawNextPieces(v);
579 }