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