comparison src/ljgbads.inc @ 0:c84446dfb3f5

initial add
author paulo@localhost
date Fri, 13 Mar 2009 00:39:12 -0700
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:253bc4eeed01
1 /* DS/GBA frontend for LOCKJAW, an implementation of the Soviet Mind Game
2
3 Copyright (C) 2006-2007 Damian Yerrick <tepples+lj@spamcop.net>
4
5 This work is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 Original game concept and design by Alexey Pajitnov.
20 The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg,
21 or The Tetris Company LLC.
22
23 */
24
25 #include "fontdraw.h"
26
27 unsigned char wantPause;
28 volatile char redrawWholeScreen = 0;
29
30 void finishSprites();
31
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 }
39
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 }
62
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;
66
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 }
81
82 void updField(const LJView *const v, LJBits rows) {
83 const LJField *const p = v->field;
84
85 for (int y = 0;
86 y < LJ_PF_VIS_HT && rows != 0;
87 ++y, rows >>= 1) {
88 int blankTile = 0;
89
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 }
107
108 void blitField(LJView *v) {
109
110 }
111
112 int getTime() {
113 return curTime;
114 }
115
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
122
123 void install_timer(void)
124 {
125
126 // Turn off interrupts before doing anything
127 REG_IME = 0;
128
129 // Overwrite the ISR
130 IRQ_HANDLER = isr;
131
132 // Hook up the interrupt destination
133 REG_IE = IRQ_VBLANK;
134
135 // Acknowledge all pending interrupts
136 REG_IF = ~0;
137
138 // Set up an interrupt source
139 REG_DISPSTAT = DISP_VBLANK_IRQ;
140
141 // Turn interrupts back on
142 REG_IME = 1;
143 }
144
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 }
149
150 static void upcvt_4bit(void *dst, const u8 *src, size_t len)
151 {
152 u32 *out = dst;
153
154 for(; len > 0; len--)
155 {
156 u32 dst_bits = 0;
157 u32 src_bits = *src++;
158 u32 x;
159
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 }
169
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;
174
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 }
193
194 static void loadConnections(void) {
195 loadOneConnection(PATRAM4(0, 16), gbablk_chr + 8*32);
196 loadOneConnection(PATRAM4(0, 256), gbablk_chr + 12*32);
197 }
198
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 }
205
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;
209
210 c <<= 12;
211 while (*str != 0 && spacesLeft > 0) {
212 *dst++ = c | *(unsigned char *)str++;
213 --spacesLeft;
214 }
215 }
216
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 };
231
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 };
246
247 void setupPalette(const u16 *colors) {
248 for (int i = 1; i < 12; ++i) {
249 int c = colors[i];
250
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 }
260
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 }
270
271 #endif
272
273 void waitForStart(void) {
274 LJBits lastJ = ~0;
275 LJBits jnew = 0;
276
277 do {
278 LJBits j = ~REG_KEYINPUT;
279 jnew = j & ~lastJ;
280 lastJ = j;
281 vsync();
282 } while(!(jnew & (KEY_A | KEY_START)));
283 }
284
285
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 };
298
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;
304
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 }
323
324 LJBits menuReadPad(void) {
325 LJBits keys = readPad(0);
326 if (keys & VKEY_START) {
327 keys |= VKEY_ROTR;
328 }
329 return keys;
330 }
331
332 void ljBeginDraw(LJView *v, int sync) {
333 vsync();
334 finishSprites();
335 }
336
337 void ljEndDraw(LJView *v) {
338
339 }
340
341 /* Replay stubs */
342 void replayRecord(struct LJReplay *r, LJBits keys, const LJInput *in) {
343
344 }
345
346 void replayClose(struct LJReplay *r) {
347
348 }
349
350 int getReplayFrame(struct LJReplay *r, LJInput *d) {
351 return 0;
352 }
353
354 #define READY_GO_LINE 13
355
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;
374
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();
391
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;
402
403 #ifdef ARM9
404 tb.cmd = TALKBACK_PLAY_MUSIC;
405 #endif
406 }
407
408 int pauseGame(struct LJPCView *v) {
409 LJBits lastKeys = ~0;
410 int unpaused = 0;
411 int canceled = 0;
412
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);
427
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 }
450
451 int ljHandleConsoleButtons(LJView *v) {
452 LJBits keys = ~REG_KEYINPUT;
453 int canceled = 0;
454
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 }
464
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);
468
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];
480
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;
488
489 v->backDirty |= bits | (bits << 1);
490 v->frontDirty |= bits | (bits << 1);
491 }
492
493 #define SHADOW_BLOCK 0x00
494
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;
502
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;
510
511 v->backDirty |= bits;
512 v->frontDirty |= bits;
513 }
514
515 void drawNextPieces(LJView *v) {
516 int holdPieceColor = v->field->alreadyHeld
517 ? 0x80
518 : pieceColors[v->field->holdPiece];
519
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);
525
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];
531
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 }
541
542 void drawScore(LJView *v) {
543 char txt[16];
544 int tpm = -1;
545 int lvl = v->field->speedState.level;
546
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);
551
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 }
557
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 }
570
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 }