paulo@0: /* DS frontend for LOCKJAW, an implementation of the Soviet Mind Game paulo@0: paulo@0: Copyright (C) 2006-2007 Damian Yerrick paulo@0: paulo@0: This work is free software; you can redistribute it and/or modify paulo@0: it under the terms of the GNU General Public License as published by paulo@0: the Free Software Foundation; either version 2 of the License, or paulo@0: (at your option) any later version. paulo@0: paulo@0: This program is distributed in the hope that it will be useful, paulo@0: but WITHOUT ANY WARRANTY; without even the implied warranty of paulo@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the paulo@0: GNU General Public License for more details. paulo@0: paulo@0: You should have received a copy of the GNU General Public License paulo@0: along with this program; if not, write to the Free Software paulo@0: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA paulo@0: paulo@0: Original game concept and design by Alexey Pajitnov. paulo@0: The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, paulo@0: or The Tetris Company LLC. paulo@0: paulo@0: */ paulo@0: paulo@0: #include "ljplay.h" paulo@0: #include "ljds.h" paulo@0: #include "talkback.h" paulo@0: #include paulo@0: #include paulo@0: #include "options.h" paulo@0: #include "gbamenus.h" paulo@0: #include "ljpath.h" paulo@0: paulo@0: #if 1 paulo@0: #define LJ_VERSION "0.46 ("__DATE__")" paulo@0: #else paulo@0: #define LJ_VERSION "WIP ("__DATE__")" paulo@0: #endif paulo@0: paulo@0: #define SCREEN_W 32 paulo@0: #define SCREEN_H 24 paulo@0: #define DS_PFTOP 3 paulo@0: #define DS_PFLEFT 10 paulo@0: paulo@0: /* My ghetto IPC system */ paulo@0: volatile P8A7Talkback tb_cached = { paulo@0: .cmd = 0, paulo@0: .sounds = 0 paulo@0: }; paulo@0: paulo@0: #define tb (*(volatile P8A7Talkback *) \ paulo@0: ((volatile char *)&tb_cached + 0x00400000)) paulo@0: paulo@0: short mouse_x, mouse_y; paulo@0: LJBits mouse_b; paulo@0: paulo@0: #include "ljgbads.inc" paulo@0: paulo@0: unsigned int nSprites = 0; paulo@0: volatile int curTime; paulo@0: paulo@0: void gba_play_sound(struct LJPCView *v, int n) { paulo@0: paulo@0: } paulo@0: paulo@0: void gba_poll_sound(struct LJPCView *plat) { paulo@0: paulo@0: } paulo@0: paulo@0: LJBits readHWKeys(void) { paulo@0: scanKeys(); paulo@0: LJBits j = keysHeld(); paulo@0: touchPosition xy = touchReadXY(); paulo@0: paulo@0: if (j & KEY_TOUCH) { paulo@0: mouse_x = xy.px; paulo@0: mouse_y = xy.py; paulo@0: mouse_b = 1; paulo@0: j &= ~(KEY_TOUCH_RIGHT | KEY_TOUCH_LEFT paulo@0: | KEY_TOUCH_UP | KEY_TOUCH_DOWN); paulo@0: if (xy.px < 96) { paulo@0: j |= KEY_TOUCH_LEFT; paulo@0: } else if (xy.px >= 160) { paulo@0: j |= KEY_TOUCH_RIGHT; paulo@0: } paulo@0: if (xy.py < 64) { paulo@0: j |= KEY_TOUCH_UP; paulo@0: } else if (xy.py >= 128) { paulo@0: j |= KEY_TOUCH_DOWN; paulo@0: } paulo@0: } else { paulo@0: mouse_b = 0; paulo@0: } paulo@0: return j; paulo@0: } paulo@0: paulo@0: void finishSprites(void) { paulo@0: for (int i = nSprites - 1; i >= 0; --i) { paulo@0: MAINOAM[i].attribute[0] = 512; paulo@0: } paulo@0: nSprites = 128; paulo@0: } paulo@0: paulo@0: void vsync(void) { paulo@0: swiWaitForVBlank(); paulo@0: wantPause |= needLidSleep(); paulo@0: } paulo@0: paulo@0: void isr(void) paulo@0: { paulo@0: int interrupts = REG_IF; paulo@0: paulo@0: VBLANK_INTR_WAIT_FLAGS |= interrupts; paulo@0: REG_IF = interrupts; paulo@0: ++curTime; paulo@0: } paulo@0: paulo@0: #define KEY_X (IPC_X << 16) paulo@0: #define KEY_Y (IPC_Y << 16) paulo@0: #define KEY_PEN (IPC_PEN_DOWN << 16) paulo@0: #define VRAM_MAIN ((uint16 *)0x06000000) paulo@0: #define VRAM_SUB ((uint16 *)0x06200000) paulo@0: paulo@0: paulo@0: paulo@0: /** paulo@0: * Tells the ARM7 to play these sound effects. paulo@0: */ paulo@0: void playSoundEffects(LJView *v, LJBits sounds, int countdown) { paulo@0: tb.countdown = countdown; paulo@0: tb.sounds |= sounds; paulo@0: } paulo@0: paulo@0: #define SHADOW_BLOCK 0x00 paulo@0: paulo@0: /** paulo@0: * Draws a tetromino whose lower left corner of the bounding box is at (x, y) paulo@0: * @param b the bitmap to draw to paulo@0: * @param piece the piece to be drawn paulo@0: * @param x distance from to left side of 4x4 box paulo@0: * @param y distance from top of bitmap to bottom of 4x4 box paulo@0: * @param the rotation state (0: U; 1: R; 2: D; 3: L; 4: Initial position) paulo@0: * @param w width of each block paulo@0: * @param h height of each block paulo@0: * @param color Drawing style paulo@0: * color == 0: draw shadow paulo@0: * color == 0x10 through 0x70: draw in that color paulo@0: * color == 0x80: draw as garbage paulo@0: * color == -255 through -1: draw with 255 through 1 percent lighting paulo@0: */ paulo@0: LJBits drawPiece(LJView *const v, void *const b, paulo@0: int piece, int x, int y, int theta, paulo@0: int color, int w, int h) { paulo@0: // Don't try to draw the -1 that's the sentinel for no hold piece paulo@0: if (piece < 0) paulo@0: return 0; paulo@0: paulo@0: LJBits rows = 0; paulo@0: LJBlkSpec blocks[4]; paulo@0: paulo@0: expandPieceToBlocks(blocks, v->field, piece, 0, 0, theta); paulo@0: paulo@0: for (int blk = 0; blk < 4; ++blk) { paulo@0: int blkValue = blocks[blk].conn; paulo@0: if (blkValue) { paulo@0: int blkX = blocks[blk].x; paulo@0: int blkY = blocks[blk].y; paulo@0: const int dstX = x + w * blkX; paulo@0: const int dstY = y + h * (-1 - blkY); paulo@0: paulo@0: if (color == 0x80) { paulo@0: blkValue = 0x8001; // garbage hold paulo@0: } else if (color != 0) { paulo@0: blkValue = ((blkValue & 0xF0) << 8) | 1; paulo@0: } else if (color == 0) { paulo@0: if (v->hideShadow == LJSHADOW_COLORED) { paulo@0: blkValue = ((blkValue & 0xF0) << 8) | 2; paulo@0: } else { paulo@0: blkValue = 0x8002; paulo@0: } paulo@0: } paulo@0: paulo@0: if (dstY > -8 && dstY < 192) { paulo@0: --nSprites; paulo@0: MAINOAM[nSprites].attribute[0] = dstY & 0x00FF; paulo@0: MAINOAM[nSprites].attribute[1] = dstX & 0x01FF; paulo@0: MAINOAM[nSprites].attribute[2] = blkValue; paulo@0: } paulo@0: paulo@0: rows |= 1 << blkY; paulo@0: } paulo@0: } paulo@0: paulo@0: return rows; paulo@0: } paulo@0: paulo@0: void openWindow(void) { paulo@0: videoSetMode(MODE_0_2D paulo@0: | DISPLAY_BG0_ACTIVE paulo@0: | DISPLAY_SPR_1D_LAYOUT paulo@0: | DISPLAY_SPR_ACTIVE); paulo@0: videoSetModeSub(MODE_0_2D paulo@0: | DISPLAY_BG0_ACTIVE); paulo@0: BGCTRL[0] = BG_16_COLOR | BG_TILE_BASE(0) | BG_MAP_BASE(31); paulo@0: BGCTRL_SUB[0] = BG_16_COLOR | BG_TILE_BASE(0) | BG_MAP_BASE(31); paulo@0: paulo@0: vramSetMainBanks(VRAM_A_MAIN_BG, VRAM_B_MAIN_SPRITE_0x06400000, paulo@0: VRAM_C_SUB_BG, VRAM_D_SUB_SPRITE); paulo@0: /* load_font(); */ paulo@0: // Load palette paulo@0: BG_PALETTE[0] = RGB5(31,31,31); paulo@0: BG_PALETTE[1] = RGB5( 0, 0,15); paulo@0: BG_PALETTE_SUB[0] = RGB5(0, 0, 0); paulo@0: setupPalette(srsColors); paulo@0: paulo@0: // Set scrolling paulo@0: BG_OFFSET[0].x = 0; paulo@0: BG_OFFSET[0].y = 0; paulo@0: BG_OFFSET_SUB[0].x = 0; paulo@0: BG_OFFSET_SUB[0].y = 0; paulo@0: paulo@0: SUB_BG2_XDX = 0x100; paulo@0: SUB_BG2_XDY = 0; paulo@0: SUB_BG2_YDX = 0; paulo@0: SUB_BG2_YDY = 0x100; paulo@0: SUB_BG2_CY = 0; paulo@0: SUB_BG2_CX = 0; paulo@0: paulo@0: lcdMainOnTop(); paulo@0: } paulo@0: paulo@0: void install_sound(void) { paulo@0: IPC->soundData = (void *)&tb; paulo@0: } paulo@0: paulo@0: #ifdef TRAP_SPRINTF paulo@0: int sprintf (char *dst, const char *format, ...) { paulo@0: BG_PALETTE[0] = RGB5(31, 0, 0); paulo@0: strcpy(dst, "[NO FPU]"); paulo@0: return 8; paulo@0: } paulo@0: #endif paulo@0: paulo@0: int main(void) { paulo@0: LJField p = { paulo@0: .leftWall = 1, paulo@0: .rightWall = 11, paulo@0: .ceiling = 20 paulo@0: }; paulo@0: LJControl control = { paulo@0: .dasSpeed = 1, paulo@0: .dasDelay = 10, paulo@0: .initialDAS = 1, paulo@0: .allowDiagonals = 0, paulo@0: .softDropSpeed = 0, paulo@0: .softDropLock = 0, paulo@0: .hardDropLock = 1 paulo@0: }; paulo@0: struct LJPCView platView; paulo@0: LJView mainView = { paulo@0: .field = &p, paulo@0: .control = &control, paulo@0: .smoothGravity = 1, paulo@0: .nextPieces = 3, paulo@0: .plat = &platView, paulo@0: .backDirty = ~0 paulo@0: }; paulo@0: paulo@0: powerON(POWER_ALL_2D); paulo@0: initOptions(customPrefs); paulo@0: install_timer(); paulo@0: install_sound(); paulo@0: openWindow(); paulo@0: paulo@0: BG_PALETTE_SUB[0] = RGB5( 0, 0, 0); paulo@0: BG_PALETTE_SUB[1] = RGB5(10,20,10); paulo@0: BG_PALETTE_SUB[2] = RGB5(15,31,15); paulo@0: BG_PALETTE_SUB[3] = RGB5(15,31,15); paulo@0: vwfWinInit(&vwfTouch); paulo@0: { paulo@0: int x = vwfPuts(&vwfTouch, "finding memory card... ", 0, 0); paulo@0: if (ljpathInit("/data/lockjaw/lj.nds")) { paulo@0: vwfPuts(&vwfTouch, "success!", x, 0); paulo@0: } else { paulo@0: vwfPuts(&vwfTouch, "failed.", x, 0); paulo@0: vwfPuts(&vwfTouch, "To learn how to fix this, see", 0, 12); paulo@0: vwfPuts(&vwfTouch, "http://dldi.drunkencoders.com/", 0, 24); paulo@0: } paulo@0: } paulo@0: paulo@0: coprNotice(); paulo@0: paulo@0: load_font(); paulo@0: drawFrame(&mainView); paulo@0: paulo@0: while (1) { paulo@0: LJView *const players[1] = {&mainView}; paulo@0: paulo@0: for (int y = 0; y < LJ_PF_VIS_HT; ++y) { paulo@0: for (int x = 0; x < LJ_PF_WID; ++x) { paulo@0: p.b[y][x] = 0; paulo@0: } paulo@0: } paulo@0: updField(&mainView, ~0); paulo@0: paulo@0: // turn on sub display only long enough for options paulo@0: videoSetModeSub(MODE_0_2D paulo@0: | DISPLAY_BG0_ACTIVE); paulo@0: setupPalette(srsColors); paulo@0: options(&mainView, customPrefs); paulo@0: videoSetModeSub(MODE_0_2D); paulo@0: BGCTRL_SUB[0] = BG_16_COLOR | BG_TILE_BASE(0) | BG_MAP_BASE(31); paulo@0: BG_PALETTE_SUB[0] = RGB5(0, 0, 0); paulo@0: unpackCommonOptions(&mainView, customPrefs); paulo@0: paulo@0: p.seed = curTime ^ (curTime << 16); paulo@0: play(players, 1); paulo@0: paulo@0: tb.cmd = TALKBACK_STOP_MUSIC; paulo@0: BG_PALETTE[0] = (control.countdown > 0) paulo@0: ? RGB5(31, 15, 15) paulo@0: : RGB5(15, 31, 15); paulo@0: paulo@0: // play game over sound paulo@0: if (control.countdown > 0) { paulo@0: playSoundEffects(&mainView, 3 << 16, control.countdown); paulo@0: } paulo@0: for (int i = 0; i < 60; ++i) { paulo@0: vsync(); paulo@0: //gba_poll_sound(&platView); paulo@0: } paulo@0: debrief(&mainView); paulo@0: } paulo@0: }