paulo@0: /* PC frontend for LOCKJAW, an implementation of the Soviet Mind Game paulo@0: paulo@0: Copyright (C) 2006-2008 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 "ljpc.h" paulo@0: #include "ljreplay.h" paulo@0: #include paulo@0: #include paulo@0: #include "scenario.h" paulo@0: #include "ljpath.h" paulo@0: paulo@0: #if 1 paulo@0: #define LJ_VERSION "0.46a ("__DATE__")" paulo@0: #else paulo@0: #define LJ_VERSION "WIP ("__DATE__")" paulo@0: #endif paulo@0: paulo@0: #define USE_PICS_FOLDER 0 paulo@0: #define MAX_PLAYERS 2 paulo@0: #define LJ_TICK_RATE 3600 // frames per minute paulo@0: paulo@0: int bgColor, fgColor = 0, hiliteColor, pfBgColor = 0, pfFgColor; paulo@0: static volatile int curTime = 0; paulo@0: volatile char redrawWholeScreen = 1; paulo@0: static volatile int wantPause = 0; paulo@0: const DATAFILE *dat = NULL, *sound_dat = NULL; paulo@0: const FONT *aver32 = NULL, *aver16 = NULL; paulo@0: char withSound = 0; paulo@0: char autoPause; paulo@0: int refreshRate = 0; paulo@0: char demoFilename[512]; paulo@0: paulo@0: int withPics = -1; paulo@0: paulo@0: char skinName[PATH_MAX] = "default.skin"; paulo@0: char ljblocksSRSName[PATH_MAX]; paulo@0: char ljblocksSegaName[PATH_MAX]; paulo@0: char ljconnSRSName[PATH_MAX]; paulo@0: char ljconnSegaName[PATH_MAX]; paulo@0: char ljbgName[PATH_MAX]; paulo@0: char menubgName[PATH_MAX]; paulo@0: char bgmName[PATH_MAX]; paulo@0: char bgmRhythmName[PATH_MAX]; paulo@0: unsigned long int bgmLoopPoint = 0; paulo@0: int bgmReadyGo = 0; paulo@0: int bgmVolume = 128; paulo@0: int skinW = 800; paulo@0: int skinH = 600; paulo@0: paulo@0: paulo@0: paulo@0: paulo@0: static void incCurTime(void) { paulo@0: ++curTime; paulo@0: } END_OF_FUNCTION(incCurTime); paulo@0: paulo@0: static void amnesia(void) { paulo@0: redrawWholeScreen = 1; paulo@0: } END_OF_FUNCTION(amnesia); paulo@0: paulo@0: static void requestPause(void) { paulo@0: wantPause |= autoPause; paulo@0: } paulo@0: paulo@0: paulo@0: int getTime(void) { paulo@0: return curTime; paulo@0: } paulo@0: paulo@0: void yieldCPU(void) { paulo@0: rest(5); paulo@0: } paulo@0: paulo@0: void ljBeginDraw(LJView *v, int sync) { paulo@0: vsync(); paulo@0: if (redrawWholeScreen) { paulo@0: redrawWholeScreen = 0; paulo@0: playRedrawScreen(v); paulo@0: } paulo@0: paulo@0: BITMAP *sc = v->plat->skin->fullback; paulo@0: paulo@0: // Draw shape paulo@0: blit(v->plat->skin->bg, sc, 0, 0, 0, 0, 48, 16); paulo@0: if (v->control->replayDst) { paulo@0: paulo@0: // circle: record paulo@0: circlefill(sc, 8, 8, 7, fgColor); paulo@0: textout_ex(sc, aver16, "Rec", 18, 2, fgColor, -1); paulo@0: } else if (v->control->replaySrc) { paulo@0: paulo@0: // triangle: play paulo@0: for (int wid = 0; wid < 7; ++wid) { paulo@0: hline(sc, 2, 2 + wid, wid * 2 + 3, fgColor); paulo@0: hline(sc, 2, 14 - wid, wid * 2 + 3, fgColor); paulo@0: } paulo@0: textout_ex(sc, aver16, "Play", 18, 2, fgColor, -1); paulo@0: } else { paulo@0: paulo@0: // square: stop paulo@0: rectfill(sc, 2, 2, 14, 14, fgColor); paulo@0: } paulo@0: blit(sc, screen, 0, 0, 0, 0, 48, 16); paulo@0: } paulo@0: paulo@0: void ljEndDraw(LJView *v) { paulo@0: paulo@0: } paulo@0: paulo@0: static void startRecording(LJView *v) { paulo@0: withPics = -1; paulo@0: paulo@0: // close existing playback paulo@0: if (v->control->replaySrc) { paulo@0: replayClose(v->control->replaySrc); paulo@0: v->control->replaySrc = NULL; paulo@0: } paulo@0: paulo@0: // toggle recording paulo@0: if (v->control->replayDst) { paulo@0: replayClose(v->control->replayDst); paulo@0: v->control->replayDst = NULL; paulo@0: } else { paulo@0: char path[PATH_MAX]; paulo@0: paulo@0: ljpathFind_w(path, "demo.ljm"); paulo@0: v->control->replayDst = newReplay(path, v->field); paulo@0: #if USE_PICS_FOLDER paulo@0: withPics = 0; paulo@0: #endif paulo@0: } paulo@0: } paulo@0: paulo@0: static void startPlaying(LJView *v) { paulo@0: withPics = -1; paulo@0: paulo@0: // close existing recording paulo@0: if (v->control->replayDst) { paulo@0: replayClose(v->control->replayDst); paulo@0: v->control->replayDst = NULL; paulo@0: } paulo@0: paulo@0: // toggle playback paulo@0: if (v->control->replaySrc) { paulo@0: replayClose(v->control->replaySrc); paulo@0: v->control->replaySrc = NULL; paulo@0: } else { paulo@0: char path[PATH_MAX]; paulo@0: paulo@0: if (ljpathFind_r(path, "demo.ljm")) { paulo@0: v->control->replaySrc = openReplay(path, v->field); paulo@0: } paulo@0: v->nLockTimes = 0; paulo@0: #if USE_PICS_FOLDER paulo@0: withPics = 0; paulo@0: #endif paulo@0: } paulo@0: v->backDirty = ~0; paulo@0: v->field->reloaded = 1; paulo@0: } paulo@0: paulo@0: int ljHandleConsoleButtons(LJView *v) { paulo@0: int canceled = 0; paulo@0: paulo@0: while (keypressed()) { paulo@0: int scancode; paulo@0: int codepoint = ureadkey(&scancode); paulo@0: paulo@0: if (scancode == KEY_ESC) { paulo@0: wantPause = 1; paulo@0: } else if (scancode == KEY_PRTSCR) { paulo@0: saveScreen(-1); paulo@0: save_bitmap("ljbackbuf.bmp", v->plat->skin->fullback, NULL); paulo@0: } else if (codepoint == '[') { paulo@0: v->plat->wantRecord = 1; paulo@0: } else if (codepoint == ']') { paulo@0: v->plat->wantRecord = 0; paulo@0: startPlaying(v); paulo@0: } paulo@0: } paulo@0: if (v->plat->wantRecord) { paulo@0: v->plat->wantRecord = 0; paulo@0: startRecording(v); paulo@0: } paulo@0: if (wantPause) { paulo@0: canceled = pauseGame(v->plat); paulo@0: wantPause = 0; paulo@0: } paulo@0: paulo@0: if (wantsClose) { paulo@0: canceled = 1; paulo@0: } paulo@0: paulo@0: return canceled; paulo@0: } paulo@0: paulo@0: paulo@0: static void drawBlock(const LJPCView *const v, int x, int y, int blk) { paulo@0: paulo@0: if (x >= 0 && x < LJ_PF_WID && y >= 0 && y < LJ_PF_VIS_HT) { paulo@0: const int w = v->skin->blkW; paulo@0: const int h = v->skin->blkH; paulo@0: const int dstX = w * x; paulo@0: const int dstY = h * (LJ_PF_VIS_HT - 1 - y); paulo@0: paulo@0: if (v->skin->transparentPF && (blk == 0 || blk == 0x100)) { paulo@0: paulo@0: // Copy background paulo@0: const unsigned int srcX = dstX + v->baseX; paulo@0: const unsigned int srcY = dstY + v->skin->baseY paulo@0: - LJ_PF_VIS_HT * h - v->skin->pfElev; paulo@0: blit(v->skin->bg, v->back, srcX, srcY, dstX, dstY, w, h); paulo@0: } else if (blk && v->skin->connBlocks) { paulo@0: paulo@0: // Copy connected block paulo@0: const unsigned int srcX = ((blk >> 0) & 15) * w; paulo@0: const unsigned int srcY = ((blk >> 4) & 15) * h; paulo@0: blit(v->skin->connBlocks, v->back, srcX, srcY, dstX, dstY, w, h); paulo@0: } else { paulo@0: paulo@0: // Copy lone block paulo@0: const unsigned int srcX = ((blk >> 4) & 7) * w; paulo@0: const unsigned int srcY = ((blk >> 7) & 1) * h; paulo@0: blit(v->skin->blocks, v->back, srcX, srcY, dstX, dstY, w, h); paulo@0: } paulo@0: paulo@0: // 0x100: hotline paulo@0: if (blk & 0x100) { paulo@0: hline(v->back, dstX, dstY + h / 2 - 1, dstX + w - 1, pfFgColor); paulo@0: hline(v->back, dstX, dstY + h / 2 , dstX + w - 1, pfFgColor); paulo@0: hline(v->back, dstX, dstY + h / 2 + 1, dstX + w - 1, pfBgColor); paulo@0: } paulo@0: } paulo@0: } paulo@0: paulo@0: /** paulo@0: * Draws multiple rows of the playfield paulo@0: * @param p the playfield paulo@0: * @param rows the rows to be updated (0x00001 on bottom, 0x80000 on top) paulo@0: */ paulo@0: void updField(const LJView *const v, LJBits rows) { paulo@0: const LJField *const p = v->field; paulo@0: paulo@0: acquire_bitmap(v->plat->back); paulo@0: for (int y = 0; paulo@0: y < LJ_PF_VIS_HT && rows != 0; paulo@0: ++y, rows >>= 1) { paulo@0: int blankTile = 0; paulo@0: paulo@0: // hotline: draw 0x100 as the background paulo@0: if (hotlineRows[y] && v->field->scoreStyle == LJSCORE_HOTLINE) { paulo@0: blankTile = 0x100; paulo@0: } paulo@0: // during line clear delay, draw bars to show which lines were cleared paulo@0: if (p->state == LJS_LINES_FALLING && p->stateTime > 0 paulo@0: && ((1 << y) & p->tempRows)) { paulo@0: blankTile = 0x100; paulo@0: } paulo@0: if (rows & 1) { paulo@0: for (int x = p->leftWall; x < p->rightWall; x++) { paulo@0: int b = v->hidePF ? 0 : p->b[y][x]; paulo@0: drawBlock(v->plat, x, y, b ? b : blankTile); paulo@0: } paulo@0: } paulo@0: } paulo@0: release_bitmap(v->plat->back); paulo@0: } 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, BITMAP *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: const int srcW = v->plat->skin->blkW; paulo@0: const int srcH = v->plat->skin->blkH; paulo@0: BITMAP *singleBlocks = v->plat->skin->blocks; paulo@0: int singleSeries = (color == 0 paulo@0: && singleBlocks->h >= 6 * srcH) paulo@0: ? 4 : 2; paulo@0: paulo@0: // color: 0 for shadow, >0 for piece paulo@0: BITMAP *connBlocks = (color != SHADOW_BLOCK) paulo@0: ? v->plat->skin->connBlocks : NULL; paulo@0: paulo@0: BITMAP *transTemp = color < 0 paulo@0: || (color == SHADOW_BLOCK && v->hideShadow < LJSHADOW_COLORED) paulo@0: ? create_bitmap(w, h) : 0; paulo@0: paulo@0: if (transTemp) { paulo@0: set_trans_blender(0, 0, 0, v->hideShadow ? 128 : 64); paulo@0: } paulo@0: paulo@0: expandPieceToBlocks(blocks, v->field, piece, 0, 0, theta); paulo@0: acquire_bitmap(b); paulo@0: for (int blk = 0; blk < 4; ++blk) { paulo@0: int blkValue = blocks[blk].conn; paulo@0: paulo@0: // If blkValue == 0 then the block is not part of the piece, paulo@0: // such as if it's a domino or tromino or (once pentominoes paulo@0: // are added) a tetromino. 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 > 0) { paulo@0: blkValue = color | (blkValue & CONNECT_MASK); paulo@0: } else if (color == 0 && v->hideShadow == LJSHADOW_COLORLESS) { paulo@0: blkValue = 0; paulo@0: } paulo@0: paulo@0: if (connBlocks) { paulo@0: const unsigned int srcX = ((blkValue >> 0) & 15) * srcW; paulo@0: const unsigned int srcY = ((blkValue >> 4) & 15) * srcH; paulo@0: paulo@0: if (transTemp) { paulo@0: stretch_blit(connBlocks, transTemp, paulo@0: srcX, srcY, srcW, srcH, paulo@0: 0, 0, w, h); paulo@0: if (color < 0) { paulo@0: draw_lit_sprite(b, transTemp, dstX, dstY, color + 256); paulo@0: } else { paulo@0: draw_trans_sprite(b, transTemp, dstX, dstY); paulo@0: } paulo@0: } else if (srcW != w || srcH != h) { paulo@0: stretch_blit(connBlocks, b, paulo@0: srcX, srcY, srcW, srcH, paulo@0: dstX, dstY, w, h); paulo@0: if (dstY > 600) { paulo@0: allegro_message("dstY = %d\n", dstY); paulo@0: } paulo@0: } else { paulo@0: blit(connBlocks, b, paulo@0: srcX, srcY, paulo@0: dstX, dstY, w, h); paulo@0: } paulo@0: } else { paulo@0: const unsigned int srcX = ((blkValue >> 4) & 7) * srcW; paulo@0: const unsigned int srcY = (((blkValue >> 7) & 1) + singleSeries) * srcH; paulo@0: paulo@0: if (transTemp) { paulo@0: stretch_blit(singleBlocks, transTemp, paulo@0: srcX, srcY, srcW, srcH, paulo@0: 0, 0, w, h); paulo@0: if (color < 0) { paulo@0: draw_lit_sprite(b, transTemp, dstX, dstY, color + 256); paulo@0: } else { paulo@0: draw_trans_sprite(b, transTemp, dstX, dstY); paulo@0: } paulo@0: } else { paulo@0: stretch_blit(singleBlocks, b, paulo@0: srcX, srcY, srcW, srcH, paulo@0: dstX, dstY, w, h); paulo@0: } paulo@0: } paulo@0: rows |= 1 << blkY; paulo@0: } paulo@0: } paulo@0: release_bitmap(b); paulo@0: if (transTemp) { paulo@0: solid_mode(); paulo@0: destroy_bitmap(transTemp); paulo@0: } paulo@0: paulo@0: return rows; paulo@0: } paulo@0: paulo@0: void drawPieceInPF(LJView *v, int dstX, LJFixed dstY, int theta, int color) { paulo@0: LJBits bits = 0; paulo@0: BITMAP *b = v->plat->back; paulo@0: const LJField *const p = v->field; paulo@0: int y = ljfixfloor(dstY); paulo@0: const int w = v->plat->skin->blkW; paulo@0: const int h = v->plat->skin->blkH; paulo@0: int drawnY = fixmul(h, dstY); paulo@0: paulo@0: bits = drawPiece(v, b, p->curPiece[0], paulo@0: w * dstX, h * LJ_PF_VIS_HT - drawnY, paulo@0: theta, paulo@0: color, w, h); paulo@0: bits = (y >= 0) ? bits << y : bits >> -y; paulo@0: bits &= (1 << LJ_PF_VIS_HT) - 1; paulo@0: if (dstY & 0xFFFF) { paulo@0: bits |= bits << 1; paulo@0: } paulo@0: paulo@0: v->backDirty |= bits; paulo@0: v->frontDirty |= bits; paulo@0: } paulo@0: paulo@0: paulo@0: void drawFallingPiece(LJView *v) { paulo@0: const LJField *const p = v->field; paulo@0: int piece = p->curPiece[0]; paulo@0: int y = v->smoothGravity paulo@0: ? p->y paulo@0: : ljitofix(ljfixfloor(p->y)); paulo@0: const int color = (p->state == LJS_LANDED) paulo@0: ? -128 - ((p->stateTime + 1) * 128 / (p->speed.lockDelay + 1)) paulo@0: : pieceColors[piece]; paulo@0: paulo@0: // Draw trails paulo@0: if (v->showTrails) { paulo@0: paulo@0: // trail going up paulo@0: while (v->trailY - y < ljitofix(-1)) { paulo@0: v->trailY += ljitofix(1); paulo@0: drawPieceInPF(v, p->x, v->trailY, p->theta, color); paulo@0: } paulo@0: paulo@0: // trail going down paulo@0: while (v->trailY - y > ljitofix(1)) { paulo@0: v->trailY -= ljitofix(1); paulo@0: drawPieceInPF(v, p->x, v->trailY, p->theta, color); paulo@0: } paulo@0: } paulo@0: drawPieceInPF(v, p->x, y, p->theta, color); paulo@0: v->trailY = y; paulo@0: } paulo@0: paulo@0: void drawShadow(LJView *v) { paulo@0: const LJField *const p = v->field; paulo@0: int y = ljitofix(p->hardDropY); paulo@0: const int color = SHADOW_BLOCK; paulo@0: drawPieceInPF(v, p->x, y, p->theta, color); paulo@0: } paulo@0: paulo@0: void drawNextPieces(LJView *v) { paulo@0: int baseX = v->plat->baseX; paulo@0: int baseY = v->plat->skin->baseY - v->plat->skin->pfElev; paulo@0: int blkW = v->plat->skin->blkW; paulo@0: int blkH = v->plat->skin->blkH; paulo@0: int holdPieceColor = v->field->alreadyHeld paulo@0: ? 0x80 paulo@0: : pieceColors[v->field->holdPiece]; paulo@0: int ceil = v->field->ceiling; paulo@0: paulo@0: BITMAP *sc = v->plat->skin->fullback; paulo@0: paulo@0: if (v->frontDirty & LJ_DIRTY_NEXT) { paulo@0: int holdW = blkW * 2 / 3; paulo@0: int holdH = blkH * 2 / 3; paulo@0: int holdX = baseX + blkW; paulo@0: int holdY = baseY - (ceil - 1) * blkH - 4 * holdH; paulo@0: paulo@0: // Move the hold piece within the screen paulo@0: if (holdY < 0) { paulo@0: holdY = 0; paulo@0: } paulo@0: paulo@0: // Draw hold piece paulo@0: blit(v->plat->skin->bg, sc, paulo@0: holdX, holdY, paulo@0: holdX, holdY, paulo@0: holdW * 4, holdH * 2); paulo@0: drawPiece(v, sc, paulo@0: v->field->holdPiece, holdX, holdY + 4 * holdH, 4, paulo@0: holdPieceColor, holdW, holdH); paulo@0: blit(sc, screen, paulo@0: holdX, holdY, paulo@0: holdX, holdY, paulo@0: holdW * 4, holdH * 2); paulo@0: } paulo@0: // Draw next pieces paulo@0: paulo@0: switch (v->plat->skin->nextPos) { paulo@0: case LJNEXT_RIGHT: paulo@0: case LJNEXT_RIGHT_TAPER: paulo@0: if (v->frontDirty & LJ_DIRTY_NEXT) { paulo@0: int y = baseY - ceil * blkH; paulo@0: int x = baseX + LJ_PF_WID * blkW; paulo@0: int w = blkW; paulo@0: int h = blkH; paulo@0: for (int i = 1; i <= v->nextPieces; ++i) { paulo@0: int piece = v->field->curPiece[i]; paulo@0: paulo@0: blit(v->plat->skin->bg, sc, x, y, x, y, w * 4, h * 2); paulo@0: if (!v->hideNext) { paulo@0: drawPiece(v, sc, paulo@0: piece, x, y + 4 * h, 4, paulo@0: pieceColors[piece], w, h); paulo@0: } paulo@0: blit(sc, screen, x, y, x, y, w * 4, h * 2); paulo@0: y += 8 + h * 2; paulo@0: if (v->plat->skin->nextPos == LJNEXT_RIGHT_TAPER) { paulo@0: --h; paulo@0: --w; paulo@0: } paulo@0: } paulo@0: } paulo@0: break; paulo@0: paulo@0: case LJNEXT_TOP: paulo@0: if (v->frontDirty & LJ_DIRTY_NEXT) { paulo@0: int y = baseY - (ceil + 2) * blkH - 8; paulo@0: int x = baseX + 4 * blkW; paulo@0: int blitX = x, blitY = y; paulo@0: paulo@0: blit(v->plat->skin->bg, sc, x, y, x, y, blkW * 8, blkH * 2); paulo@0: if (!v->hideNext) { paulo@0: if (v->nextPieces >= 1) { paulo@0: int piece = v->field->curPiece[1]; paulo@0: drawPiece(v, sc, paulo@0: piece, x, y + 4 * blkH, 4, paulo@0: pieceColors[piece], blkW, blkH); paulo@0: } paulo@0: if (v->nextPieces >= 2) { paulo@0: int piece = v->field->curPiece[2]; paulo@0: x += 4 * blkW; paulo@0: drawPiece(v, sc, paulo@0: piece, x, y + 3 * blkH, 4, paulo@0: pieceColors[piece], blkW/ 2, blkH / 2); paulo@0: } paulo@0: if (v->nextPieces >= 3) { paulo@0: int piece = v->field->curPiece[3]; paulo@0: x += 2 * blkW; paulo@0: drawPiece(v, sc, paulo@0: piece, x, y + 3 * blkH, 4, paulo@0: pieceColors[piece], blkW / 2, blkH / 2); paulo@0: } paulo@0: } paulo@0: blit(sc, screen, blitX, blitY, blitX, blitY, blkW * 8, blkH * 2); paulo@0: } paulo@0: break; paulo@0: } paulo@0: paulo@0: if (v->plat->nextAbove && !v->hideNext) { paulo@0: int row = (v->field->hardDropY + 4); paulo@0: int x = (1 + v->field->x) * blkW; paulo@0: for (int i = 1; paulo@0: i <= v->plat->nextAbove paulo@0: && row <= v->field->ceiling - 2; paulo@0: ++i) { paulo@0: int y = (LJ_PF_VIS_HT - row) * blkH; paulo@0: int piece = v->field->curPiece[i]; paulo@0: paulo@0: drawPiece(v, v->plat->back, paulo@0: piece, x, y, 4, paulo@0: pieceColors[piece], blkW / 2, blkH / 2); paulo@0: v->backDirty |= 3 << row; paulo@0: row += 2; paulo@0: } paulo@0: } paulo@0: v->frontDirty &= ~LJ_DIRTY_NEXT; paulo@0: } paulo@0: paulo@0: void drawScore(LJView *v) { paulo@0: int gameTime = v->field->gameTime; paulo@0: int seconds = gameTime / 60; paulo@0: int minutes = seconds / 60; paulo@0: int baseX = v->plat->baseX; paulo@0: int tpm = -1; paulo@0: int spawnLeft = v->plat->skin->blkW * LJ_SPAWN_X + baseX; paulo@0: int pfRight = v->plat->skin->blkW * LJ_PF_WID + baseX; paulo@0: BITMAP *sc = v->plat->skin->fullback; paulo@0: paulo@0: if (withPics >= 0) { paulo@0: if (v->field->nPieces != withPics) { paulo@0: saveScreen(v->field->nPieces); paulo@0: } paulo@0: withPics = v->field->nPieces; paulo@0: } paulo@0: paulo@0: if (v->nLockTimes >= 2 ) { paulo@0: int time = v->lockTime[0] - v->lockTime[v->nLockTimes - 1]; paulo@0: if (time > 0) { paulo@0: tpm = 3600 * (v->nLockTimes - 1) / time; paulo@0: } paulo@0: } paulo@0: paulo@0: if (v->frontDirty & LJ_DIRTY_SCORE) { paulo@0: switch (v->plat->skin->nextPos) { paulo@0: case LJNEXT_TOP: paulo@0: blit(v->plat->skin->bg, sc, paulo@0: pfRight, 72, paulo@0: pfRight, 72, paulo@0: 112, 136); paulo@0: paulo@0: textout_ex(sc, aver32, "Lines:", pfRight, 72, fgColor, -1); paulo@0: textprintf_right_ex(sc, aver32, pfRight + 104, 102, fgColor, -1, paulo@0: "%d", v->field->lines); paulo@0: textout_ex(sc, aver32, "Score:", pfRight, 142, fgColor, -1); paulo@0: textprintf_right_ex(sc, aver32, pfRight + 104, 172, fgColor, -1, paulo@0: "%d", v->field->score); paulo@0: textout_ex(sc, aver32, "Level:", pfRight, 212, fgColor, -1); paulo@0: textprintf_right_ex(sc, aver32, pfRight + 104, 242, fgColor, -1, paulo@0: "%d", v->field->speedState.level); paulo@0: blit(sc, screen, paulo@0: pfRight, 72, paulo@0: pfRight, 72, paulo@0: 112, 136); paulo@0: break; paulo@0: paulo@0: default: paulo@0: blit(v->plat->skin->bg, sc, spawnLeft, 12, spawnLeft, 12, 288, 30); paulo@0: blit(v->plat->skin->bg, sc, paulo@0: spawnLeft, 42, spawnLeft, 42, paulo@0: pfRight - spawnLeft, 30); paulo@0: textprintf_right_ex(sc, aver32, spawnLeft + 288, 12, fgColor, -1, paulo@0: "Lv. %d", v->field->speedState.level); paulo@0: textprintf_ex(sc, aver32, spawnLeft, 12, fgColor, -1, paulo@0: "Lines: %d", v->field->lines); paulo@0: textprintf_ex(sc, aver32, spawnLeft, 42, fgColor, -1, paulo@0: "Score: %d", v->field->score); paulo@0: blit(sc, screen, spawnLeft, 12, spawnLeft, 12, 288, 60); paulo@0: break; paulo@0: } paulo@0: } paulo@0: paulo@0: /* If speed is defined, and there is room to draw it, draw it. */ paulo@0: if (tpm > 0 && v->nextPieces <= 3) { paulo@0: blit(v->plat->skin->bg, sc, paulo@0: pfRight, 282, paulo@0: pfRight, 282, paulo@0: 104, 60); paulo@0: textout_ex(sc, aver32, "Speed:", pfRight, 282, fgColor, -1); paulo@0: textprintf_right_ex(sc, aver32, pfRight + 104, 312, fgColor, -1, paulo@0: "%d", tpm); paulo@0: blit(sc, screen, paulo@0: pfRight, 282, paulo@0: pfRight, 282, paulo@0: 104, 60); paulo@0: } paulo@0: paulo@0: if (v->frontDirty & LJ_DIRTY_NEXT) { paulo@0: paulo@0: // Erase gimmick paulo@0: blit(v->plat->skin->bg, sc, paulo@0: baseX, v->plat->skin->baseY + 8, paulo@0: baseX, v->plat->skin->baseY + 8, paulo@0: v->plat->skin->blkW * LJ_PF_WID, 30); paulo@0: // Draw gimmick paulo@0: if (v->field->gimmick >= 0 && v->field->gimmick < LJGM_N_GIMMICKS) { paulo@0: const char *gimmickName = ljGetFourCCName(gimmickNames[v->field->gimmick]); paulo@0: textout_centre_ex(sc, aver32, paulo@0: gimmickName ? gimmickName : "Bad gimmick ID", paulo@0: baseX + (LJ_PF_WID / 2) * v->plat->skin->blkW, paulo@0: v->plat->skin->baseY + 8, paulo@0: fgColor, -1); paulo@0: blit(sc, screen, paulo@0: baseX, v->plat->skin->baseY + 8, paulo@0: baseX, v->plat->skin->baseY + 8, paulo@0: v->plat->skin->blkW * LJ_PF_WID, 30); paulo@0: } paulo@0: } paulo@0: drawNextPieces(v); paulo@0: paulo@0: blit(v->plat->skin->bg, sc, pfRight, 42, pfRight, 42, 96, 30); paulo@0: #if 0 paulo@0: // Use this for DEBUG inspection into a variable paulo@0: textprintf_right_ex(sc, aver16, pfRight + 96, 8, fgColor, -1, paulo@0: "%d", v->field->bpmCounter); paulo@0: #endif paulo@0: textprintf_right_ex(sc, aver32, pfRight + 96, 42, fgColor, -1, paulo@0: "%d:%02d", minutes, seconds - 60 * minutes); paulo@0: blit(sc, screen, pfRight, 42, pfRight, 42, 96, 30); paulo@0: paulo@0: } paulo@0: paulo@0: void blitField(LJView *v) { paulo@0: int blkH = v->plat->skin->blkH; paulo@0: int rowY = v->plat->skin->baseY paulo@0: - v->plat->skin->pfElev paulo@0: - blkH * v->field->ceiling; paulo@0: int x = v->plat->skin->blkW * v->field->leftWall; paulo@0: int w = v->plat->skin->blkW * (v->field->rightWall - v->field->leftWall); paulo@0: paulo@0: // Copy each dirty row paulo@0: for (int y = v->field->ceiling - 1; y >= 0; --y) { paulo@0: if (v->frontDirty & (1 << y)) { paulo@0: int top = (LJ_PF_VIS_HT - y - 1) * blkH; paulo@0: int ht = 0; paulo@0: paulo@0: // Find the height of the contiguous rows to blit paulo@0: do { paulo@0: ht += blkH; paulo@0: --y; paulo@0: } while ((y >= 0) paulo@0: && (v->frontDirty & (1 << y))); paulo@0: blit(v->plat->back, screen, paulo@0: x, top, paulo@0: x + v->plat->baseX, rowY, paulo@0: w, ht); paulo@0: rowY += ht; paulo@0: } paulo@0: rowY += blkH; paulo@0: } paulo@0: paulo@0: v->frontDirty &= (~0) << LJ_PF_HT; paulo@0: } paulo@0: paulo@0: void saveScreen(int n) { paulo@0: BITMAP *b = create_bitmap(SCREEN_W, SCREEN_H); paulo@0: if (b) { paulo@0: PALETTE pal; paulo@0: paulo@0: get_palette(pal); paulo@0: blit(screen, b, 0, 0, 0, 0, SCREEN_W, SCREEN_H); paulo@0: if (n < 0) { paulo@0: save_bitmap("ljsnap.bmp", b, pal); paulo@0: } else { paulo@0: char filename[64]; paulo@0: sprintf(filename, "pics/lj%05d.bmp", n); paulo@0: save_bitmap(filename, b, pal); paulo@0: } paulo@0: destroy_bitmap(b); paulo@0: } paulo@0: } paulo@0: paulo@0: #define PRESETS_PER_COL 6 paulo@0: #define N_NONPRESETS 2 paulo@0: #define PRESETS_TOP 140 paulo@0: #define PRESET_COL_WIDTH 250 paulo@0: #define PRESET_ROW_HT 40 paulo@0: paulo@0: static const char *const nonPresetNames[N_NONPRESETS] = { paulo@0: "Full Custom", "Back" paulo@0: }; paulo@0: paulo@0: static void getPresetDrawRow(unsigned int preset, int hilite) { paulo@0: unsigned int ht = text_height(aver32); paulo@0: const char *txt = preset < nLoadedPresets paulo@0: ? loadedPresets[preset].name paulo@0: : nonPresetNames[preset - nLoadedPresets]; paulo@0: if (!txt) { paulo@0: txt = "Bad"; paulo@0: } paulo@0: unsigned int wid = text_length(aver32, txt); paulo@0: paulo@0: int buttonCol = preset / PRESETS_PER_COL; paulo@0: int buttonX = 20 + buttonCol * PRESET_COL_WIDTH; paulo@0: int buttonY = PRESETS_TOP paulo@0: + PRESET_ROW_HT * (preset - buttonCol * PRESETS_PER_COL); paulo@0: unsigned int buttonWidth = wid + 16; paulo@0: paulo@0: rectfill(screen, paulo@0: buttonX, buttonY, paulo@0: buttonX + buttonWidth - 1, buttonY + PRESET_ROW_HT - 1, paulo@0: hilite ? hiliteColor : bgColor); paulo@0: if (hilite) { paulo@0: rect(screen, paulo@0: buttonX, buttonY, paulo@0: buttonX + buttonWidth - 1, buttonY + PRESET_ROW_HT - 1, paulo@0: fgColor); paulo@0: } paulo@0: textout_ex(screen, aver32, txt, paulo@0: 8 + buttonX, paulo@0: 20 + buttonY - ht / 2, paulo@0: fgColor, -1); paulo@0: } paulo@0: paulo@0: int getPreset(int lastPreset) { paulo@0: LJBits lastKeys = ~0; paulo@0: redrawWholeScreen = 1; paulo@0: paulo@0: clear_keybuf(); paulo@0: if (lastPreset < 0) { paulo@0: lastPreset += nLoadedPresets + N_NONPRESETS; paulo@0: } paulo@0: paulo@0: for(int done = 0; done == 0; ) { paulo@0: if (redrawWholeScreen) { paulo@0: redrawWholeScreen = 0; paulo@0: clear_to_color(screen, bgColor); paulo@0: textout_ex(screen, aver32, "LOCKJAW > Play", 16, 32, fgColor, -1); paulo@0: textout_ex(screen, aver32, "Select a scenario:", 16, 90, fgColor, -1); paulo@0: paulo@0: for (int preset = 0; paulo@0: preset < nLoadedPresets + N_NONPRESETS; paulo@0: ++preset) { paulo@0: getPresetDrawRow(preset, preset == lastPreset); paulo@0: } paulo@0: textout_ex(screen, aver16, "Arrows: move; Rotate Right: start", paulo@0: 16, PRESETS_TOP + 40 * (PRESETS_PER_COL + 1), fgColor, -1); paulo@0: textout_ex(screen, aver16, "Coming soon: an editor for these!", paulo@0: 16, PRESETS_TOP + 40 * (PRESETS_PER_COL + 1) + 20, fgColor, -1); paulo@0: } paulo@0: while (keypressed()) { paulo@0: int scancode; paulo@0: ureadkey(&scancode); paulo@0: if (scancode == KEY_PRTSCR) { paulo@0: saveScreen(-1); paulo@0: } paulo@0: } paulo@0: paulo@0: int preset = lastPreset; paulo@0: LJBits keys = menuReadPad(); paulo@0: LJBits newKeys = keys & ~lastKeys; paulo@0: paulo@0: if ((newKeys & VKEY_ROTL) || wantsClose) { paulo@0: preset = nLoadedPresets + N_NONPRESETS - 1; paulo@0: ezPlaySample("rotate_wav", 128); paulo@0: } paulo@0: if ((newKeys & VKEY_ROTR) || wantsClose) { paulo@0: done = 1; paulo@0: ezPlaySample("line_wav", 128); paulo@0: } paulo@0: paulo@0: if (newKeys & VKEY_UP) { paulo@0: if (preset <= 0) { paulo@0: preset = nLoadedPresets + N_NONPRESETS - 1; paulo@0: } else { paulo@0: --preset; paulo@0: } paulo@0: ezPlaySample("shift_wav", 128); paulo@0: } paulo@0: if (newKeys & VKEY_DOWN) { paulo@0: ++preset; paulo@0: if (preset >= nLoadedPresets + N_NONPRESETS) { paulo@0: preset = 0; paulo@0: } paulo@0: ezPlaySample("shift_wav", 128); paulo@0: } paulo@0: paulo@0: if (newKeys & VKEY_LEFT) { paulo@0: if (preset < PRESETS_PER_COL) { paulo@0: preset += (((nLoadedPresets + N_NONPRESETS) paulo@0: / PRESETS_PER_COL) paulo@0: ) paulo@0: * PRESETS_PER_COL; paulo@0: if (preset >= nLoadedPresets + N_NONPRESETS) { paulo@0: preset -= PRESETS_PER_COL; paulo@0: } paulo@0: } else { paulo@0: preset -= PRESETS_PER_COL; paulo@0: } paulo@0: ezPlaySample("shift_wav", 128); paulo@0: } paulo@0: if (newKeys & VKEY_RIGHT) { paulo@0: preset += PRESETS_PER_COL; paulo@0: if (preset >= nLoadedPresets + N_NONPRESETS) { paulo@0: preset %= PRESETS_PER_COL; paulo@0: } paulo@0: ezPlaySample("shift_wav", 128); paulo@0: } paulo@0: paulo@0: if (preset != lastPreset) { paulo@0: vsync(); paulo@0: getPresetDrawRow(lastPreset, 0); paulo@0: getPresetDrawRow(preset, 1); paulo@0: lastPreset = preset; paulo@0: } else { paulo@0: rest(30); paulo@0: } paulo@0: lastKeys = keys; paulo@0: } paulo@0: paulo@0: // Wrap the nonpresets into the negative numbers. paulo@0: if (lastPreset >= nLoadedPresets) { paulo@0: lastPreset -= (nLoadedPresets + N_NONPRESETS); paulo@0: } paulo@0: paulo@0: return lastPreset; paulo@0: } paulo@0: paulo@0: /** paulo@0: * Pauses the game, returning nonzero if the player wants to quit. paulo@0: */ paulo@0: int pauseGame(LJPCView *v) { paulo@0: int escCount = 0; paulo@0: int quit = 0; paulo@0: int escHold = curTime; paulo@0: redrawWholeScreen = 1; paulo@0: paulo@0: LJMusic_pause(v->skin->bgm, 1); paulo@0: while (escCount < 2 && !quit) { paulo@0: LJMusic_poll(v->skin->bgm); paulo@0: if (redrawWholeScreen) { paulo@0: redrawWholeScreen = 0; paulo@0: clear_to_color(screen, pfBgColor); paulo@0: textout_centre_ex(screen, aver32, "LOCKJAW: GAME PAUSED", paulo@0: SCREEN_W / 2, 200, pfFgColor, -1); paulo@0: textout_centre_ex(screen, aver32, "Press Esc to continue", paulo@0: SCREEN_W / 2, 250, pfFgColor, -1); paulo@0: textout_centre_ex(screen, aver32, "or hold Esc to quit", paulo@0: SCREEN_W / 2, 300, pfFgColor, -1); paulo@0: } paulo@0: paulo@0: if (key[KEY_ESC]) { paulo@0: if (escHold == 0) { paulo@0: escHold = curTime; paulo@0: } paulo@0: if (curTime - escHold >= 60) { paulo@0: quit = 1; paulo@0: } paulo@0: } else { paulo@0: if (escHold) { paulo@0: ++escCount; paulo@0: } paulo@0: escHold = 0; paulo@0: } paulo@0: rest(30); paulo@0: if (wantsClose) { paulo@0: quit = 1; paulo@0: } paulo@0: paulo@0: } paulo@0: LJMusic_pause(v->skin->bgm, 0); paulo@0: redrawWholeScreen = 1; paulo@0: clear_keybuf(); paulo@0: return quit; paulo@0: } paulo@0: paulo@0: paulo@0: void pcInit(LJView *v, struct LJPrefs *prefs) { paulo@0: v->plat->b2bcd1 = 0; paulo@0: v->plat->b2bcd2 = 0; paulo@0: v->plat->baseX = v->plat->skin->baseX; paulo@0: paulo@0: // If the player has chosen to use more next pieces than the paulo@0: // next piece position can handle, set the number of paulo@0: // next pieces for correctness of debrief(). paulo@0: if (v->nextPieces > 3 && v->plat->skin->nextPos == LJNEXT_TOP) { paulo@0: v->nextPieces = 3; paulo@0: } paulo@0: } paulo@0: paulo@0: /** paulo@0: * Redraws everything on the screen. paulo@0: * Called when needs redraw. paulo@0: */ paulo@0: void playRedrawScreen(LJView *v) { paulo@0: blit(v->plat->skin->bg, screen, paulo@0: 0, 0, paulo@0: 0, 0, paulo@0: SCREEN_W, SCREEN_H); paulo@0: v->frontDirty = ~0; paulo@0: } paulo@0: paulo@0: #if 0 paulo@0: void startingAniWantsSkip(LJView *v) { paulo@0: LJInput unusedIn; paulo@0: addKeysToInput(&unusedIn, readPad(), v->field, v->control); paulo@0: } paulo@0: #endif paulo@0: paulo@0: void playSampleForTetromino(int piece); paulo@0: paulo@0: void restPollingMusic(int nFrames, LJMusic *bgm) { paulo@0: nFrames += curTime; paulo@0: while (curTime - nFrames < 0) { paulo@0: if (bgm) { paulo@0: LJMusic_poll(bgm); paulo@0: } paulo@0: } paulo@0: } paulo@0: paulo@0: extern int bgmReadyGo; paulo@0: void startingAnimation(LJView *v) { paulo@0: int readyGoX = v->plat->baseX + v->plat->skin->blkW * LJ_PF_WID / 2; paulo@0: int readyGoY = v->plat->skin->baseY paulo@0: - v->plat->skin->pfElev paulo@0: - v->plat->skin->blkH paulo@0: * v->field->ceiling * 3 / 5; paulo@0: paulo@0: clear_keybuf(); paulo@0: v->backDirty = 0; paulo@0: paulo@0: playRedrawScreen(v); paulo@0: blitField(v); paulo@0: textout_centre_ex(screen, aver32, "Ready", paulo@0: readyGoX, readyGoY, pfFgColor, -1); paulo@0: paulo@0: ezPlaySample("ready_wav", 128); paulo@0: restPollingMusic(36, bgmReadyGo ? v->plat->skin->bgm : NULL); paulo@0: v->frontDirty = ~0; paulo@0: paulo@0: if (!wantsClose) { paulo@0: blitField(v); paulo@0: textout_centre_ex(screen, aver32, "GO!", paulo@0: readyGoX, readyGoY, pfFgColor, -1); paulo@0: drawScore(v); paulo@0: paulo@0: ezPlaySample("go_wav", 128); paulo@0: v->frontDirty = ~0; paulo@0: restPollingMusic(12, bgmReadyGo ? v->plat->skin->bgm : NULL); paulo@0: } paulo@0: if (!wantsClose) { paulo@0: playSampleForTetromino(v->field->curPiece[1]); paulo@0: restPollingMusic(24, bgmReadyGo ? v->plat->skin->bgm : NULL); paulo@0: } paulo@0: } paulo@0: paulo@0: paulo@0: static void gameOverAnimation(const LJPCView *const v, const LJField *p, int won) { paulo@0: int ceiling = p->ceiling; paulo@0: int left = v->baseX + p->leftWall * v->skin->blkW; paulo@0: int right = v->baseX + p->rightWall * v->skin->blkW - 1; paulo@0: paulo@0: ezPlaySample("sectionup_wav", 0); paulo@0: if (!won) { paulo@0: ezPlaySample("gameover_wav", 256); paulo@0: } else { paulo@0: ezPlaySample("win_wav", 256); paulo@0: } paulo@0: paulo@0: for (int t = ceiling + v->skin->blkH - 2; t >= 0; --t) { paulo@0: paulo@0: // FIXME: vsync doesn't work on Vista paulo@0: vsync(); paulo@0: for (int row = ceiling - 1; row >= 0; --row) { paulo@0: int ysub = t - row; paulo@0: paulo@0: if (ysub >= 0 && ysub < v->skin->blkH) { paulo@0: int y = v->skin->baseY - v->skin->pfElev paulo@0: - row * v->skin->blkH - ysub - 1; paulo@0: hline(screen, left, y, right, pfBgColor); paulo@0: } paulo@0: } paulo@0: if (wantsClose) { paulo@0: t = 0; paulo@0: } paulo@0: } paulo@0: } paulo@0: paulo@0: #define MENU_COPR_NOTICE_LINES 4 paulo@0: const char *const menuCoprNotice[MENU_COPR_NOTICE_LINES] = { paulo@0: "Copr. 2006-2008 Damian Yerrick", paulo@0: "Not sponsored or endorsed by The Tetris Company.", paulo@0: "LOCKJAW comes with ABSOLUTELY NO WARRANTY. This is free software, and you are", paulo@0: "welcome to redistribute it under certain conditions as described in GPL.txt." paulo@0: }; paulo@0: paulo@0: static BITMAP *buildTitleScreen(void) { paulo@0: BITMAP *back = create_system_bitmap(SCREEN_W, SCREEN_H); paulo@0: if (!back) { paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: // Gradient from (0, 0, 0) to (0, 0, 153) paulo@0: for (int y = 0; y < 192; ++y) { paulo@0: for (int x = -((y * 13) & 0x1F); paulo@0: x < SCREEN_W; paulo@0: x += 32) { paulo@0: int colValue = y + ((rand() & 0x7000) >> 12); paulo@0: int c = makecol(0, 0, 153 * colValue / 192); paulo@0: hline(back, x, y, x + 31, c); paulo@0: } paulo@0: } paulo@0: paulo@0: // Gradient from (102, 51, 0) to (204, 102, 0) paulo@0: for (int y = 192; y < 384; ++y) { paulo@0: for (int x = -((y * 13) & 0x1F); paulo@0: x < SCREEN_W; paulo@0: x += 32) { paulo@0: int colValue = y + ((rand() & 0x7800) >> 11); paulo@0: int c = makecol(102 * colValue / 192, 51 * colValue / 192, 0); paulo@0: hline(back, x, y, x + 31, c); paulo@0: } paulo@0: } paulo@0: paulo@0: // Gradient from (204, 102, 0) to (255, 128, 0) paulo@0: for (int y = 384; y < SCREEN_H; ++y) { paulo@0: for (int x = -((y * 13) & 0x1F); paulo@0: x < SCREEN_W; paulo@0: x += 32) { paulo@0: int colValue = y - 400 + ((rand() & 0x7C00) >> 10); paulo@0: if (colValue > 600 - 384) { paulo@0: colValue = 600 - 384; paulo@0: } paulo@0: int c = makecol(204 + 50 * colValue / (600 - 384), paulo@0: 102 + 25 * colValue / (600 - 384), paulo@0: 0); paulo@0: hline(back, x, y, x + 31, c); paulo@0: } paulo@0: } paulo@0: paulo@0: DATAFILE *obj = find_datafile_object(dat, "arttitle_bmp"); paulo@0: BITMAP *logo = obj ? obj->dat : NULL; paulo@0: obj = find_datafile_object(dat, "arttitle_pal"); paulo@0: const RGB *pal = obj ? obj->dat : NULL; paulo@0: paulo@0: if (logo && pal) { paulo@0: set_palette(pal); paulo@0: draw_sprite(back, logo, paulo@0: (SCREEN_W - logo->w) / 2, (384 - logo->h) / 2); paulo@0: //unselect_palette(); paulo@0: } paulo@0: paulo@0: textout_centre_ex(back, aver32, "Arrows: change; Enter: choose", paulo@0: SCREEN_W / 2, 440, paulo@0: 0, -1); paulo@0: paulo@0: textout_centre_ex(back, aver32, "LOCKJAW: The Reference "LJ_VERSION, paulo@0: SCREEN_W / 2, SCREEN_H - 40, paulo@0: 0, -1); paulo@0: paulo@0: return back; paulo@0: } paulo@0: paulo@0: enum { paulo@0: TITLE_EXIT = 0, paulo@0: TITLE_PLAY, paulo@0: TITLE_REPLAY, paulo@0: TITLE_OPTIONS, paulo@0: TITLE_SKIN, paulo@0: TITLE_KEYS, paulo@0: N_TITLE_ACTIONS paulo@0: }; paulo@0: paulo@0: static const char *titleActions[N_TITLE_ACTIONS] = { paulo@0: [TITLE_EXIT] = "Exit", paulo@0: [TITLE_PLAY] = "Play", paulo@0: [TITLE_REPLAY] = "Replay", paulo@0: [TITLE_SKIN] = "Skin...", paulo@0: [TITLE_OPTIONS] = "Options...", paulo@0: [TITLE_KEYS] = "Game Keys..." paulo@0: }; paulo@0: /* paulo@0: 0: Exit paulo@0: 1: Play paulo@0: 2 paulo@0: */ paulo@0: int title(void) { paulo@0: paulo@0: // don't even draw if the player is trying to close the window paulo@0: if (wantsClose) { paulo@0: return 0; paulo@0: } paulo@0: paulo@0: BITMAP *back = buildTitleScreen(); paulo@0: LJBits lastKeys = ~0; paulo@0: int redraw = 1; paulo@0: int choice = 1; paulo@0: paulo@0: if (!back) { paulo@0: alert("Not enough memory to display the title screen.", paulo@0: "(If you don't even have RAM for a title screen,", paulo@0: "then what do you have RAM for?)", paulo@0: "Exit", 0, 13, 0); paulo@0: return 0; paulo@0: } paulo@0: paulo@0: redrawWholeScreen = 1; paulo@0: paulo@0: for(int done = 0; done == 0; ) { paulo@0: if (redrawWholeScreen) { paulo@0: redrawWholeScreen = 0; paulo@0: blit(back, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); paulo@0: redraw = 1; paulo@0: } paulo@0: if (redraw) { paulo@0: int dehilite = makecol(221, 153, 85); paulo@0: int white = makecol(255, 255, 255); paulo@0: redraw = 0; paulo@0: vsync(); paulo@0: blit(back, screen, 0, 400, 0, 400, SCREEN_W, 30); paulo@0: textout_centre_ex(screen, aver32, paulo@0: titleActions[choice], paulo@0: SCREEN_W / 2, 400, white, -1); paulo@0: textout_centre_ex(screen, aver32, paulo@0: titleActions[choice == 0 ? N_TITLE_ACTIONS - 1 : choice - 1], paulo@0: SCREEN_W / 2 - 160, 400, dehilite, -1); paulo@0: textout_centre_ex(screen, aver32, paulo@0: titleActions[choice == N_TITLE_ACTIONS - 1 ? 0 : choice + 1], paulo@0: SCREEN_W / 2 + 160, 400, dehilite, -1); paulo@0: } paulo@0: paulo@0: while (keypressed()) { paulo@0: int scancode; paulo@0: ureadkey(&scancode); paulo@0: if (scancode == KEY_PRTSCR) { paulo@0: saveScreen(-1); paulo@0: } paulo@0: } paulo@0: paulo@0: LJBits keys = menuReadPad(); paulo@0: LJBits newKeys = keys & ~lastKeys; paulo@0: paulo@0: if (newKeys & VKEY_LEFT) { paulo@0: --choice; paulo@0: redraw = 1; paulo@0: ezPlaySample("shift_wav", 128); paulo@0: } paulo@0: if (newKeys & VKEY_RIGHT) { paulo@0: ++choice; paulo@0: redraw = 1; paulo@0: ezPlaySample("shift_wav", 128); paulo@0: } paulo@0: if (newKeys & VKEY_ROTL) { paulo@0: choice = 0; paulo@0: redraw = 1; paulo@0: ezPlaySample("rotate_wav", 128); paulo@0: } paulo@0: if (newKeys & VKEY_ROTR) { paulo@0: done = 1; paulo@0: ezPlaySample("line_wav", 128); paulo@0: } paulo@0: if (choice < 0) { paulo@0: choice += N_TITLE_ACTIONS; paulo@0: } paulo@0: if (choice >= N_TITLE_ACTIONS) { paulo@0: choice -= N_TITLE_ACTIONS; paulo@0: } paulo@0: paulo@0: lastKeys = keys; paulo@0: paulo@0: if (!redraw) { paulo@0: rest(30); paulo@0: } paulo@0: if (wantsClose) { paulo@0: done = 1; paulo@0: choice = 0; paulo@0: } paulo@0: } paulo@0: destroy_bitmap(back); paulo@0: return choice; paulo@0: } paulo@0: paulo@0: paulo@0: void setupWindow(void) { paulo@0: set_window_title("LOCKJAW"); paulo@0: bgColor = makecol(255, 255, 255); paulo@0: pfFgColor = bgColor; paulo@0: hiliteColor = makecol(255, 255, 204); paulo@0: refreshRate = get_refresh_rate(); paulo@0: } paulo@0: paulo@0: void drawCoprNotice(void) { paulo@0: clear_to_color(screen, pfBgColor); paulo@0: for (int i = 0; i < MENU_COPR_NOTICE_LINES; ++i) { paulo@0: textout_ex(screen, font, menuCoprNotice[i], paulo@0: 16, 580 + 12 * (i - MENU_COPR_NOTICE_LINES), paulo@0: pfFgColor, -1); paulo@0: } paulo@0: } paulo@0: paulo@0: paulo@0: /** paulo@0: * Destroys the back buffers for both playfields. paulo@0: */ paulo@0: static void destroyBackBuf(LJPCView *plat) { paulo@0: for (int i = 0; i < MAX_PLAYERS; ++i) { paulo@0: if (plat->back) { paulo@0: destroy_bitmap(plat->back); paulo@0: plat->back = NULL; paulo@0: } paulo@0: } paulo@0: if (plat->skin->fullback) { paulo@0: destroy_bitmap(plat->skin->fullback); paulo@0: plat->skin->fullback = NULL; paulo@0: } paulo@0: } paulo@0: paulo@0: /** paulo@0: * Creates the back buffers for both playfields. paulo@0: */ paulo@0: static int createBackBuf(LJView *v) { paulo@0: v->plat->skin->fullback = create_system_bitmap(SCREEN_W, SCREEN_H); paulo@0: if(!v->plat->skin->fullback) { paulo@0: allegro_message("Could not create back buffer.\n"); paulo@0: return 0; paulo@0: } paulo@0: paulo@0: int blkW = v->plat->skin->blkW; paulo@0: int blkH = v->plat->skin->blkH; paulo@0: int y = v->plat->skin->baseY paulo@0: - v->plat->skin->pfElev paulo@0: - blkH * v->field->ceiling; paulo@0: paulo@0: for (int i = 0; i < MAX_PLAYERS; ++i) { paulo@0: int x = v->plat->skin->baseX paulo@0: + blkW * v[i].field->leftWall; paulo@0: paulo@0: v[i].plat->back = create_sub_bitmap(v->plat->skin->fullback, paulo@0: x + SCREEN_W / 2 * i, paulo@0: y, paulo@0: v->plat->skin->blkW * LJ_PF_WID, paulo@0: v->plat->skin->blkH * LJ_PF_VIS_HT); paulo@0: if (!v[i].plat->back) { paulo@0: destroyBackBuf(v->plat); paulo@0: return 0; paulo@0: } paulo@0: } paulo@0: return 1; paulo@0: } paulo@0: paulo@0: /** paulo@0: * Destroys all system bitmaps that a given view owns. paulo@0: * Useful before changing screen mode. paulo@0: */ paulo@0: void destroySystemBitmaps(LJPCView *plat) { paulo@0: destroyBackBuf(plat); paulo@0: if (plat->skin->connBlocks) { paulo@0: destroy_bitmap(plat->skin->connBlocks); paulo@0: plat->skin->connBlocks = NULL; paulo@0: } paulo@0: } paulo@0: paulo@0: int openWindow(int windowed) paulo@0: { paulo@0: int depth = desktop_color_depth(); paulo@0: int card = windowed ? GFX_AUTODETECT_WINDOWED : GFX_AUTODETECT_FULLSCREEN; paulo@0: paulo@0: /* Reference implementation for Allegro is not compatible with paulo@0: indexed color. */ paulo@0: if (depth < 15) { paulo@0: depth = 16; paulo@0: } paulo@0: paulo@0: // Full screen procedure paulo@0: set_color_depth(depth); paulo@0: if (set_gfx_mode(card, skinW, skinH, 0, 0) == 0) { paulo@0: setupWindow(); paulo@0: return 0; paulo@0: } paulo@0: paulo@0: // Windows can't tell 16 bit from 15 bit. If desktop color depth is reported as 16, try 15 too. paulo@0: if (depth == 16) { paulo@0: set_color_depth(15); paulo@0: if (set_gfx_mode(card, skinW, skinH, 0, 0) == 0) { paulo@0: setupWindow(); paulo@0: return 0; paulo@0: } paulo@0: } paulo@0: paulo@0: return -1; paulo@0: } paulo@0: paulo@0: BITMAP *loadConnections(const char *filename, int blkW, int blkH) { paulo@0: BITMAP *src = load_bitmap(filename, NULL); paulo@0: paulo@0: if (!src) { paulo@0: return NULL; paulo@0: } paulo@0: BITMAP *dst = create_system_bitmap(blkW*16, blkH*16); paulo@0: if (!dst) { paulo@0: destroy_bitmap(src); paulo@0: return NULL; paulo@0: } paulo@0: acquire_bitmap(dst); paulo@0: for (unsigned int col = 0; col < 16; ++col) { paulo@0: unsigned int srcXBase = (col & 0x03) * blkW * 2; paulo@0: unsigned int srcYBase = (col >> 2) * blkH * 2; paulo@0: unsigned int dstYBase = col * blkH; paulo@0: for (unsigned int conn = 0; conn < 16; ++conn) { paulo@0: unsigned int dstXBase = conn * blkW; paulo@0: unsigned int topSegY = (conn & CONNECT_U) ? blkH : 0; paulo@0: unsigned int botSegY = (conn & CONNECT_D) ? blkH/2 : 3*blkH/2; paulo@0: unsigned int leftSegX = (conn & CONNECT_L) ? blkW : 0; paulo@0: unsigned int rightSegX = (conn & CONNECT_R) ? blkW/2 : 3*blkW/2; paulo@0: blit(src, dst, paulo@0: srcXBase + leftSegX, srcYBase + topSegY, paulo@0: dstXBase + 0, dstYBase + 0, paulo@0: blkW/2, blkH/2); paulo@0: blit(src, dst, paulo@0: srcXBase + rightSegX, srcYBase + topSegY, paulo@0: dstXBase + blkW/2, dstYBase + 0, paulo@0: blkW/2, blkH/2); paulo@0: blit(src, dst, paulo@0: srcXBase + leftSegX, srcYBase + botSegY, paulo@0: dstXBase + 0, dstYBase + blkH/2, paulo@0: blkW/2, blkH/2); paulo@0: blit(src, dst, paulo@0: srcXBase + rightSegX, srcYBase + botSegY, paulo@0: dstXBase + blkW/2, dstYBase + blkH/2, paulo@0: blkW/2, blkH/2); paulo@0: } paulo@0: } paulo@0: release_bitmap(dst); paulo@0: destroy_bitmap(src); paulo@0: return dst; paulo@0: } paulo@0: paulo@0: void closeWindow(void) { paulo@0: set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); paulo@0: } paulo@0: paulo@0: static void mainCleanup(LJPCView *v) { paulo@0: destroyBackBuf(v); paulo@0: if (v->skin->blocks) { paulo@0: destroy_bitmap(v->skin->blocks); paulo@0: } paulo@0: if (v->skin->connBlocks) { paulo@0: destroy_bitmap(v->skin->connBlocks); paulo@0: } paulo@0: LJMusic_delete(v->skin->bgm); paulo@0: if (withSound) { paulo@0: remove_sound(); paulo@0: } paulo@0: closeWindow(); paulo@0: } paulo@0: paulo@0: /** paulo@0: * Resets all skin settings to their initial values paulo@0: * so that skins can override them. paulo@0: */ paulo@0: void loadSkinDefaults(LJPCSkin *s) { paulo@0: ustrzcpy(ljblocksSRSName, sizeof(ljblocksSRSName) - 1, paulo@0: "ljblocks.bmp"); paulo@0: ustrzcpy(ljblocksSegaName, sizeof(ljblocksSegaName) - 1, paulo@0: "ljblocks-sega.bmp"); paulo@0: ustrzcpy(ljconnSRSName, sizeof(ljconnSRSName) - 1, paulo@0: "ljconn.bmp"); paulo@0: ustrzcpy(ljconnSegaName, sizeof(ljconnSegaName) - 1, paulo@0: "ljconn-sega.bmp"); paulo@0: ustrzcpy(ljbgName, sizeof(ljbgName) - 1, paulo@0: "ljbg.jpg"); paulo@0: ustrzcpy(menubgName, sizeof(ljbgName) - 1, paulo@0: "menubg.jpg"); paulo@0: ustrzcpy(bgmName, sizeof(bgmName) - 1, paulo@0: "bgm.s3m"); paulo@0: ustrzcpy(bgmRhythmName, sizeof(bgmRhythmName) - 1, paulo@0: "bgm-rhythm.s3m"); paulo@0: bgmLoopPoint = 0; paulo@0: bgmReadyGo = 0; paulo@0: bgmVolume = 128; paulo@0: bgColor = makecol(255, 255, 255); paulo@0: fgColor = makecol(0, 0, 0); paulo@0: hiliteColor = makecol(255, 255, 204); paulo@0: pfBgColor = makecol(0, 0, 0); paulo@0: pfFgColor = makecol(255, 255, 255); paulo@0: s->blkW = 24; paulo@0: s->blkH = 24; paulo@0: s->transparentPF = 0; paulo@0: s->shiftScale = 0; paulo@0: s->baseX = 0; paulo@0: s->nextPos = 0; paulo@0: } paulo@0: paulo@0: /** paulo@0: * Converts a single hexadecimal digit to its value. paulo@0: * @param in USASCII/Unicode value of hex digit character paulo@0: * @return value paulo@0: */ paulo@0: int hexDigitValue(int in) { paulo@0: if (in >= '0' && in <= '9') { paulo@0: return in - '0'; paulo@0: } else if (in >= 'A' && in <= 'F') { paulo@0: return in - 'A' + 10; paulo@0: } else if (in >= 'a' && in <= 'f') { paulo@0: return in - 'a' + 10; paulo@0: } else { paulo@0: return -1; paulo@0: } paulo@0: } paulo@0: paulo@0: int translateComponent(const char **in, size_t nDigits) { paulo@0: const char *here = *in; paulo@0: int hi = hexDigitValue(*here++); paulo@0: int lo = nDigits > 3 ? hexDigitValue(*here++) : hi; paulo@0: *in = here; paulo@0: if (hi >= 0 && lo >= 0) { paulo@0: return hi * 16 + lo; paulo@0: } else { paulo@0: return -1; paulo@0: } paulo@0: } paulo@0: paulo@0: /** paulo@0: * Interprets a hexadecimal color specifier. paulo@0: * @param in hexadecimal color specifier, in #ABC or #AABBCC format paulo@0: * @return Allegro device-dependent color, in makecol() format paulo@0: */ paulo@0: int translateColor(const char *in) { paulo@0: size_t nDigits = 0; paulo@0: paulo@0: // Verify and skip # paulo@0: if (*in != '#') { paulo@0: return -1; paulo@0: } paulo@0: ++in; paulo@0: paulo@0: // Determine whether we have a 3-digit color or a 6-digit color paulo@0: for (const char *here = in; paulo@0: hexDigitValue(*here) >= 0; paulo@0: ++here) { paulo@0: ++nDigits; paulo@0: } paulo@0: if (nDigits != 3 && nDigits != 6) { paulo@0: return -1; paulo@0: } paulo@0: paulo@0: int red = translateComponent(&in, nDigits); paulo@0: int green = translateComponent(&in, nDigits); paulo@0: int blue = translateComponent(&in, nDigits); paulo@0: if (red >= 0 && green >= 0 && blue >= 0) { paulo@0: return makecol(red, green, blue); paulo@0: } else { paulo@0: return -1; paulo@0: } paulo@0: }; paulo@0: paulo@0: /** paulo@0: * Loads skin parameters from the file. paulo@0: * @param skinName Name of skin ini file paulo@0: */ paulo@0: int loadSkinFile(LJPCSkin *s, const char *skinName) { paulo@0: paulo@0: // Don't use ljfopen here because lj.ini specifies the absolute skin file paulo@0: FILE *fp = fopen(skinName, "rt"); paulo@0: char key[1024], var[1024], val[1024], input_buf[1024]; paulo@0: paulo@0: key[0] = 0; paulo@0: var[0] = 0; paulo@0: val[0] = 0; paulo@0: loadSkinDefaults(s); paulo@0: paulo@0: if (!fp) return 0; paulo@0: while(1) { paulo@0: int rval; paulo@0: paulo@0: if(!fgets (input_buf, sizeof(input_buf), fp)) paulo@0: break; paulo@0: rval = parse_ini_line(input_buf, key, var, val); paulo@0: paulo@0: if(!ustrcmp ("ljblocksSRS", var)) { paulo@0: ustrzcpy(ljblocksSRSName, sizeof(ljblocksSRSName) - 1, val); paulo@0: } paulo@0: else if(!ustrcmp ("ljblocksSega", var)) { paulo@0: ustrzcpy(ljblocksSegaName, sizeof(ljblocksSegaName) - 1, val); paulo@0: } paulo@0: else if(!ustrcmp ("ljconnSRS", var)) { paulo@0: ustrzcpy(ljconnSRSName, sizeof(ljconnSRSName) - 1, val); paulo@0: } paulo@0: else if(!ustrcmp ("ljconnSega", var)) { paulo@0: ustrzcpy(ljconnSegaName, sizeof(ljconnSegaName) - 1, val); paulo@0: } paulo@0: else if(!ustrcmp ("bgm", var)) { paulo@0: ustrzcpy(bgmName, sizeof(bgmName) - 1, val); paulo@0: } paulo@0: else if(!ustrcmp ("bgmRhythm", var)) { paulo@0: ustrzcpy(bgmRhythmName, sizeof(bgmRhythmName) - 1, val); paulo@0: } paulo@0: else if(!ustrcmp ("ljbg", var)) { paulo@0: ustrzcpy(ljbgName, sizeof(ljbgName) - 1, val); paulo@0: } paulo@0: else if(!ustrcmp ("bgmLoopPoint", var)) { paulo@0: unsigned long int c = strtoul(val, NULL, 0); paulo@0: if (c >= 0) { paulo@0: bgmLoopPoint = c; paulo@0: } paulo@0: } paulo@0: else if(!ustrcmp ("bgmVolume", var)) { paulo@0: unsigned long int c = strtol(val, NULL, 0); paulo@0: if (c >= 0) { paulo@0: bgmVolume = c; paulo@0: } paulo@0: } paulo@0: else if(!ustrcmp ("bgmReadyGo", var)) { paulo@0: unsigned long int c = strtoul(val, NULL, 0); paulo@0: if (c >= 0) { paulo@0: bgmReadyGo = c; paulo@0: } paulo@0: } paulo@0: else if(!ustrcmp ("pfbgcolor", var)) { paulo@0: int c = translateColor(val); paulo@0: if (c >= 0) { paulo@0: pfBgColor = c; paulo@0: } paulo@0: } paulo@0: else if(!ustrcmp ("pfcolor", var)) { paulo@0: int c = translateColor(val); paulo@0: if (c >= 0) { paulo@0: pfFgColor = c; paulo@0: } paulo@0: } paulo@0: else if(!ustrcmp ("bgcolor", var)) { paulo@0: int c = translateColor(val); paulo@0: if (c >= 0) { paulo@0: bgColor = c; paulo@0: } paulo@0: } paulo@0: else if(!ustrcmp ("color", var)) { paulo@0: int c = translateColor(val); paulo@0: if (c >= 0) { paulo@0: fgColor = c; paulo@0: } paulo@0: } paulo@0: else if(!ustrcmp ("hilitecolor", var)) { paulo@0: int c = translateColor(val); paulo@0: if (c >= 0) { paulo@0: hiliteColor = c; paulo@0: } paulo@0: } paulo@0: else if(!ustrcmp ("blkW", var)) { paulo@0: int c = strtol(val, NULL, 0); paulo@0: if (c >= 0) { paulo@0: s->blkW = c; paulo@0: } paulo@0: } paulo@0: else if(!ustrcmp ("blkH", var)) { paulo@0: int c = strtol(val, NULL, 0); paulo@0: if (c >= 0) { paulo@0: s->blkH = c; paulo@0: } paulo@0: } paulo@0: else if(!ustrcmp ("transparentPF", var)) { paulo@0: int c = atoi(val); paulo@0: if (c >= 0) { paulo@0: s->transparentPF = c; paulo@0: } paulo@0: } paulo@0: else if(!ustrcmp ("shiftScale", var)) { paulo@0: int c = atoi(val); paulo@0: if (c >= 0) { paulo@0: s->shiftScale = c; paulo@0: } paulo@0: } paulo@0: else if(!ustrcmp ("baseX", var)) { paulo@0: int c = atoi(val); paulo@0: if (c >= 0) { paulo@0: s->baseX = c; paulo@0: } paulo@0: } paulo@0: else if(!ustrcmp ("nextPos", var)) { paulo@0: int c = atoi(val); paulo@0: if (c >= 0) { paulo@0: s->nextPos = c; paulo@0: } paulo@0: } paulo@0: else if(!ustrcmp ("wndW", var)) { paulo@0: unsigned long int c = strtol(val, NULL, 0); paulo@0: if (c >= 0) { paulo@0: skinW = c; paulo@0: } paulo@0: } paulo@0: else if(!ustrcmp ("wndH", var)) { paulo@0: unsigned long int c = strtoul(val, NULL, 0); paulo@0: if (c >= 0) { paulo@0: skinH = c; paulo@0: } paulo@0: } paulo@0: paulo@0: } paulo@0: fclose(fp); paulo@0: ljpathSetSkinFolder(skinName); paulo@0: return 0; paulo@0: } paulo@0: paulo@0: static void drawProgressSegment(int min, int max) { paulo@0: min = min * SCREEN_W / 100; paulo@0: max = max * SCREEN_W / 100; paulo@0: int blue = makecol(0, 0, 255); paulo@0: rectfill(screen, min, SCREEN_H - 8, max - 1, SCREEN_H - 5, blue); paulo@0: int orange = makecol(255, 128, 0); paulo@0: rectfill(screen, min, SCREEN_H - 4, max - 1, SCREEN_H - 1, orange); paulo@0: } paulo@0: paulo@0: static int loadSkin(LJView *v, const char *skinName) { paulo@0: BITMAP *bmp; paulo@0: const LJRotSystem *rs = rotSystems[v->field->rotationSystem]; paulo@0: int colorScheme = rs->colorScheme; paulo@0: paulo@0: rectfill(screen, 0, 592, 799, 599, 0); paulo@0: loadSkinFile(v->plat->skin, skinName); paulo@0: paulo@0: destroyBackBuf(v->plat); paulo@0: if (!createBackBuf(v)) { paulo@0: allegro_message("Could not create back buffer.\n"); paulo@0: return 1; paulo@0: } paulo@0: paulo@0: drawProgressSegment(0, 20); paulo@0: paulo@0: // Load background image paulo@0: char path[PATH_MAX]; paulo@0: bmp = ljpathFind_r(path, ljbgName) paulo@0: ? load_bitmap(path, NULL) : NULL; paulo@0: if (v->plat->skin->bg) { paulo@0: destroy_bitmap(v->plat->skin->bg); paulo@0: } paulo@0: if (bmp) { paulo@0: paulo@0: // If the image size doesn't match the window size, resize it paulo@0: if (bmp->w != SCREEN_W || bmp->h != SCREEN_H) { paulo@0: BITMAP *resized = create_bitmap(SCREEN_W, SCREEN_H); paulo@0: paulo@0: if (resized) { paulo@0: stretch_blit(bmp, resized, 0, 0, bmp->w, bmp->h, paulo@0: 0, 0, SCREEN_W, SCREEN_H); paulo@0: destroy_bitmap(bmp); paulo@0: } paulo@0: if (bmp) { paulo@0: bmp = resized; paulo@0: } else { paulo@0: allegro_message("Background image \"%s\" resize failed.\n", paulo@0: ljbgName); paulo@0: } paulo@0: } paulo@0: } paulo@0: if(!bmp) { paulo@0: bmp = create_bitmap(SCREEN_W, SCREEN_H); paulo@0: if (bmp) { paulo@0: allegro_message("Background image \"%s\" not found.\n" paulo@0: "Using plain background instead.\n", paulo@0: ljbgName); paulo@0: clear_to_color(bmp, bgColor); paulo@0: } else { paulo@0: allegro_message("Background image \"%s\" not found.\n", paulo@0: ljbgName); paulo@0: return 0; paulo@0: } paulo@0: } paulo@0: v->plat->skin->bg = bmp; paulo@0: drawProgressSegment(20, 40); paulo@0: paulo@0: // load block images paulo@0: if (v->plat->skin->blocks) { paulo@0: destroy_bitmap(v->plat->skin->blocks); paulo@0: } paulo@0: bmp = ljpathFind_r(path, colorScheme paulo@0: ? ljblocksSegaName paulo@0: : ljblocksSRSName) paulo@0: ? load_bitmap(path, NULL) : NULL; paulo@0: v->plat->skin->blocks = bmp; paulo@0: if(!v->plat->skin->blocks) { paulo@0: allegro_message("Background image \"%s\" not found.\n", paulo@0: ljbgName); paulo@0: return 0; paulo@0: } paulo@0: drawProgressSegment(40, 60); paulo@0: paulo@0: // load connected block images paulo@0: if (v->plat->skin->connBlocks) { paulo@0: destroy_bitmap(v->plat->skin->connBlocks); paulo@0: } paulo@0: bmp = ljpathFind_r(path, colorScheme paulo@0: ? ljconnSegaName paulo@0: : ljconnSRSName) paulo@0: ? loadConnections(path, paulo@0: v->plat->skin->blkW, paulo@0: v->plat->skin->blkH) paulo@0: : NULL; paulo@0: v->plat->skin->connBlocks = bmp; paulo@0: drawProgressSegment(60, 80); paulo@0: paulo@0: // load music paulo@0: int isRhythm = (v->field->speedState.curve == LJSPD_RHYTHM); paulo@0: if (ljpathFind_r(path, isRhythm ? bgmRhythmName : bgmName)) { paulo@0: LJMusic_load(v->plat->skin->bgm, path); paulo@0: } paulo@0: if (!isRhythm) { paulo@0: LJMusic_setLoop(v->plat->skin->bgm, bgmLoopPoint); paulo@0: } paulo@0: drawProgressSegment(80, 100); paulo@0: return 1; paulo@0: } paulo@0: paulo@0: void drop_mouse(void) { paulo@0: while (mouse_y < SCREEN_H - 25) { paulo@0: position_mouse(mouse_x, mouse_y + 20); paulo@0: rest(10); paulo@0: } paulo@0: ezPlaySample("land_wav", 192); paulo@0: position_mouse(mouse_x, SCREEN_H - 5); paulo@0: ezPlaySample("lock_wav", 192); paulo@0: } paulo@0: paulo@0: int pickReplay(void) { paulo@0: FONT *oldFont = font; paulo@0: font = (FONT *)aver16; paulo@0: install_mouse(); paulo@0: int got = file_select_ex("Choose a demo:", demoFilename, "ljm", sizeof(demoFilename), 600, 400); paulo@0: drop_mouse(); paulo@0: remove_mouse(); paulo@0: font = oldFont; paulo@0: return got ? 0 : -1; paulo@0: } paulo@0: paulo@0: void badReplay(void) { paulo@0: acquire_screen(); paulo@0: clear_to_color(screen, bgColor); paulo@0: textout_ex(screen, aver32, "The demo", 100, 100, fgColor, -1); paulo@0: textout_ex(screen, aver16, demoFilename, 100, 130, fgColor, -1); paulo@0: textout_ex(screen, aver32, "could not be played because it was", 100, 150, fgColor, -1); paulo@0: textout_ex(screen, aver32, "recorded with a different version", 100, 180, fgColor, -1); paulo@0: textout_ex(screen, aver32, "of LOCKJAW software.", 100, 210, fgColor, -1); paulo@0: release_screen(); paulo@0: paulo@0: LJBits lastKeys = ~0; paulo@0: LJBits keys, newKeys = 0; paulo@0: paulo@0: do { paulo@0: keys = menuReadPad(); paulo@0: newKeys = keys & ~lastKeys; paulo@0: lastKeys = keys; paulo@0: rest(30); paulo@0: } while (!(newKeys & (VKEY_ROTL | VKEY_ROTR))); paulo@0: } paulo@0: paulo@0: int pickSkin(void) { paulo@0: FONT *oldFont = font; paulo@0: font = (FONT *)aver16; paulo@0: install_mouse(); paulo@0: int got = file_select_ex("Choose a skin:", skinName, "skin", sizeof(skinName), 600, 400); paulo@0: drop_mouse(); paulo@0: remove_mouse(); paulo@0: font = oldFont; paulo@0: return got ? 0 : -1; paulo@0: } paulo@0: paulo@0: void calcElev(LJView *v) { paulo@0: int blkH = v->plat->skin->blkH; paulo@0: int ceiling = v->field->ceiling; paulo@0: int elev = (LJ_PF_VIS_HT - ceiling) * blkH; paulo@0: paulo@0: if (elev > 480 - ceiling * blkH) { paulo@0: elev = 480 - ceiling * blkH; paulo@0: } paulo@0: if (elev < 0) { paulo@0: elev = 0; paulo@0: } paulo@0: v->plat->skin->pfElev = elev; paulo@0: } paulo@0: paulo@0: int main(const int argc, const char *const *argv) { paulo@0: const char *cmdLineDemo = NULL; paulo@0: int lastPreset = -2; // start out with full custom paulo@0: LJPCSkin skin = { paulo@0: .baseY = 552, paulo@0: .blkW = 24, paulo@0: .blkH = 24 paulo@0: }; paulo@0: LJField p[MAX_PLAYERS]; paulo@0: LJControl control[2] = { paulo@0: { paulo@0: .replaySrc = 0, paulo@0: .replayDst = 0 paulo@0: } paulo@0: }; paulo@0: LJPCView platView[MAX_PLAYERS] = { paulo@0: { paulo@0: .skin = &skin paulo@0: }, paulo@0: { paulo@0: .skin = &skin paulo@0: } paulo@0: }; paulo@0: LJView mainView[MAX_PLAYERS] = { paulo@0: { paulo@0: .field = &p[0], paulo@0: .control = &control[0], paulo@0: .plat = &platView[0], paulo@0: .backDirty = ~0 paulo@0: }, paulo@0: { paulo@0: .field = &p[1], paulo@0: .control = &control[1], paulo@0: .plat = &platView[1], paulo@0: .backDirty = ~0, paulo@0: } paulo@0: }; paulo@0: paulo@0: struct LJPrefs prefs = { paulo@0: .number = { paulo@0: [OPTIONS_TRAILS] = 1, paulo@0: [OPTIONS_AUTO_PAUSE] = 1, paulo@0: [OPTIONS_AUTO_RECORD] = 0, paulo@0: [OPTIONS_WINDOWED] = 1 paulo@0: } paulo@0: }; paulo@0: paulo@0: // as of 0.46, we're starting to make it a bit more 2-player-clean paulo@0: int nPlayers = 1; paulo@0: paulo@0: allegro_init(); paulo@0: ljpathInit(argc > 0 ? argv[0] : "."); paulo@0: install_timer(); paulo@0: initOptions(prefs.number); paulo@0: loadOptions(&prefs); paulo@0: loadSkinFile(&skin, skinName); paulo@0: paulo@0: if (argc > 1) { paulo@0: if (argv[1][0] == '-') { paulo@0: if (!ustrcmp("--help", argv[1]) paulo@0: || !ustrcmp("-h", argv[1])) { paulo@0: allegro_message("Usage: lj [DEMOFILE]\n"); paulo@0: return 0; paulo@0: } paulo@0: } else { paulo@0: cmdLineDemo = argv[1]; paulo@0: } paulo@0: } paulo@0: paulo@0: if (openWindow(prefs.number[OPTIONS_WINDOWED]) != 0) { paulo@0: allegro_message("LOCKJAW fatal error: Could not open an %dx%d pixel %s.\n" paulo@0: "Trying %s next time.\n", paulo@0: skinW, skinH, paulo@0: prefs.number[OPTIONS_WINDOWED] ? "window": "screen mode", paulo@0: prefs.number[OPTIONS_WINDOWED] ? "the full screen": "a window"); paulo@0: prefs.number[OPTIONS_WINDOWED] = !prefs.number[OPTIONS_WINDOWED]; paulo@0: saveOptions(&prefs); paulo@0: return EXIT_FAILURE; paulo@0: } paulo@0: drawCoprNotice(); paulo@0: LOCK_FUNCTION(incCurTime); paulo@0: LOCK_VARIABLE(curTime); paulo@0: install_int_ex(incCurTime, BPM_TO_TIMER(LJ_TICK_RATE)); paulo@0: paulo@0: jpgalleg_init(); paulo@0: set_color_conversion(COLORCONV_NONE); paulo@0: { paulo@0: char path[PATH_MAX]; paulo@0: if (ljpathFind_r(path, "lj.dat")) { paulo@0: dat = load_datafile(path); paulo@0: } paulo@0: } paulo@0: set_color_conversion(COLORCONV_TOTAL); paulo@0: if(!dat) { paulo@0: closeWindow(); paulo@0: allegro_message("LOCKJAW fatal error: Could not load datafile lj.dat\n"); paulo@0: return 1; paulo@0: } paulo@0: paulo@0: { paulo@0: const DATAFILE *aver16dat = find_datafile_object(dat, "Aver16_bmp"); paulo@0: aver16 = aver16dat ? aver16dat->dat : font; paulo@0: const DATAFILE *aver32dat = find_datafile_object(dat, "Aver32_bmp"); paulo@0: aver32 = aver32dat ? aver32dat->dat : aver16; paulo@0: } paulo@0: paulo@0: LOCK_FUNCTION(amnesia); paulo@0: LOCK_VARIABLE(redrawWholeScreen); paulo@0: paulo@0: // If we can be notified on switching out, take this notification. paulo@0: if (set_display_switch_mode(SWITCH_BACKGROUND) >= 0 paulo@0: || set_display_switch_mode(SWITCH_BACKAMNESIA) >= 0) { paulo@0: set_display_switch_callback(SWITCH_OUT, requestPause); paulo@0: } paulo@0: set_display_switch_callback(SWITCH_IN, amnesia); paulo@0: paulo@0: install_keyboard(); paulo@0: initKeys(); paulo@0: paulo@0: for (int i = 0; i < MAX_PLAYERS; ++i) { paulo@0: p[i].seed = time(NULL) + 123456789*i; paulo@0: } paulo@0: paulo@0: reserve_voices(8, 0); paulo@0: set_volume_per_voice(0); paulo@0: #ifdef ALLEGRO_WINDOWS paulo@0: // Under Windows, use the Allegro mixer because on my machine paulo@0: // and probably others, the built-in mixer will replace the very paulo@0: // beginning of one sound with the end of the last sound played paulo@0: // on that voice. paulo@0: withSound = !install_sound(DIGI_DIRECTAMX(0), MIDI_NONE, NULL); paulo@0: #else paulo@0: withSound = !install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL); paulo@0: #endif paulo@0: skin.bgm = LJMusic_new(); paulo@0: { paulo@0: char path[PATH_MAX]; paulo@0: if (ljpathFind_r(path, "sound.dat")) { paulo@0: sound_dat = load_datafile(path); paulo@0: } paulo@0: } paulo@0: paulo@0: for (int i = 0; i < MAX_PLAYERS; ++i) { paulo@0: unpackOptions(&mainView[i], &prefs); paulo@0: } paulo@0: if (loadSkin(&mainView[0], skinName) < 0) { paulo@0: mainCleanup(platView); paulo@0: return EXIT_FAILURE; paulo@0: } else { paulo@0: paulo@0: } paulo@0: paulo@0: if(!skin.blocks) { paulo@0: mainCleanup(platView); paulo@0: allegro_message("Blocks image \"%s\" not found.\n", paulo@0: p[0].rotationSystem paulo@0: ? ljblocksSegaName paulo@0: : ljblocksSRSName); paulo@0: return 1; paulo@0: } paulo@0: paulo@0: srand(time(NULL)); paulo@0: paulo@0: // Wait for copyright notice to be displayed "conspicuously" paulo@0: if (curTime < 180) { paulo@0: rest(3100 - curTime * 16); paulo@0: } paulo@0: paulo@0: for (int action = cmdLineDemo ? TITLE_REPLAY : title(); paulo@0: action > TITLE_EXIT && !wantsClose; paulo@0: action = title()) { paulo@0: switch (action) { paulo@0: case TITLE_PLAY: paulo@0: for (int preset = getPreset(lastPreset); paulo@0: preset != -1 && !wantsClose; paulo@0: preset = getPreset(lastPreset)) paulo@0: { paulo@0: lastPreset = preset; paulo@0: for (int i = 0; i < MAX_PLAYERS; ++i) { paulo@0: unpackOptions(&mainView[i], &prefs); paulo@0: } paulo@0: if (preset >= 0) { paulo@0: presetStart(); paulo@0: presetAdd(preset); paulo@0: for (int i = 0; i < MAX_PLAYERS; ++i) { paulo@0: unpackOptions(&mainView[i], &prefs); paulo@0: presetFinish(&mainView[i]); paulo@0: } paulo@0: } paulo@0: // reload the skin paulo@0: if (loadSkin(&mainView[0], skinName) < 0) { paulo@0: mainCleanup(platView); paulo@0: return EXIT_FAILURE; paulo@0: }; paulo@0: for (int i = 0; i < nPlayers; ++i) { paulo@0: calcElev(&mainView[0]); paulo@0: pcInit(&mainView[0], &prefs); paulo@0: } paulo@0: wantPause = 0; paulo@0: platView[0].wantRecord = prefs.number[OPTIONS_AUTO_RECORD]; paulo@0: LJMusic_start(skin.bgm, paulo@0: 4096, // mix buffer size paulo@0: bgmVolume); // volume scale paulo@0: paulo@0: LJView *const players[MAX_PLAYERS] = {&mainView[0], &mainView[1]}; paulo@0: play(players, nPlayers); paulo@0: LJMusic_stop(skin.bgm); paulo@0: if (!wantsClose) { paulo@0: gameOverAnimation(&platView[0], &p[0], paulo@0: control[0].countdown <= 0); paulo@0: debrief(&mainView[0]); paulo@0: } paulo@0: } paulo@0: break; paulo@0: paulo@0: case TITLE_REPLAY: paulo@0: { paulo@0: if (cmdLineDemo) { paulo@0: ustrzcpy(demoFilename, sizeof(demoFilename) - 1, paulo@0: cmdLineDemo); paulo@0: cmdLineDemo = NULL; paulo@0: } else if (pickReplay() < 0) { paulo@0: break; paulo@0: } paulo@0: paulo@0: unpackOptions(&mainView[0], &prefs); paulo@0: calcElev(&mainView[0]); paulo@0: pcInit(&mainView[0], &prefs); paulo@0: wantPause = 0; paulo@0: platView[0].wantRecord = 0; paulo@0: LJMusic_start(skin.bgm, paulo@0: 4096, // mix buffer size paulo@0: bgmVolume); // volume scale paulo@0: paulo@0: LJView *const players[2] = {&mainView[0], &mainView[1]}; paulo@0: paulo@0: p->gimmick = -1; // gimmick must be < 0 to activate replay paulo@0: play(players, 1); paulo@0: LJMusic_stop(skin.bgm); paulo@0: if (p[0].gimmick < 0) { paulo@0: badReplay(); paulo@0: } else { paulo@0: if (!wantsClose) { paulo@0: gameOverAnimation(&platView[0], &p[0], paulo@0: control[0].countdown <= 0); paulo@0: debrief(&mainView[0]); paulo@0: } paulo@0: } paulo@0: } paulo@0: break; paulo@0: paulo@0: case TITLE_SKIN: paulo@0: pickSkin(); paulo@0: paulo@0: // if resolution changed, reopen the window paulo@0: { paulo@0: int oldW = skinW; paulo@0: int oldH = skinH; paulo@0: loadSkinFile(&skin, skinName); paulo@0: if (skinH != oldH || skinW != oldW) { paulo@0: destroySystemBitmaps(&platView[0]); paulo@0: openWindow(prefs.number[OPTIONS_WINDOWED]); paulo@0: } paulo@0: } paulo@0: paulo@0: // reload the skin paulo@0: if (loadSkin(&mainView[0], skinName) < 0) { paulo@0: mainCleanup(platView); paulo@0: return EXIT_FAILURE; paulo@0: }; paulo@0: paulo@0: // save options paulo@0: saveOptions(&prefs); paulo@0: break; paulo@0: paulo@0: case TITLE_OPTIONS: paulo@0: { paulo@0: int oldWindowed = prefs.number[OPTIONS_WINDOWED]; paulo@0: options(&mainView[0], prefs.number); paulo@0: if (oldWindowed != prefs.number[OPTIONS_WINDOWED]) { paulo@0: destroySystemBitmaps(&platView[0]); paulo@0: openWindow(prefs.number[OPTIONS_WINDOWED]); paulo@0: } paulo@0: } paulo@0: saveOptions(&prefs); paulo@0: paulo@0: // reload the skin if the player changed the rotation system paulo@0: unpackOptions(&mainView[0], &prefs); paulo@0: if (wantsClose) { paulo@0: break; paulo@0: } paulo@0: if (loadSkin(&mainView[0], skinName) < 0) { paulo@0: mainCleanup(platView); paulo@0: return EXIT_FAILURE; paulo@0: }; paulo@0: break; paulo@0: paulo@0: case TITLE_KEYS: paulo@0: configureKeys(); paulo@0: break; paulo@0: paulo@0: } paulo@0: } paulo@0: paulo@0: mainCleanup(platView); paulo@0: return 0; paulo@0: } END_OF_MAIN();