Mercurial > hg > index.fcgi > lj > lj046-2players
diff src/ljpc.c @ 0:c84446dfb3f5
initial add
author | paulo@localhost |
---|---|
date | Fri, 13 Mar 2009 00:39:12 -0700 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/ljpc.c Fri Mar 13 00:39:12 2009 -0700 1.3 @@ -0,0 +1,2156 @@ 1.4 +/* PC frontend for LOCKJAW, an implementation of the Soviet Mind Game 1.5 + 1.6 +Copyright (C) 2006-2008 Damian Yerrick <tepples+lj@spamcop.net> 1.7 + 1.8 +This work is free software; you can redistribute it and/or modify 1.9 +it under the terms of the GNU General Public License as published by 1.10 +the Free Software Foundation; either version 2 of the License, or 1.11 +(at your option) any later version. 1.12 + 1.13 +This program is distributed in the hope that it will be useful, 1.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 1.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1.16 +GNU General Public License for more details. 1.17 + 1.18 +You should have received a copy of the GNU General Public License 1.19 +along with this program; if not, write to the Free Software 1.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 1.21 + 1.22 +Original game concept and design by Alexey Pajitnov. 1.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 1.24 +or The Tetris Company LLC. 1.25 + 1.26 +*/ 1.27 + 1.28 +#include "ljpc.h" 1.29 +#include "ljreplay.h" 1.30 +#include <jpgalleg.h> 1.31 +#include <time.h> 1.32 +#include "scenario.h" 1.33 +#include "ljpath.h" 1.34 + 1.35 +#if 1 1.36 + #define LJ_VERSION "0.46a ("__DATE__")" 1.37 +#else 1.38 + #define LJ_VERSION "WIP ("__DATE__")" 1.39 +#endif 1.40 + 1.41 +#define USE_PICS_FOLDER 0 1.42 +#define MAX_PLAYERS 2 1.43 +#define LJ_TICK_RATE 3600 // frames per minute 1.44 + 1.45 +int bgColor, fgColor = 0, hiliteColor, pfBgColor = 0, pfFgColor; 1.46 +static volatile int curTime = 0; 1.47 +volatile char redrawWholeScreen = 1; 1.48 +static volatile int wantPause = 0; 1.49 +const DATAFILE *dat = NULL, *sound_dat = NULL; 1.50 +const FONT *aver32 = NULL, *aver16 = NULL; 1.51 +char withSound = 0; 1.52 +char autoPause; 1.53 +int refreshRate = 0; 1.54 +char demoFilename[512]; 1.55 + 1.56 +int withPics = -1; 1.57 + 1.58 +char skinName[PATH_MAX] = "default.skin"; 1.59 +char ljblocksSRSName[PATH_MAX]; 1.60 +char ljblocksSegaName[PATH_MAX]; 1.61 +char ljconnSRSName[PATH_MAX]; 1.62 +char ljconnSegaName[PATH_MAX]; 1.63 +char ljbgName[PATH_MAX]; 1.64 +char menubgName[PATH_MAX]; 1.65 +char bgmName[PATH_MAX]; 1.66 +char bgmRhythmName[PATH_MAX]; 1.67 +unsigned long int bgmLoopPoint = 0; 1.68 +int bgmReadyGo = 0; 1.69 +int bgmVolume = 128; 1.70 +int skinW = 800; 1.71 +int skinH = 600; 1.72 + 1.73 + 1.74 + 1.75 + 1.76 +static void incCurTime(void) { 1.77 + ++curTime; 1.78 +} END_OF_FUNCTION(incCurTime); 1.79 + 1.80 +static void amnesia(void) { 1.81 + redrawWholeScreen = 1; 1.82 +} END_OF_FUNCTION(amnesia); 1.83 + 1.84 +static void requestPause(void) { 1.85 + wantPause |= autoPause; 1.86 +} 1.87 + 1.88 + 1.89 +int getTime(void) { 1.90 + return curTime; 1.91 +} 1.92 + 1.93 +void yieldCPU(void) { 1.94 + rest(5); 1.95 +} 1.96 + 1.97 +void ljBeginDraw(LJView *v, int sync) { 1.98 + vsync(); 1.99 + if (redrawWholeScreen) { 1.100 + redrawWholeScreen = 0; 1.101 + playRedrawScreen(v); 1.102 + } 1.103 + 1.104 + BITMAP *sc = v->plat->skin->fullback; 1.105 + 1.106 + // Draw shape 1.107 + blit(v->plat->skin->bg, sc, 0, 0, 0, 0, 48, 16); 1.108 + if (v->control->replayDst) { 1.109 + 1.110 + // circle: record 1.111 + circlefill(sc, 8, 8, 7, fgColor); 1.112 + textout_ex(sc, aver16, "Rec", 18, 2, fgColor, -1); 1.113 + } else if (v->control->replaySrc) { 1.114 + 1.115 + // triangle: play 1.116 + for (int wid = 0; wid < 7; ++wid) { 1.117 + hline(sc, 2, 2 + wid, wid * 2 + 3, fgColor); 1.118 + hline(sc, 2, 14 - wid, wid * 2 + 3, fgColor); 1.119 + } 1.120 + textout_ex(sc, aver16, "Play", 18, 2, fgColor, -1); 1.121 + } else { 1.122 + 1.123 + // square: stop 1.124 + rectfill(sc, 2, 2, 14, 14, fgColor); 1.125 + } 1.126 + blit(sc, screen, 0, 0, 0, 0, 48, 16); 1.127 +} 1.128 + 1.129 +void ljEndDraw(LJView *v) { 1.130 + 1.131 +} 1.132 + 1.133 +static void startRecording(LJView *v) { 1.134 + withPics = -1; 1.135 + 1.136 + // close existing playback 1.137 + if (v->control->replaySrc) { 1.138 + replayClose(v->control->replaySrc); 1.139 + v->control->replaySrc = NULL; 1.140 + } 1.141 + 1.142 + // toggle recording 1.143 + if (v->control->replayDst) { 1.144 + replayClose(v->control->replayDst); 1.145 + v->control->replayDst = NULL; 1.146 + } else { 1.147 + char path[PATH_MAX]; 1.148 + 1.149 + ljpathFind_w(path, "demo.ljm"); 1.150 + v->control->replayDst = newReplay(path, v->field); 1.151 +#if USE_PICS_FOLDER 1.152 + withPics = 0; 1.153 +#endif 1.154 + } 1.155 +} 1.156 + 1.157 +static void startPlaying(LJView *v) { 1.158 + withPics = -1; 1.159 + 1.160 + // close existing recording 1.161 + if (v->control->replayDst) { 1.162 + replayClose(v->control->replayDst); 1.163 + v->control->replayDst = NULL; 1.164 + } 1.165 + 1.166 + // toggle playback 1.167 + if (v->control->replaySrc) { 1.168 + replayClose(v->control->replaySrc); 1.169 + v->control->replaySrc = NULL; 1.170 + } else { 1.171 + char path[PATH_MAX]; 1.172 + 1.173 + if (ljpathFind_r(path, "demo.ljm")) { 1.174 + v->control->replaySrc = openReplay(path, v->field); 1.175 + } 1.176 + v->nLockTimes = 0; 1.177 +#if USE_PICS_FOLDER 1.178 + withPics = 0; 1.179 +#endif 1.180 + } 1.181 + v->backDirty = ~0; 1.182 + v->field->reloaded = 1; 1.183 +} 1.184 + 1.185 +int ljHandleConsoleButtons(LJView *v) { 1.186 + int canceled = 0; 1.187 + 1.188 + while (keypressed()) { 1.189 + int scancode; 1.190 + int codepoint = ureadkey(&scancode); 1.191 + 1.192 + if (scancode == KEY_ESC) { 1.193 + wantPause = 1; 1.194 + } else if (scancode == KEY_PRTSCR) { 1.195 + saveScreen(-1); 1.196 + save_bitmap("ljbackbuf.bmp", v->plat->skin->fullback, NULL); 1.197 + } else if (codepoint == '[') { 1.198 + v->plat->wantRecord = 1; 1.199 + } else if (codepoint == ']') { 1.200 + v->plat->wantRecord = 0; 1.201 + startPlaying(v); 1.202 + } 1.203 + } 1.204 + if (v->plat->wantRecord) { 1.205 + v->plat->wantRecord = 0; 1.206 + startRecording(v); 1.207 + } 1.208 + if (wantPause) { 1.209 + canceled = pauseGame(v->plat); 1.210 + wantPause = 0; 1.211 + } 1.212 + 1.213 + if (wantsClose) { 1.214 + canceled = 1; 1.215 + } 1.216 + 1.217 + return canceled; 1.218 +} 1.219 + 1.220 + 1.221 +static void drawBlock(const LJPCView *const v, int x, int y, int blk) { 1.222 + 1.223 + if (x >= 0 && x < LJ_PF_WID && y >= 0 && y < LJ_PF_VIS_HT) { 1.224 + const int w = v->skin->blkW; 1.225 + const int h = v->skin->blkH; 1.226 + const int dstX = w * x; 1.227 + const int dstY = h * (LJ_PF_VIS_HT - 1 - y); 1.228 + 1.229 + if (v->skin->transparentPF && (blk == 0 || blk == 0x100)) { 1.230 + 1.231 + // Copy background 1.232 + const unsigned int srcX = dstX + v->baseX; 1.233 + const unsigned int srcY = dstY + v->skin->baseY 1.234 + - LJ_PF_VIS_HT * h - v->skin->pfElev; 1.235 + blit(v->skin->bg, v->back, srcX, srcY, dstX, dstY, w, h); 1.236 + } else if (blk && v->skin->connBlocks) { 1.237 + 1.238 + // Copy connected block 1.239 + const unsigned int srcX = ((blk >> 0) & 15) * w; 1.240 + const unsigned int srcY = ((blk >> 4) & 15) * h; 1.241 + blit(v->skin->connBlocks, v->back, srcX, srcY, dstX, dstY, w, h); 1.242 + } else { 1.243 + 1.244 + // Copy lone block 1.245 + const unsigned int srcX = ((blk >> 4) & 7) * w; 1.246 + const unsigned int srcY = ((blk >> 7) & 1) * h; 1.247 + blit(v->skin->blocks, v->back, srcX, srcY, dstX, dstY, w, h); 1.248 + } 1.249 + 1.250 + // 0x100: hotline 1.251 + if (blk & 0x100) { 1.252 + hline(v->back, dstX, dstY + h / 2 - 1, dstX + w - 1, pfFgColor); 1.253 + hline(v->back, dstX, dstY + h / 2 , dstX + w - 1, pfFgColor); 1.254 + hline(v->back, dstX, dstY + h / 2 + 1, dstX + w - 1, pfBgColor); 1.255 + } 1.256 + } 1.257 +} 1.258 + 1.259 +/** 1.260 + * Draws multiple rows of the playfield 1.261 + * @param p the playfield 1.262 + * @param rows the rows to be updated (0x00001 on bottom, 0x80000 on top) 1.263 + */ 1.264 +void updField(const LJView *const v, LJBits rows) { 1.265 + const LJField *const p = v->field; 1.266 + 1.267 + acquire_bitmap(v->plat->back); 1.268 + for (int y = 0; 1.269 + y < LJ_PF_VIS_HT && rows != 0; 1.270 + ++y, rows >>= 1) { 1.271 + int blankTile = 0; 1.272 + 1.273 + // hotline: draw 0x100 as the background 1.274 + if (hotlineRows[y] && v->field->scoreStyle == LJSCORE_HOTLINE) { 1.275 + blankTile = 0x100; 1.276 + } 1.277 + // during line clear delay, draw bars to show which lines were cleared 1.278 + if (p->state == LJS_LINES_FALLING && p->stateTime > 0 1.279 + && ((1 << y) & p->tempRows)) { 1.280 + blankTile = 0x100; 1.281 + } 1.282 + if (rows & 1) { 1.283 + for (int x = p->leftWall; x < p->rightWall; x++) { 1.284 + int b = v->hidePF ? 0 : p->b[y][x]; 1.285 + drawBlock(v->plat, x, y, b ? b : blankTile); 1.286 + } 1.287 + } 1.288 + } 1.289 + release_bitmap(v->plat->back); 1.290 +} 1.291 + 1.292 + 1.293 +#define SHADOW_BLOCK 0x00 1.294 + 1.295 +/** 1.296 + * Draws a tetromino whose lower left corner of the bounding box is at (x, y) 1.297 + * @param b the bitmap to draw to 1.298 + * @param piece the piece to be drawn 1.299 + * @param x distance from to left side of 4x4 box 1.300 + * @param y distance from top of bitmap to bottom of 4x4 box 1.301 + * @param the rotation state (0: U; 1: R; 2: D; 3: L; 4: Initial position) 1.302 + * @param w width of each block 1.303 + * @param h height of each block 1.304 + * @param color Drawing style 1.305 + * color == 0: draw shadow 1.306 + * color == 0x10 through 0x70: draw in that color 1.307 + * color == 0x80: draw as garbage 1.308 + * color == -255 through -1: draw with 255 through 1 percent lighting 1.309 + */ 1.310 +LJBits drawPiece(LJView *const v, BITMAP *const b, 1.311 + int piece, int x, int y, int theta, 1.312 + int color, int w, int h) { 1.313 + // Don't try to draw the -1 that's the sentinel for no hold piece 1.314 + if (piece < 0) 1.315 + return 0; 1.316 + 1.317 + LJBits rows = 0; 1.318 + LJBlkSpec blocks[4]; 1.319 + const int srcW = v->plat->skin->blkW; 1.320 + const int srcH = v->plat->skin->blkH; 1.321 + BITMAP *singleBlocks = v->plat->skin->blocks; 1.322 + int singleSeries = (color == 0 1.323 + && singleBlocks->h >= 6 * srcH) 1.324 + ? 4 : 2; 1.325 + 1.326 + // color: 0 for shadow, >0 for piece 1.327 + BITMAP *connBlocks = (color != SHADOW_BLOCK) 1.328 + ? v->plat->skin->connBlocks : NULL; 1.329 + 1.330 + BITMAP *transTemp = color < 0 1.331 + || (color == SHADOW_BLOCK && v->hideShadow < LJSHADOW_COLORED) 1.332 + ? create_bitmap(w, h) : 0; 1.333 + 1.334 + if (transTemp) { 1.335 + set_trans_blender(0, 0, 0, v->hideShadow ? 128 : 64); 1.336 + } 1.337 + 1.338 + expandPieceToBlocks(blocks, v->field, piece, 0, 0, theta); 1.339 + acquire_bitmap(b); 1.340 + for (int blk = 0; blk < 4; ++blk) { 1.341 + int blkValue = blocks[blk].conn; 1.342 + 1.343 + // If blkValue == 0 then the block is not part of the piece, 1.344 + // such as if it's a domino or tromino or (once pentominoes 1.345 + // are added) a tetromino. 1.346 + if (blkValue) { 1.347 + int blkX = blocks[blk].x; 1.348 + int blkY = blocks[blk].y; 1.349 + const int dstX = x + w * blkX; 1.350 + const int dstY = y + h * (-1 - blkY); 1.351 + 1.352 + if (color > 0) { 1.353 + blkValue = color | (blkValue & CONNECT_MASK); 1.354 + } else if (color == 0 && v->hideShadow == LJSHADOW_COLORLESS) { 1.355 + blkValue = 0; 1.356 + } 1.357 + 1.358 + if (connBlocks) { 1.359 + const unsigned int srcX = ((blkValue >> 0) & 15) * srcW; 1.360 + const unsigned int srcY = ((blkValue >> 4) & 15) * srcH; 1.361 + 1.362 + if (transTemp) { 1.363 + stretch_blit(connBlocks, transTemp, 1.364 + srcX, srcY, srcW, srcH, 1.365 + 0, 0, w, h); 1.366 + if (color < 0) { 1.367 + draw_lit_sprite(b, transTemp, dstX, dstY, color + 256); 1.368 + } else { 1.369 + draw_trans_sprite(b, transTemp, dstX, dstY); 1.370 + } 1.371 + } else if (srcW != w || srcH != h) { 1.372 + stretch_blit(connBlocks, b, 1.373 + srcX, srcY, srcW, srcH, 1.374 + dstX, dstY, w, h); 1.375 + if (dstY > 600) { 1.376 + allegro_message("dstY = %d\n", dstY); 1.377 + } 1.378 + } else { 1.379 + blit(connBlocks, b, 1.380 + srcX, srcY, 1.381 + dstX, dstY, w, h); 1.382 + } 1.383 + } else { 1.384 + const unsigned int srcX = ((blkValue >> 4) & 7) * srcW; 1.385 + const unsigned int srcY = (((blkValue >> 7) & 1) + singleSeries) * srcH; 1.386 + 1.387 + if (transTemp) { 1.388 + stretch_blit(singleBlocks, transTemp, 1.389 + srcX, srcY, srcW, srcH, 1.390 + 0, 0, w, h); 1.391 + if (color < 0) { 1.392 + draw_lit_sprite(b, transTemp, dstX, dstY, color + 256); 1.393 + } else { 1.394 + draw_trans_sprite(b, transTemp, dstX, dstY); 1.395 + } 1.396 + } else { 1.397 + stretch_blit(singleBlocks, b, 1.398 + srcX, srcY, srcW, srcH, 1.399 + dstX, dstY, w, h); 1.400 + } 1.401 + } 1.402 + rows |= 1 << blkY; 1.403 + } 1.404 + } 1.405 + release_bitmap(b); 1.406 + if (transTemp) { 1.407 + solid_mode(); 1.408 + destroy_bitmap(transTemp); 1.409 + } 1.410 + 1.411 + return rows; 1.412 +} 1.413 + 1.414 +void drawPieceInPF(LJView *v, int dstX, LJFixed dstY, int theta, int color) { 1.415 + LJBits bits = 0; 1.416 + BITMAP *b = v->plat->back; 1.417 + const LJField *const p = v->field; 1.418 + int y = ljfixfloor(dstY); 1.419 + const int w = v->plat->skin->blkW; 1.420 + const int h = v->plat->skin->blkH; 1.421 + int drawnY = fixmul(h, dstY); 1.422 + 1.423 + bits = drawPiece(v, b, p->curPiece[0], 1.424 + w * dstX, h * LJ_PF_VIS_HT - drawnY, 1.425 + theta, 1.426 + color, w, h); 1.427 + bits = (y >= 0) ? bits << y : bits >> -y; 1.428 + bits &= (1 << LJ_PF_VIS_HT) - 1; 1.429 + if (dstY & 0xFFFF) { 1.430 + bits |= bits << 1; 1.431 + } 1.432 + 1.433 + v->backDirty |= bits; 1.434 + v->frontDirty |= bits; 1.435 +} 1.436 + 1.437 + 1.438 +void drawFallingPiece(LJView *v) { 1.439 + const LJField *const p = v->field; 1.440 + int piece = p->curPiece[0]; 1.441 + int y = v->smoothGravity 1.442 + ? p->y 1.443 + : ljitofix(ljfixfloor(p->y)); 1.444 + const int color = (p->state == LJS_LANDED) 1.445 + ? -128 - ((p->stateTime + 1) * 128 / (p->speed.lockDelay + 1)) 1.446 + : pieceColors[piece]; 1.447 + 1.448 + // Draw trails 1.449 + if (v->showTrails) { 1.450 + 1.451 + // trail going up 1.452 + while (v->trailY - y < ljitofix(-1)) { 1.453 + v->trailY += ljitofix(1); 1.454 + drawPieceInPF(v, p->x, v->trailY, p->theta, color); 1.455 + } 1.456 + 1.457 + // trail going down 1.458 + while (v->trailY - y > ljitofix(1)) { 1.459 + v->trailY -= ljitofix(1); 1.460 + drawPieceInPF(v, p->x, v->trailY, p->theta, color); 1.461 + } 1.462 + } 1.463 + drawPieceInPF(v, p->x, y, p->theta, color); 1.464 + v->trailY = y; 1.465 +} 1.466 + 1.467 +void drawShadow(LJView *v) { 1.468 + const LJField *const p = v->field; 1.469 + int y = ljitofix(p->hardDropY); 1.470 + const int color = SHADOW_BLOCK; 1.471 + drawPieceInPF(v, p->x, y, p->theta, color); 1.472 +} 1.473 + 1.474 +void drawNextPieces(LJView *v) { 1.475 + int baseX = v->plat->baseX; 1.476 + int baseY = v->plat->skin->baseY - v->plat->skin->pfElev; 1.477 + int blkW = v->plat->skin->blkW; 1.478 + int blkH = v->plat->skin->blkH; 1.479 + int holdPieceColor = v->field->alreadyHeld 1.480 + ? 0x80 1.481 + : pieceColors[v->field->holdPiece]; 1.482 + int ceil = v->field->ceiling; 1.483 + 1.484 + BITMAP *sc = v->plat->skin->fullback; 1.485 + 1.486 + if (v->frontDirty & LJ_DIRTY_NEXT) { 1.487 + int holdW = blkW * 2 / 3; 1.488 + int holdH = blkH * 2 / 3; 1.489 + int holdX = baseX + blkW; 1.490 + int holdY = baseY - (ceil - 1) * blkH - 4 * holdH; 1.491 + 1.492 + // Move the hold piece within the screen 1.493 + if (holdY < 0) { 1.494 + holdY = 0; 1.495 + } 1.496 + 1.497 + // Draw hold piece 1.498 + blit(v->plat->skin->bg, sc, 1.499 + holdX, holdY, 1.500 + holdX, holdY, 1.501 + holdW * 4, holdH * 2); 1.502 + drawPiece(v, sc, 1.503 + v->field->holdPiece, holdX, holdY + 4 * holdH, 4, 1.504 + holdPieceColor, holdW, holdH); 1.505 + blit(sc, screen, 1.506 + holdX, holdY, 1.507 + holdX, holdY, 1.508 + holdW * 4, holdH * 2); 1.509 + } 1.510 + // Draw next pieces 1.511 + 1.512 + switch (v->plat->skin->nextPos) { 1.513 + case LJNEXT_RIGHT: 1.514 + case LJNEXT_RIGHT_TAPER: 1.515 + if (v->frontDirty & LJ_DIRTY_NEXT) { 1.516 + int y = baseY - ceil * blkH; 1.517 + int x = baseX + LJ_PF_WID * blkW; 1.518 + int w = blkW; 1.519 + int h = blkH; 1.520 + for (int i = 1; i <= v->nextPieces; ++i) { 1.521 + int piece = v->field->curPiece[i]; 1.522 + 1.523 + blit(v->plat->skin->bg, sc, x, y, x, y, w * 4, h * 2); 1.524 + if (!v->hideNext) { 1.525 + drawPiece(v, sc, 1.526 + piece, x, y + 4 * h, 4, 1.527 + pieceColors[piece], w, h); 1.528 + } 1.529 + blit(sc, screen, x, y, x, y, w * 4, h * 2); 1.530 + y += 8 + h * 2; 1.531 + if (v->plat->skin->nextPos == LJNEXT_RIGHT_TAPER) { 1.532 + --h; 1.533 + --w; 1.534 + } 1.535 + } 1.536 + } 1.537 + break; 1.538 + 1.539 + case LJNEXT_TOP: 1.540 + if (v->frontDirty & LJ_DIRTY_NEXT) { 1.541 + int y = baseY - (ceil + 2) * blkH - 8; 1.542 + int x = baseX + 4 * blkW; 1.543 + int blitX = x, blitY = y; 1.544 + 1.545 + blit(v->plat->skin->bg, sc, x, y, x, y, blkW * 8, blkH * 2); 1.546 + if (!v->hideNext) { 1.547 + if (v->nextPieces >= 1) { 1.548 + int piece = v->field->curPiece[1]; 1.549 + drawPiece(v, sc, 1.550 + piece, x, y + 4 * blkH, 4, 1.551 + pieceColors[piece], blkW, blkH); 1.552 + } 1.553 + if (v->nextPieces >= 2) { 1.554 + int piece = v->field->curPiece[2]; 1.555 + x += 4 * blkW; 1.556 + drawPiece(v, sc, 1.557 + piece, x, y + 3 * blkH, 4, 1.558 + pieceColors[piece], blkW/ 2, blkH / 2); 1.559 + } 1.560 + if (v->nextPieces >= 3) { 1.561 + int piece = v->field->curPiece[3]; 1.562 + x += 2 * blkW; 1.563 + drawPiece(v, sc, 1.564 + piece, x, y + 3 * blkH, 4, 1.565 + pieceColors[piece], blkW / 2, blkH / 2); 1.566 + } 1.567 + } 1.568 + blit(sc, screen, blitX, blitY, blitX, blitY, blkW * 8, blkH * 2); 1.569 + } 1.570 + break; 1.571 + } 1.572 + 1.573 + if (v->plat->nextAbove && !v->hideNext) { 1.574 + int row = (v->field->hardDropY + 4); 1.575 + int x = (1 + v->field->x) * blkW; 1.576 + for (int i = 1; 1.577 + i <= v->plat->nextAbove 1.578 + && row <= v->field->ceiling - 2; 1.579 + ++i) { 1.580 + int y = (LJ_PF_VIS_HT - row) * blkH; 1.581 + int piece = v->field->curPiece[i]; 1.582 + 1.583 + drawPiece(v, v->plat->back, 1.584 + piece, x, y, 4, 1.585 + pieceColors[piece], blkW / 2, blkH / 2); 1.586 + v->backDirty |= 3 << row; 1.587 + row += 2; 1.588 + } 1.589 + } 1.590 + v->frontDirty &= ~LJ_DIRTY_NEXT; 1.591 +} 1.592 + 1.593 +void drawScore(LJView *v) { 1.594 + int gameTime = v->field->gameTime; 1.595 + int seconds = gameTime / 60; 1.596 + int minutes = seconds / 60; 1.597 + int baseX = v->plat->baseX; 1.598 + int tpm = -1; 1.599 + int spawnLeft = v->plat->skin->blkW * LJ_SPAWN_X + baseX; 1.600 + int pfRight = v->plat->skin->blkW * LJ_PF_WID + baseX; 1.601 + BITMAP *sc = v->plat->skin->fullback; 1.602 + 1.603 + if (withPics >= 0) { 1.604 + if (v->field->nPieces != withPics) { 1.605 + saveScreen(v->field->nPieces); 1.606 + } 1.607 + withPics = v->field->nPieces; 1.608 + } 1.609 + 1.610 + if (v->nLockTimes >= 2 ) { 1.611 + int time = v->lockTime[0] - v->lockTime[v->nLockTimes - 1]; 1.612 + if (time > 0) { 1.613 + tpm = 3600 * (v->nLockTimes - 1) / time; 1.614 + } 1.615 + } 1.616 + 1.617 + if (v->frontDirty & LJ_DIRTY_SCORE) { 1.618 + switch (v->plat->skin->nextPos) { 1.619 + case LJNEXT_TOP: 1.620 + blit(v->plat->skin->bg, sc, 1.621 + pfRight, 72, 1.622 + pfRight, 72, 1.623 + 112, 136); 1.624 + 1.625 + textout_ex(sc, aver32, "Lines:", pfRight, 72, fgColor, -1); 1.626 + textprintf_right_ex(sc, aver32, pfRight + 104, 102, fgColor, -1, 1.627 + "%d", v->field->lines); 1.628 + textout_ex(sc, aver32, "Score:", pfRight, 142, fgColor, -1); 1.629 + textprintf_right_ex(sc, aver32, pfRight + 104, 172, fgColor, -1, 1.630 + "%d", v->field->score); 1.631 + textout_ex(sc, aver32, "Level:", pfRight, 212, fgColor, -1); 1.632 + textprintf_right_ex(sc, aver32, pfRight + 104, 242, fgColor, -1, 1.633 + "%d", v->field->speedState.level); 1.634 + blit(sc, screen, 1.635 + pfRight, 72, 1.636 + pfRight, 72, 1.637 + 112, 136); 1.638 + break; 1.639 + 1.640 + default: 1.641 + blit(v->plat->skin->bg, sc, spawnLeft, 12, spawnLeft, 12, 288, 30); 1.642 + blit(v->plat->skin->bg, sc, 1.643 + spawnLeft, 42, spawnLeft, 42, 1.644 + pfRight - spawnLeft, 30); 1.645 + textprintf_right_ex(sc, aver32, spawnLeft + 288, 12, fgColor, -1, 1.646 + "Lv. %d", v->field->speedState.level); 1.647 + textprintf_ex(sc, aver32, spawnLeft, 12, fgColor, -1, 1.648 + "Lines: %d", v->field->lines); 1.649 + textprintf_ex(sc, aver32, spawnLeft, 42, fgColor, -1, 1.650 + "Score: %d", v->field->score); 1.651 + blit(sc, screen, spawnLeft, 12, spawnLeft, 12, 288, 60); 1.652 + break; 1.653 + } 1.654 + } 1.655 + 1.656 + /* If speed is defined, and there is room to draw it, draw it. */ 1.657 + if (tpm > 0 && v->nextPieces <= 3) { 1.658 + blit(v->plat->skin->bg, sc, 1.659 + pfRight, 282, 1.660 + pfRight, 282, 1.661 + 104, 60); 1.662 + textout_ex(sc, aver32, "Speed:", pfRight, 282, fgColor, -1); 1.663 + textprintf_right_ex(sc, aver32, pfRight + 104, 312, fgColor, -1, 1.664 + "%d", tpm); 1.665 + blit(sc, screen, 1.666 + pfRight, 282, 1.667 + pfRight, 282, 1.668 + 104, 60); 1.669 + } 1.670 + 1.671 + if (v->frontDirty & LJ_DIRTY_NEXT) { 1.672 + 1.673 + // Erase gimmick 1.674 + blit(v->plat->skin->bg, sc, 1.675 + baseX, v->plat->skin->baseY + 8, 1.676 + baseX, v->plat->skin->baseY + 8, 1.677 + v->plat->skin->blkW * LJ_PF_WID, 30); 1.678 + // Draw gimmick 1.679 + if (v->field->gimmick >= 0 && v->field->gimmick < LJGM_N_GIMMICKS) { 1.680 + const char *gimmickName = ljGetFourCCName(gimmickNames[v->field->gimmick]); 1.681 + textout_centre_ex(sc, aver32, 1.682 + gimmickName ? gimmickName : "Bad gimmick ID", 1.683 + baseX + (LJ_PF_WID / 2) * v->plat->skin->blkW, 1.684 + v->plat->skin->baseY + 8, 1.685 + fgColor, -1); 1.686 + blit(sc, screen, 1.687 + baseX, v->plat->skin->baseY + 8, 1.688 + baseX, v->plat->skin->baseY + 8, 1.689 + v->plat->skin->blkW * LJ_PF_WID, 30); 1.690 + } 1.691 + } 1.692 + drawNextPieces(v); 1.693 + 1.694 + blit(v->plat->skin->bg, sc, pfRight, 42, pfRight, 42, 96, 30); 1.695 +#if 0 1.696 + // Use this for DEBUG inspection into a variable 1.697 + textprintf_right_ex(sc, aver16, pfRight + 96, 8, fgColor, -1, 1.698 + "%d", v->field->bpmCounter); 1.699 +#endif 1.700 + textprintf_right_ex(sc, aver32, pfRight + 96, 42, fgColor, -1, 1.701 + "%d:%02d", minutes, seconds - 60 * minutes); 1.702 + blit(sc, screen, pfRight, 42, pfRight, 42, 96, 30); 1.703 + 1.704 +} 1.705 + 1.706 +void blitField(LJView *v) { 1.707 + int blkH = v->plat->skin->blkH; 1.708 + int rowY = v->plat->skin->baseY 1.709 + - v->plat->skin->pfElev 1.710 + - blkH * v->field->ceiling; 1.711 + int x = v->plat->skin->blkW * v->field->leftWall; 1.712 + int w = v->plat->skin->blkW * (v->field->rightWall - v->field->leftWall); 1.713 + 1.714 + // Copy each dirty row 1.715 + for (int y = v->field->ceiling - 1; y >= 0; --y) { 1.716 + if (v->frontDirty & (1 << y)) { 1.717 + int top = (LJ_PF_VIS_HT - y - 1) * blkH; 1.718 + int ht = 0; 1.719 + 1.720 + // Find the height of the contiguous rows to blit 1.721 + do { 1.722 + ht += blkH; 1.723 + --y; 1.724 + } while ((y >= 0) 1.725 + && (v->frontDirty & (1 << y))); 1.726 + blit(v->plat->back, screen, 1.727 + x, top, 1.728 + x + v->plat->baseX, rowY, 1.729 + w, ht); 1.730 + rowY += ht; 1.731 + } 1.732 + rowY += blkH; 1.733 + } 1.734 + 1.735 + v->frontDirty &= (~0) << LJ_PF_HT; 1.736 +} 1.737 + 1.738 +void saveScreen(int n) { 1.739 + BITMAP *b = create_bitmap(SCREEN_W, SCREEN_H); 1.740 + if (b) { 1.741 + PALETTE pal; 1.742 + 1.743 + get_palette(pal); 1.744 + blit(screen, b, 0, 0, 0, 0, SCREEN_W, SCREEN_H); 1.745 + if (n < 0) { 1.746 + save_bitmap("ljsnap.bmp", b, pal); 1.747 + } else { 1.748 + char filename[64]; 1.749 + sprintf(filename, "pics/lj%05d.bmp", n); 1.750 + save_bitmap(filename, b, pal); 1.751 + } 1.752 + destroy_bitmap(b); 1.753 + } 1.754 +} 1.755 + 1.756 +#define PRESETS_PER_COL 6 1.757 +#define N_NONPRESETS 2 1.758 +#define PRESETS_TOP 140 1.759 +#define PRESET_COL_WIDTH 250 1.760 +#define PRESET_ROW_HT 40 1.761 + 1.762 +static const char *const nonPresetNames[N_NONPRESETS] = { 1.763 + "Full Custom", "Back" 1.764 +}; 1.765 + 1.766 +static void getPresetDrawRow(unsigned int preset, int hilite) { 1.767 + unsigned int ht = text_height(aver32); 1.768 + const char *txt = preset < nLoadedPresets 1.769 + ? loadedPresets[preset].name 1.770 + : nonPresetNames[preset - nLoadedPresets]; 1.771 + if (!txt) { 1.772 + txt = "Bad"; 1.773 + } 1.774 + unsigned int wid = text_length(aver32, txt); 1.775 + 1.776 + int buttonCol = preset / PRESETS_PER_COL; 1.777 + int buttonX = 20 + buttonCol * PRESET_COL_WIDTH; 1.778 + int buttonY = PRESETS_TOP 1.779 + + PRESET_ROW_HT * (preset - buttonCol * PRESETS_PER_COL); 1.780 + unsigned int buttonWidth = wid + 16; 1.781 + 1.782 + rectfill(screen, 1.783 + buttonX, buttonY, 1.784 + buttonX + buttonWidth - 1, buttonY + PRESET_ROW_HT - 1, 1.785 + hilite ? hiliteColor : bgColor); 1.786 + if (hilite) { 1.787 + rect(screen, 1.788 + buttonX, buttonY, 1.789 + buttonX + buttonWidth - 1, buttonY + PRESET_ROW_HT - 1, 1.790 + fgColor); 1.791 + } 1.792 + textout_ex(screen, aver32, txt, 1.793 + 8 + buttonX, 1.794 + 20 + buttonY - ht / 2, 1.795 + fgColor, -1); 1.796 +} 1.797 + 1.798 +int getPreset(int lastPreset) { 1.799 + LJBits lastKeys = ~0; 1.800 + redrawWholeScreen = 1; 1.801 + 1.802 + clear_keybuf(); 1.803 + if (lastPreset < 0) { 1.804 + lastPreset += nLoadedPresets + N_NONPRESETS; 1.805 + } 1.806 + 1.807 + for(int done = 0; done == 0; ) { 1.808 + if (redrawWholeScreen) { 1.809 + redrawWholeScreen = 0; 1.810 + clear_to_color(screen, bgColor); 1.811 + textout_ex(screen, aver32, "LOCKJAW > Play", 16, 32, fgColor, -1); 1.812 + textout_ex(screen, aver32, "Select a scenario:", 16, 90, fgColor, -1); 1.813 + 1.814 + for (int preset = 0; 1.815 + preset < nLoadedPresets + N_NONPRESETS; 1.816 + ++preset) { 1.817 + getPresetDrawRow(preset, preset == lastPreset); 1.818 + } 1.819 + textout_ex(screen, aver16, "Arrows: move; Rotate Right: start", 1.820 + 16, PRESETS_TOP + 40 * (PRESETS_PER_COL + 1), fgColor, -1); 1.821 + textout_ex(screen, aver16, "Coming soon: an editor for these!", 1.822 + 16, PRESETS_TOP + 40 * (PRESETS_PER_COL + 1) + 20, fgColor, -1); 1.823 + } 1.824 + while (keypressed()) { 1.825 + int scancode; 1.826 + ureadkey(&scancode); 1.827 + if (scancode == KEY_PRTSCR) { 1.828 + saveScreen(-1); 1.829 + } 1.830 + } 1.831 + 1.832 + int preset = lastPreset; 1.833 + LJBits keys = menuReadPad(); 1.834 + LJBits newKeys = keys & ~lastKeys; 1.835 + 1.836 + if ((newKeys & VKEY_ROTL) || wantsClose) { 1.837 + preset = nLoadedPresets + N_NONPRESETS - 1; 1.838 + ezPlaySample("rotate_wav", 128); 1.839 + } 1.840 + if ((newKeys & VKEY_ROTR) || wantsClose) { 1.841 + done = 1; 1.842 + ezPlaySample("line_wav", 128); 1.843 + } 1.844 + 1.845 + if (newKeys & VKEY_UP) { 1.846 + if (preset <= 0) { 1.847 + preset = nLoadedPresets + N_NONPRESETS - 1; 1.848 + } else { 1.849 + --preset; 1.850 + } 1.851 + ezPlaySample("shift_wav", 128); 1.852 + } 1.853 + if (newKeys & VKEY_DOWN) { 1.854 + ++preset; 1.855 + if (preset >= nLoadedPresets + N_NONPRESETS) { 1.856 + preset = 0; 1.857 + } 1.858 + ezPlaySample("shift_wav", 128); 1.859 + } 1.860 + 1.861 + if (newKeys & VKEY_LEFT) { 1.862 + if (preset < PRESETS_PER_COL) { 1.863 + preset += (((nLoadedPresets + N_NONPRESETS) 1.864 + / PRESETS_PER_COL) 1.865 + ) 1.866 + * PRESETS_PER_COL; 1.867 + if (preset >= nLoadedPresets + N_NONPRESETS) { 1.868 + preset -= PRESETS_PER_COL; 1.869 + } 1.870 + } else { 1.871 + preset -= PRESETS_PER_COL; 1.872 + } 1.873 + ezPlaySample("shift_wav", 128); 1.874 + } 1.875 + if (newKeys & VKEY_RIGHT) { 1.876 + preset += PRESETS_PER_COL; 1.877 + if (preset >= nLoadedPresets + N_NONPRESETS) { 1.878 + preset %= PRESETS_PER_COL; 1.879 + } 1.880 + ezPlaySample("shift_wav", 128); 1.881 + } 1.882 + 1.883 + if (preset != lastPreset) { 1.884 + vsync(); 1.885 + getPresetDrawRow(lastPreset, 0); 1.886 + getPresetDrawRow(preset, 1); 1.887 + lastPreset = preset; 1.888 + } else { 1.889 + rest(30); 1.890 + } 1.891 + lastKeys = keys; 1.892 + } 1.893 + 1.894 + // Wrap the nonpresets into the negative numbers. 1.895 + if (lastPreset >= nLoadedPresets) { 1.896 + lastPreset -= (nLoadedPresets + N_NONPRESETS); 1.897 + } 1.898 + 1.899 + return lastPreset; 1.900 +} 1.901 + 1.902 +/** 1.903 + * Pauses the game, returning nonzero if the player wants to quit. 1.904 + */ 1.905 +int pauseGame(LJPCView *v) { 1.906 + int escCount = 0; 1.907 + int quit = 0; 1.908 + int escHold = curTime; 1.909 + redrawWholeScreen = 1; 1.910 + 1.911 + LJMusic_pause(v->skin->bgm, 1); 1.912 + while (escCount < 2 && !quit) { 1.913 + LJMusic_poll(v->skin->bgm); 1.914 + if (redrawWholeScreen) { 1.915 + redrawWholeScreen = 0; 1.916 + clear_to_color(screen, pfBgColor); 1.917 + textout_centre_ex(screen, aver32, "LOCKJAW: GAME PAUSED", 1.918 + SCREEN_W / 2, 200, pfFgColor, -1); 1.919 + textout_centre_ex(screen, aver32, "Press Esc to continue", 1.920 + SCREEN_W / 2, 250, pfFgColor, -1); 1.921 + textout_centre_ex(screen, aver32, "or hold Esc to quit", 1.922 + SCREEN_W / 2, 300, pfFgColor, -1); 1.923 + } 1.924 + 1.925 + if (key[KEY_ESC]) { 1.926 + if (escHold == 0) { 1.927 + escHold = curTime; 1.928 + } 1.929 + if (curTime - escHold >= 60) { 1.930 + quit = 1; 1.931 + } 1.932 + } else { 1.933 + if (escHold) { 1.934 + ++escCount; 1.935 + } 1.936 + escHold = 0; 1.937 + } 1.938 + rest(30); 1.939 + if (wantsClose) { 1.940 + quit = 1; 1.941 + } 1.942 + 1.943 + } 1.944 + LJMusic_pause(v->skin->bgm, 0); 1.945 + redrawWholeScreen = 1; 1.946 + clear_keybuf(); 1.947 + return quit; 1.948 +} 1.949 + 1.950 + 1.951 +void pcInit(LJView *v, struct LJPrefs *prefs) { 1.952 + v->plat->b2bcd1 = 0; 1.953 + v->plat->b2bcd2 = 0; 1.954 + v->plat->baseX = v->plat->skin->baseX; 1.955 + 1.956 + // If the player has chosen to use more next pieces than the 1.957 + // next piece position can handle, set the number of 1.958 + // next pieces for correctness of debrief(). 1.959 + if (v->nextPieces > 3 && v->plat->skin->nextPos == LJNEXT_TOP) { 1.960 + v->nextPieces = 3; 1.961 + } 1.962 +} 1.963 + 1.964 +/** 1.965 + * Redraws everything on the screen. 1.966 + * Called when needs redraw. 1.967 + */ 1.968 +void playRedrawScreen(LJView *v) { 1.969 + blit(v->plat->skin->bg, screen, 1.970 + 0, 0, 1.971 + 0, 0, 1.972 + SCREEN_W, SCREEN_H); 1.973 + v->frontDirty = ~0; 1.974 +} 1.975 + 1.976 +#if 0 1.977 +void startingAniWantsSkip(LJView *v) { 1.978 + LJInput unusedIn; 1.979 + addKeysToInput(&unusedIn, readPad(), v->field, v->control); 1.980 +} 1.981 +#endif 1.982 + 1.983 +void playSampleForTetromino(int piece); 1.984 + 1.985 +void restPollingMusic(int nFrames, LJMusic *bgm) { 1.986 + nFrames += curTime; 1.987 + while (curTime - nFrames < 0) { 1.988 + if (bgm) { 1.989 + LJMusic_poll(bgm); 1.990 + } 1.991 + } 1.992 +} 1.993 + 1.994 +extern int bgmReadyGo; 1.995 +void startingAnimation(LJView *v) { 1.996 + int readyGoX = v->plat->baseX + v->plat->skin->blkW * LJ_PF_WID / 2; 1.997 + int readyGoY = v->plat->skin->baseY 1.998 + - v->plat->skin->pfElev 1.999 + - v->plat->skin->blkH 1.1000 + * v->field->ceiling * 3 / 5; 1.1001 + 1.1002 + clear_keybuf(); 1.1003 + v->backDirty = 0; 1.1004 + 1.1005 + playRedrawScreen(v); 1.1006 + blitField(v); 1.1007 + textout_centre_ex(screen, aver32, "Ready", 1.1008 + readyGoX, readyGoY, pfFgColor, -1); 1.1009 + 1.1010 + ezPlaySample("ready_wav", 128); 1.1011 + restPollingMusic(36, bgmReadyGo ? v->plat->skin->bgm : NULL); 1.1012 + v->frontDirty = ~0; 1.1013 + 1.1014 + if (!wantsClose) { 1.1015 + blitField(v); 1.1016 + textout_centre_ex(screen, aver32, "GO!", 1.1017 + readyGoX, readyGoY, pfFgColor, -1); 1.1018 + drawScore(v); 1.1019 + 1.1020 + ezPlaySample("go_wav", 128); 1.1021 + v->frontDirty = ~0; 1.1022 + restPollingMusic(12, bgmReadyGo ? v->plat->skin->bgm : NULL); 1.1023 + } 1.1024 + if (!wantsClose) { 1.1025 + playSampleForTetromino(v->field->curPiece[1]); 1.1026 + restPollingMusic(24, bgmReadyGo ? v->plat->skin->bgm : NULL); 1.1027 + } 1.1028 +} 1.1029 + 1.1030 + 1.1031 +static void gameOverAnimation(const LJPCView *const v, const LJField *p, int won) { 1.1032 + int ceiling = p->ceiling; 1.1033 + int left = v->baseX + p->leftWall * v->skin->blkW; 1.1034 + int right = v->baseX + p->rightWall * v->skin->blkW - 1; 1.1035 + 1.1036 + ezPlaySample("sectionup_wav", 0); 1.1037 + if (!won) { 1.1038 + ezPlaySample("gameover_wav", 256); 1.1039 + } else { 1.1040 + ezPlaySample("win_wav", 256); 1.1041 + } 1.1042 + 1.1043 + for (int t = ceiling + v->skin->blkH - 2; t >= 0; --t) { 1.1044 + 1.1045 + // FIXME: vsync doesn't work on Vista 1.1046 + vsync(); 1.1047 + for (int row = ceiling - 1; row >= 0; --row) { 1.1048 + int ysub = t - row; 1.1049 + 1.1050 + if (ysub >= 0 && ysub < v->skin->blkH) { 1.1051 + int y = v->skin->baseY - v->skin->pfElev 1.1052 + - row * v->skin->blkH - ysub - 1; 1.1053 + hline(screen, left, y, right, pfBgColor); 1.1054 + } 1.1055 + } 1.1056 + if (wantsClose) { 1.1057 + t = 0; 1.1058 + } 1.1059 + } 1.1060 +} 1.1061 + 1.1062 +#define MENU_COPR_NOTICE_LINES 4 1.1063 +const char *const menuCoprNotice[MENU_COPR_NOTICE_LINES] = { 1.1064 + "Copr. 2006-2008 Damian Yerrick", 1.1065 + "Not sponsored or endorsed by The Tetris Company.", 1.1066 + "LOCKJAW comes with ABSOLUTELY NO WARRANTY. This is free software, and you are", 1.1067 + "welcome to redistribute it under certain conditions as described in GPL.txt." 1.1068 +}; 1.1069 + 1.1070 +static BITMAP *buildTitleScreen(void) { 1.1071 + BITMAP *back = create_system_bitmap(SCREEN_W, SCREEN_H); 1.1072 + if (!back) { 1.1073 + return NULL; 1.1074 + } 1.1075 + 1.1076 + // Gradient from (0, 0, 0) to (0, 0, 153) 1.1077 + for (int y = 0; y < 192; ++y) { 1.1078 + for (int x = -((y * 13) & 0x1F); 1.1079 + x < SCREEN_W; 1.1080 + x += 32) { 1.1081 + int colValue = y + ((rand() & 0x7000) >> 12); 1.1082 + int c = makecol(0, 0, 153 * colValue / 192); 1.1083 + hline(back, x, y, x + 31, c); 1.1084 + } 1.1085 + } 1.1086 + 1.1087 + // Gradient from (102, 51, 0) to (204, 102, 0) 1.1088 + for (int y = 192; y < 384; ++y) { 1.1089 + for (int x = -((y * 13) & 0x1F); 1.1090 + x < SCREEN_W; 1.1091 + x += 32) { 1.1092 + int colValue = y + ((rand() & 0x7800) >> 11); 1.1093 + int c = makecol(102 * colValue / 192, 51 * colValue / 192, 0); 1.1094 + hline(back, x, y, x + 31, c); 1.1095 + } 1.1096 + } 1.1097 + 1.1098 + // Gradient from (204, 102, 0) to (255, 128, 0) 1.1099 + for (int y = 384; y < SCREEN_H; ++y) { 1.1100 + for (int x = -((y * 13) & 0x1F); 1.1101 + x < SCREEN_W; 1.1102 + x += 32) { 1.1103 + int colValue = y - 400 + ((rand() & 0x7C00) >> 10); 1.1104 + if (colValue > 600 - 384) { 1.1105 + colValue = 600 - 384; 1.1106 + } 1.1107 + int c = makecol(204 + 50 * colValue / (600 - 384), 1.1108 + 102 + 25 * colValue / (600 - 384), 1.1109 + 0); 1.1110 + hline(back, x, y, x + 31, c); 1.1111 + } 1.1112 + } 1.1113 + 1.1114 + DATAFILE *obj = find_datafile_object(dat, "arttitle_bmp"); 1.1115 + BITMAP *logo = obj ? obj->dat : NULL; 1.1116 + obj = find_datafile_object(dat, "arttitle_pal"); 1.1117 + const RGB *pal = obj ? obj->dat : NULL; 1.1118 + 1.1119 + if (logo && pal) { 1.1120 + set_palette(pal); 1.1121 + draw_sprite(back, logo, 1.1122 + (SCREEN_W - logo->w) / 2, (384 - logo->h) / 2); 1.1123 + //unselect_palette(); 1.1124 + } 1.1125 + 1.1126 + textout_centre_ex(back, aver32, "Arrows: change; Enter: choose", 1.1127 + SCREEN_W / 2, 440, 1.1128 + 0, -1); 1.1129 + 1.1130 + textout_centre_ex(back, aver32, "LOCKJAW: The Reference "LJ_VERSION, 1.1131 + SCREEN_W / 2, SCREEN_H - 40, 1.1132 + 0, -1); 1.1133 + 1.1134 + return back; 1.1135 +} 1.1136 + 1.1137 +enum { 1.1138 + TITLE_EXIT = 0, 1.1139 + TITLE_PLAY, 1.1140 + TITLE_REPLAY, 1.1141 + TITLE_OPTIONS, 1.1142 + TITLE_SKIN, 1.1143 + TITLE_KEYS, 1.1144 + N_TITLE_ACTIONS 1.1145 +}; 1.1146 + 1.1147 +static const char *titleActions[N_TITLE_ACTIONS] = { 1.1148 + [TITLE_EXIT] = "Exit", 1.1149 + [TITLE_PLAY] = "Play", 1.1150 + [TITLE_REPLAY] = "Replay", 1.1151 + [TITLE_SKIN] = "Skin...", 1.1152 + [TITLE_OPTIONS] = "Options...", 1.1153 + [TITLE_KEYS] = "Game Keys..." 1.1154 +}; 1.1155 +/* 1.1156 + 0: Exit 1.1157 + 1: Play 1.1158 + 2 1.1159 +*/ 1.1160 +int title(void) { 1.1161 + 1.1162 + // don't even draw if the player is trying to close the window 1.1163 + if (wantsClose) { 1.1164 + return 0; 1.1165 + } 1.1166 + 1.1167 + BITMAP *back = buildTitleScreen(); 1.1168 + LJBits lastKeys = ~0; 1.1169 + int redraw = 1; 1.1170 + int choice = 1; 1.1171 + 1.1172 + if (!back) { 1.1173 + alert("Not enough memory to display the title screen.", 1.1174 + "(If you don't even have RAM for a title screen,", 1.1175 + "then what do you have RAM for?)", 1.1176 + "Exit", 0, 13, 0); 1.1177 + return 0; 1.1178 + } 1.1179 + 1.1180 + redrawWholeScreen = 1; 1.1181 + 1.1182 + for(int done = 0; done == 0; ) { 1.1183 + if (redrawWholeScreen) { 1.1184 + redrawWholeScreen = 0; 1.1185 + blit(back, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); 1.1186 + redraw = 1; 1.1187 + } 1.1188 + if (redraw) { 1.1189 + int dehilite = makecol(221, 153, 85); 1.1190 + int white = makecol(255, 255, 255); 1.1191 + redraw = 0; 1.1192 + vsync(); 1.1193 + blit(back, screen, 0, 400, 0, 400, SCREEN_W, 30); 1.1194 + textout_centre_ex(screen, aver32, 1.1195 + titleActions[choice], 1.1196 + SCREEN_W / 2, 400, white, -1); 1.1197 + textout_centre_ex(screen, aver32, 1.1198 + titleActions[choice == 0 ? N_TITLE_ACTIONS - 1 : choice - 1], 1.1199 + SCREEN_W / 2 - 160, 400, dehilite, -1); 1.1200 + textout_centre_ex(screen, aver32, 1.1201 + titleActions[choice == N_TITLE_ACTIONS - 1 ? 0 : choice + 1], 1.1202 + SCREEN_W / 2 + 160, 400, dehilite, -1); 1.1203 + } 1.1204 + 1.1205 + while (keypressed()) { 1.1206 + int scancode; 1.1207 + ureadkey(&scancode); 1.1208 + if (scancode == KEY_PRTSCR) { 1.1209 + saveScreen(-1); 1.1210 + } 1.1211 + } 1.1212 + 1.1213 + LJBits keys = menuReadPad(); 1.1214 + LJBits newKeys = keys & ~lastKeys; 1.1215 + 1.1216 + if (newKeys & VKEY_LEFT) { 1.1217 + --choice; 1.1218 + redraw = 1; 1.1219 + ezPlaySample("shift_wav", 128); 1.1220 + } 1.1221 + if (newKeys & VKEY_RIGHT) { 1.1222 + ++choice; 1.1223 + redraw = 1; 1.1224 + ezPlaySample("shift_wav", 128); 1.1225 + } 1.1226 + if (newKeys & VKEY_ROTL) { 1.1227 + choice = 0; 1.1228 + redraw = 1; 1.1229 + ezPlaySample("rotate_wav", 128); 1.1230 + } 1.1231 + if (newKeys & VKEY_ROTR) { 1.1232 + done = 1; 1.1233 + ezPlaySample("line_wav", 128); 1.1234 + } 1.1235 + if (choice < 0) { 1.1236 + choice += N_TITLE_ACTIONS; 1.1237 + } 1.1238 + if (choice >= N_TITLE_ACTIONS) { 1.1239 + choice -= N_TITLE_ACTIONS; 1.1240 + } 1.1241 + 1.1242 + lastKeys = keys; 1.1243 + 1.1244 + if (!redraw) { 1.1245 + rest(30); 1.1246 + } 1.1247 + if (wantsClose) { 1.1248 + done = 1; 1.1249 + choice = 0; 1.1250 + } 1.1251 + } 1.1252 + destroy_bitmap(back); 1.1253 + return choice; 1.1254 +} 1.1255 + 1.1256 + 1.1257 +void setupWindow(void) { 1.1258 + set_window_title("LOCKJAW"); 1.1259 + bgColor = makecol(255, 255, 255); 1.1260 + pfFgColor = bgColor; 1.1261 + hiliteColor = makecol(255, 255, 204); 1.1262 + refreshRate = get_refresh_rate(); 1.1263 +} 1.1264 + 1.1265 +void drawCoprNotice(void) { 1.1266 + clear_to_color(screen, pfBgColor); 1.1267 + for (int i = 0; i < MENU_COPR_NOTICE_LINES; ++i) { 1.1268 + textout_ex(screen, font, menuCoprNotice[i], 1.1269 + 16, 580 + 12 * (i - MENU_COPR_NOTICE_LINES), 1.1270 + pfFgColor, -1); 1.1271 + } 1.1272 +} 1.1273 + 1.1274 + 1.1275 +/** 1.1276 + * Destroys the back buffers for both playfields. 1.1277 + */ 1.1278 +static void destroyBackBuf(LJPCView *plat) { 1.1279 + for (int i = 0; i < MAX_PLAYERS; ++i) { 1.1280 + if (plat->back) { 1.1281 + destroy_bitmap(plat->back); 1.1282 + plat->back = NULL; 1.1283 + } 1.1284 + } 1.1285 + if (plat->skin->fullback) { 1.1286 + destroy_bitmap(plat->skin->fullback); 1.1287 + plat->skin->fullback = NULL; 1.1288 + } 1.1289 +} 1.1290 + 1.1291 +/** 1.1292 + * Creates the back buffers for both playfields. 1.1293 + */ 1.1294 +static int createBackBuf(LJView *v) { 1.1295 + v->plat->skin->fullback = create_system_bitmap(SCREEN_W, SCREEN_H); 1.1296 + if(!v->plat->skin->fullback) { 1.1297 + allegro_message("Could not create back buffer.\n"); 1.1298 + return 0; 1.1299 + } 1.1300 + 1.1301 + int blkW = v->plat->skin->blkW; 1.1302 + int blkH = v->plat->skin->blkH; 1.1303 + int y = v->plat->skin->baseY 1.1304 + - v->plat->skin->pfElev 1.1305 + - blkH * v->field->ceiling; 1.1306 + 1.1307 + for (int i = 0; i < MAX_PLAYERS; ++i) { 1.1308 + int x = v->plat->skin->baseX 1.1309 + + blkW * v[i].field->leftWall; 1.1310 + 1.1311 + v[i].plat->back = create_sub_bitmap(v->plat->skin->fullback, 1.1312 + x + SCREEN_W / 2 * i, 1.1313 + y, 1.1314 + v->plat->skin->blkW * LJ_PF_WID, 1.1315 + v->plat->skin->blkH * LJ_PF_VIS_HT); 1.1316 + if (!v[i].plat->back) { 1.1317 + destroyBackBuf(v->plat); 1.1318 + return 0; 1.1319 + } 1.1320 + } 1.1321 + return 1; 1.1322 +} 1.1323 + 1.1324 +/** 1.1325 + * Destroys all system bitmaps that a given view owns. 1.1326 + * Useful before changing screen mode. 1.1327 + */ 1.1328 +void destroySystemBitmaps(LJPCView *plat) { 1.1329 + destroyBackBuf(plat); 1.1330 + if (plat->skin->connBlocks) { 1.1331 + destroy_bitmap(plat->skin->connBlocks); 1.1332 + plat->skin->connBlocks = NULL; 1.1333 + } 1.1334 +} 1.1335 + 1.1336 +int openWindow(int windowed) 1.1337 +{ 1.1338 + int depth = desktop_color_depth(); 1.1339 + int card = windowed ? GFX_AUTODETECT_WINDOWED : GFX_AUTODETECT_FULLSCREEN; 1.1340 + 1.1341 + /* Reference implementation for Allegro is not compatible with 1.1342 + indexed color. */ 1.1343 + if (depth < 15) { 1.1344 + depth = 16; 1.1345 + } 1.1346 + 1.1347 + // Full screen procedure 1.1348 + set_color_depth(depth); 1.1349 + if (set_gfx_mode(card, skinW, skinH, 0, 0) == 0) { 1.1350 + setupWindow(); 1.1351 + return 0; 1.1352 + } 1.1353 + 1.1354 + // Windows can't tell 16 bit from 15 bit. If desktop color depth is reported as 16, try 15 too. 1.1355 + if (depth == 16) { 1.1356 + set_color_depth(15); 1.1357 + if (set_gfx_mode(card, skinW, skinH, 0, 0) == 0) { 1.1358 + setupWindow(); 1.1359 + return 0; 1.1360 + } 1.1361 + } 1.1362 + 1.1363 + return -1; 1.1364 +} 1.1365 + 1.1366 +BITMAP *loadConnections(const char *filename, int blkW, int blkH) { 1.1367 + BITMAP *src = load_bitmap(filename, NULL); 1.1368 + 1.1369 + if (!src) { 1.1370 + return NULL; 1.1371 + } 1.1372 + BITMAP *dst = create_system_bitmap(blkW*16, blkH*16); 1.1373 + if (!dst) { 1.1374 + destroy_bitmap(src); 1.1375 + return NULL; 1.1376 + } 1.1377 + acquire_bitmap(dst); 1.1378 + for (unsigned int col = 0; col < 16; ++col) { 1.1379 + unsigned int srcXBase = (col & 0x03) * blkW * 2; 1.1380 + unsigned int srcYBase = (col >> 2) * blkH * 2; 1.1381 + unsigned int dstYBase = col * blkH; 1.1382 + for (unsigned int conn = 0; conn < 16; ++conn) { 1.1383 + unsigned int dstXBase = conn * blkW; 1.1384 + unsigned int topSegY = (conn & CONNECT_U) ? blkH : 0; 1.1385 + unsigned int botSegY = (conn & CONNECT_D) ? blkH/2 : 3*blkH/2; 1.1386 + unsigned int leftSegX = (conn & CONNECT_L) ? blkW : 0; 1.1387 + unsigned int rightSegX = (conn & CONNECT_R) ? blkW/2 : 3*blkW/2; 1.1388 + blit(src, dst, 1.1389 + srcXBase + leftSegX, srcYBase + topSegY, 1.1390 + dstXBase + 0, dstYBase + 0, 1.1391 + blkW/2, blkH/2); 1.1392 + blit(src, dst, 1.1393 + srcXBase + rightSegX, srcYBase + topSegY, 1.1394 + dstXBase + blkW/2, dstYBase + 0, 1.1395 + blkW/2, blkH/2); 1.1396 + blit(src, dst, 1.1397 + srcXBase + leftSegX, srcYBase + botSegY, 1.1398 + dstXBase + 0, dstYBase + blkH/2, 1.1399 + blkW/2, blkH/2); 1.1400 + blit(src, dst, 1.1401 + srcXBase + rightSegX, srcYBase + botSegY, 1.1402 + dstXBase + blkW/2, dstYBase + blkH/2, 1.1403 + blkW/2, blkH/2); 1.1404 + } 1.1405 + } 1.1406 + release_bitmap(dst); 1.1407 + destroy_bitmap(src); 1.1408 + return dst; 1.1409 +} 1.1410 + 1.1411 +void closeWindow(void) { 1.1412 + set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); 1.1413 +} 1.1414 + 1.1415 +static void mainCleanup(LJPCView *v) { 1.1416 + destroyBackBuf(v); 1.1417 + if (v->skin->blocks) { 1.1418 + destroy_bitmap(v->skin->blocks); 1.1419 + } 1.1420 + if (v->skin->connBlocks) { 1.1421 + destroy_bitmap(v->skin->connBlocks); 1.1422 + } 1.1423 + LJMusic_delete(v->skin->bgm); 1.1424 + if (withSound) { 1.1425 + remove_sound(); 1.1426 + } 1.1427 + closeWindow(); 1.1428 +} 1.1429 + 1.1430 +/** 1.1431 + * Resets all skin settings to their initial values 1.1432 + * so that skins can override them. 1.1433 + */ 1.1434 +void loadSkinDefaults(LJPCSkin *s) { 1.1435 + ustrzcpy(ljblocksSRSName, sizeof(ljblocksSRSName) - 1, 1.1436 + "ljblocks.bmp"); 1.1437 + ustrzcpy(ljblocksSegaName, sizeof(ljblocksSegaName) - 1, 1.1438 + "ljblocks-sega.bmp"); 1.1439 + ustrzcpy(ljconnSRSName, sizeof(ljconnSRSName) - 1, 1.1440 + "ljconn.bmp"); 1.1441 + ustrzcpy(ljconnSegaName, sizeof(ljconnSegaName) - 1, 1.1442 + "ljconn-sega.bmp"); 1.1443 + ustrzcpy(ljbgName, sizeof(ljbgName) - 1, 1.1444 + "ljbg.jpg"); 1.1445 + ustrzcpy(menubgName, sizeof(ljbgName) - 1, 1.1446 + "menubg.jpg"); 1.1447 + ustrzcpy(bgmName, sizeof(bgmName) - 1, 1.1448 + "bgm.s3m"); 1.1449 + ustrzcpy(bgmRhythmName, sizeof(bgmRhythmName) - 1, 1.1450 + "bgm-rhythm.s3m"); 1.1451 + bgmLoopPoint = 0; 1.1452 + bgmReadyGo = 0; 1.1453 + bgmVolume = 128; 1.1454 + bgColor = makecol(255, 255, 255); 1.1455 + fgColor = makecol(0, 0, 0); 1.1456 + hiliteColor = makecol(255, 255, 204); 1.1457 + pfBgColor = makecol(0, 0, 0); 1.1458 + pfFgColor = makecol(255, 255, 255); 1.1459 + s->blkW = 24; 1.1460 + s->blkH = 24; 1.1461 + s->transparentPF = 0; 1.1462 + s->shiftScale = 0; 1.1463 + s->baseX = 0; 1.1464 + s->nextPos = 0; 1.1465 +} 1.1466 + 1.1467 +/** 1.1468 + * Converts a single hexadecimal digit to its value. 1.1469 + * @param in USASCII/Unicode value of hex digit character 1.1470 + * @return value 1.1471 +*/ 1.1472 +int hexDigitValue(int in) { 1.1473 + if (in >= '0' && in <= '9') { 1.1474 + return in - '0'; 1.1475 + } else if (in >= 'A' && in <= 'F') { 1.1476 + return in - 'A' + 10; 1.1477 + } else if (in >= 'a' && in <= 'f') { 1.1478 + return in - 'a' + 10; 1.1479 + } else { 1.1480 + return -1; 1.1481 + } 1.1482 +} 1.1483 + 1.1484 +int translateComponent(const char **in, size_t nDigits) { 1.1485 + const char *here = *in; 1.1486 + int hi = hexDigitValue(*here++); 1.1487 + int lo = nDigits > 3 ? hexDigitValue(*here++) : hi; 1.1488 + *in = here; 1.1489 + if (hi >= 0 && lo >= 0) { 1.1490 + return hi * 16 + lo; 1.1491 + } else { 1.1492 + return -1; 1.1493 + } 1.1494 +} 1.1495 + 1.1496 +/** 1.1497 + * Interprets a hexadecimal color specifier. 1.1498 + * @param in hexadecimal color specifier, in #ABC or #AABBCC format 1.1499 + * @return Allegro device-dependent color, in makecol() format 1.1500 + */ 1.1501 +int translateColor(const char *in) { 1.1502 + size_t nDigits = 0; 1.1503 + 1.1504 + // Verify and skip # 1.1505 + if (*in != '#') { 1.1506 + return -1; 1.1507 + } 1.1508 + ++in; 1.1509 + 1.1510 + // Determine whether we have a 3-digit color or a 6-digit color 1.1511 + for (const char *here = in; 1.1512 + hexDigitValue(*here) >= 0; 1.1513 + ++here) { 1.1514 + ++nDigits; 1.1515 + } 1.1516 + if (nDigits != 3 && nDigits != 6) { 1.1517 + return -1; 1.1518 + } 1.1519 + 1.1520 + int red = translateComponent(&in, nDigits); 1.1521 + int green = translateComponent(&in, nDigits); 1.1522 + int blue = translateComponent(&in, nDigits); 1.1523 + if (red >= 0 && green >= 0 && blue >= 0) { 1.1524 + return makecol(red, green, blue); 1.1525 + } else { 1.1526 + return -1; 1.1527 + } 1.1528 +}; 1.1529 + 1.1530 +/** 1.1531 + * Loads skin parameters from the file. 1.1532 + * @param skinName Name of skin ini file 1.1533 + */ 1.1534 +int loadSkinFile(LJPCSkin *s, const char *skinName) { 1.1535 + 1.1536 + // Don't use ljfopen here because lj.ini specifies the absolute skin file 1.1537 + FILE *fp = fopen(skinName, "rt"); 1.1538 + char key[1024], var[1024], val[1024], input_buf[1024]; 1.1539 + 1.1540 + key[0] = 0; 1.1541 + var[0] = 0; 1.1542 + val[0] = 0; 1.1543 + loadSkinDefaults(s); 1.1544 + 1.1545 + if (!fp) return 0; 1.1546 + while(1) { 1.1547 + int rval; 1.1548 + 1.1549 + if(!fgets (input_buf, sizeof(input_buf), fp)) 1.1550 + break; 1.1551 + rval = parse_ini_line(input_buf, key, var, val); 1.1552 + 1.1553 + if(!ustrcmp ("ljblocksSRS", var)) { 1.1554 + ustrzcpy(ljblocksSRSName, sizeof(ljblocksSRSName) - 1, val); 1.1555 + } 1.1556 + else if(!ustrcmp ("ljblocksSega", var)) { 1.1557 + ustrzcpy(ljblocksSegaName, sizeof(ljblocksSegaName) - 1, val); 1.1558 + } 1.1559 + else if(!ustrcmp ("ljconnSRS", var)) { 1.1560 + ustrzcpy(ljconnSRSName, sizeof(ljconnSRSName) - 1, val); 1.1561 + } 1.1562 + else if(!ustrcmp ("ljconnSega", var)) { 1.1563 + ustrzcpy(ljconnSegaName, sizeof(ljconnSegaName) - 1, val); 1.1564 + } 1.1565 + else if(!ustrcmp ("bgm", var)) { 1.1566 + ustrzcpy(bgmName, sizeof(bgmName) - 1, val); 1.1567 + } 1.1568 + else if(!ustrcmp ("bgmRhythm", var)) { 1.1569 + ustrzcpy(bgmRhythmName, sizeof(bgmRhythmName) - 1, val); 1.1570 + } 1.1571 + else if(!ustrcmp ("ljbg", var)) { 1.1572 + ustrzcpy(ljbgName, sizeof(ljbgName) - 1, val); 1.1573 + } 1.1574 + else if(!ustrcmp ("bgmLoopPoint", var)) { 1.1575 + unsigned long int c = strtoul(val, NULL, 0); 1.1576 + if (c >= 0) { 1.1577 + bgmLoopPoint = c; 1.1578 + } 1.1579 + } 1.1580 + else if(!ustrcmp ("bgmVolume", var)) { 1.1581 + unsigned long int c = strtol(val, NULL, 0); 1.1582 + if (c >= 0) { 1.1583 + bgmVolume = c; 1.1584 + } 1.1585 + } 1.1586 + else if(!ustrcmp ("bgmReadyGo", var)) { 1.1587 + unsigned long int c = strtoul(val, NULL, 0); 1.1588 + if (c >= 0) { 1.1589 + bgmReadyGo = c; 1.1590 + } 1.1591 + } 1.1592 + else if(!ustrcmp ("pfbgcolor", var)) { 1.1593 + int c = translateColor(val); 1.1594 + if (c >= 0) { 1.1595 + pfBgColor = c; 1.1596 + } 1.1597 + } 1.1598 + else if(!ustrcmp ("pfcolor", var)) { 1.1599 + int c = translateColor(val); 1.1600 + if (c >= 0) { 1.1601 + pfFgColor = c; 1.1602 + } 1.1603 + } 1.1604 + else if(!ustrcmp ("bgcolor", var)) { 1.1605 + int c = translateColor(val); 1.1606 + if (c >= 0) { 1.1607 + bgColor = c; 1.1608 + } 1.1609 + } 1.1610 + else if(!ustrcmp ("color", var)) { 1.1611 + int c = translateColor(val); 1.1612 + if (c >= 0) { 1.1613 + fgColor = c; 1.1614 + } 1.1615 + } 1.1616 + else if(!ustrcmp ("hilitecolor", var)) { 1.1617 + int c = translateColor(val); 1.1618 + if (c >= 0) { 1.1619 + hiliteColor = c; 1.1620 + } 1.1621 + } 1.1622 + else if(!ustrcmp ("blkW", var)) { 1.1623 + int c = strtol(val, NULL, 0); 1.1624 + if (c >= 0) { 1.1625 + s->blkW = c; 1.1626 + } 1.1627 + } 1.1628 + else if(!ustrcmp ("blkH", var)) { 1.1629 + int c = strtol(val, NULL, 0); 1.1630 + if (c >= 0) { 1.1631 + s->blkH = c; 1.1632 + } 1.1633 + } 1.1634 + else if(!ustrcmp ("transparentPF", var)) { 1.1635 + int c = atoi(val); 1.1636 + if (c >= 0) { 1.1637 + s->transparentPF = c; 1.1638 + } 1.1639 + } 1.1640 + else if(!ustrcmp ("shiftScale", var)) { 1.1641 + int c = atoi(val); 1.1642 + if (c >= 0) { 1.1643 + s->shiftScale = c; 1.1644 + } 1.1645 + } 1.1646 + else if(!ustrcmp ("baseX", var)) { 1.1647 + int c = atoi(val); 1.1648 + if (c >= 0) { 1.1649 + s->baseX = c; 1.1650 + } 1.1651 + } 1.1652 + else if(!ustrcmp ("nextPos", var)) { 1.1653 + int c = atoi(val); 1.1654 + if (c >= 0) { 1.1655 + s->nextPos = c; 1.1656 + } 1.1657 + } 1.1658 + else if(!ustrcmp ("wndW", var)) { 1.1659 + unsigned long int c = strtol(val, NULL, 0); 1.1660 + if (c >= 0) { 1.1661 + skinW = c; 1.1662 + } 1.1663 + } 1.1664 + else if(!ustrcmp ("wndH", var)) { 1.1665 + unsigned long int c = strtoul(val, NULL, 0); 1.1666 + if (c >= 0) { 1.1667 + skinH = c; 1.1668 + } 1.1669 + } 1.1670 + 1.1671 + } 1.1672 + fclose(fp); 1.1673 + ljpathSetSkinFolder(skinName); 1.1674 + return 0; 1.1675 +} 1.1676 + 1.1677 +static void drawProgressSegment(int min, int max) { 1.1678 + min = min * SCREEN_W / 100; 1.1679 + max = max * SCREEN_W / 100; 1.1680 + int blue = makecol(0, 0, 255); 1.1681 + rectfill(screen, min, SCREEN_H - 8, max - 1, SCREEN_H - 5, blue); 1.1682 + int orange = makecol(255, 128, 0); 1.1683 + rectfill(screen, min, SCREEN_H - 4, max - 1, SCREEN_H - 1, orange); 1.1684 +} 1.1685 + 1.1686 +static int loadSkin(LJView *v, const char *skinName) { 1.1687 + BITMAP *bmp; 1.1688 + const LJRotSystem *rs = rotSystems[v->field->rotationSystem]; 1.1689 + int colorScheme = rs->colorScheme; 1.1690 + 1.1691 + rectfill(screen, 0, 592, 799, 599, 0); 1.1692 + loadSkinFile(v->plat->skin, skinName); 1.1693 + 1.1694 + destroyBackBuf(v->plat); 1.1695 + if (!createBackBuf(v)) { 1.1696 + allegro_message("Could not create back buffer.\n"); 1.1697 + return 1; 1.1698 + } 1.1699 + 1.1700 + drawProgressSegment(0, 20); 1.1701 + 1.1702 + // Load background image 1.1703 + char path[PATH_MAX]; 1.1704 + bmp = ljpathFind_r(path, ljbgName) 1.1705 + ? load_bitmap(path, NULL) : NULL; 1.1706 + if (v->plat->skin->bg) { 1.1707 + destroy_bitmap(v->plat->skin->bg); 1.1708 + } 1.1709 + if (bmp) { 1.1710 + 1.1711 + // If the image size doesn't match the window size, resize it 1.1712 + if (bmp->w != SCREEN_W || bmp->h != SCREEN_H) { 1.1713 + BITMAP *resized = create_bitmap(SCREEN_W, SCREEN_H); 1.1714 + 1.1715 + if (resized) { 1.1716 + stretch_blit(bmp, resized, 0, 0, bmp->w, bmp->h, 1.1717 + 0, 0, SCREEN_W, SCREEN_H); 1.1718 + destroy_bitmap(bmp); 1.1719 + } 1.1720 + if (bmp) { 1.1721 + bmp = resized; 1.1722 + } else { 1.1723 + allegro_message("Background image \"%s\" resize failed.\n", 1.1724 + ljbgName); 1.1725 + } 1.1726 + } 1.1727 + } 1.1728 + if(!bmp) { 1.1729 + bmp = create_bitmap(SCREEN_W, SCREEN_H); 1.1730 + if (bmp) { 1.1731 + allegro_message("Background image \"%s\" not found.\n" 1.1732 + "Using plain background instead.\n", 1.1733 + ljbgName); 1.1734 + clear_to_color(bmp, bgColor); 1.1735 + } else { 1.1736 + allegro_message("Background image \"%s\" not found.\n", 1.1737 + ljbgName); 1.1738 + return 0; 1.1739 + } 1.1740 + } 1.1741 + v->plat->skin->bg = bmp; 1.1742 + drawProgressSegment(20, 40); 1.1743 + 1.1744 + // load block images 1.1745 + if (v->plat->skin->blocks) { 1.1746 + destroy_bitmap(v->plat->skin->blocks); 1.1747 + } 1.1748 + bmp = ljpathFind_r(path, colorScheme 1.1749 + ? ljblocksSegaName 1.1750 + : ljblocksSRSName) 1.1751 + ? load_bitmap(path, NULL) : NULL; 1.1752 + v->plat->skin->blocks = bmp; 1.1753 + if(!v->plat->skin->blocks) { 1.1754 + allegro_message("Background image \"%s\" not found.\n", 1.1755 + ljbgName); 1.1756 + return 0; 1.1757 + } 1.1758 + drawProgressSegment(40, 60); 1.1759 + 1.1760 + // load connected block images 1.1761 + if (v->plat->skin->connBlocks) { 1.1762 + destroy_bitmap(v->plat->skin->connBlocks); 1.1763 + } 1.1764 + bmp = ljpathFind_r(path, colorScheme 1.1765 + ? ljconnSegaName 1.1766 + : ljconnSRSName) 1.1767 + ? loadConnections(path, 1.1768 + v->plat->skin->blkW, 1.1769 + v->plat->skin->blkH) 1.1770 + : NULL; 1.1771 + v->plat->skin->connBlocks = bmp; 1.1772 + drawProgressSegment(60, 80); 1.1773 + 1.1774 + // load music 1.1775 + int isRhythm = (v->field->speedState.curve == LJSPD_RHYTHM); 1.1776 + if (ljpathFind_r(path, isRhythm ? bgmRhythmName : bgmName)) { 1.1777 + LJMusic_load(v->plat->skin->bgm, path); 1.1778 + } 1.1779 + if (!isRhythm) { 1.1780 + LJMusic_setLoop(v->plat->skin->bgm, bgmLoopPoint); 1.1781 + } 1.1782 + drawProgressSegment(80, 100); 1.1783 + return 1; 1.1784 +} 1.1785 + 1.1786 +void drop_mouse(void) { 1.1787 + while (mouse_y < SCREEN_H - 25) { 1.1788 + position_mouse(mouse_x, mouse_y + 20); 1.1789 + rest(10); 1.1790 + } 1.1791 + ezPlaySample("land_wav", 192); 1.1792 + position_mouse(mouse_x, SCREEN_H - 5); 1.1793 + ezPlaySample("lock_wav", 192); 1.1794 +} 1.1795 + 1.1796 +int pickReplay(void) { 1.1797 + FONT *oldFont = font; 1.1798 + font = (FONT *)aver16; 1.1799 + install_mouse(); 1.1800 + int got = file_select_ex("Choose a demo:", demoFilename, "ljm", sizeof(demoFilename), 600, 400); 1.1801 + drop_mouse(); 1.1802 + remove_mouse(); 1.1803 + font = oldFont; 1.1804 + return got ? 0 : -1; 1.1805 +} 1.1806 + 1.1807 +void badReplay(void) { 1.1808 + acquire_screen(); 1.1809 + clear_to_color(screen, bgColor); 1.1810 + textout_ex(screen, aver32, "The demo", 100, 100, fgColor, -1); 1.1811 + textout_ex(screen, aver16, demoFilename, 100, 130, fgColor, -1); 1.1812 + textout_ex(screen, aver32, "could not be played because it was", 100, 150, fgColor, -1); 1.1813 + textout_ex(screen, aver32, "recorded with a different version", 100, 180, fgColor, -1); 1.1814 + textout_ex(screen, aver32, "of LOCKJAW software.", 100, 210, fgColor, -1); 1.1815 + release_screen(); 1.1816 + 1.1817 + LJBits lastKeys = ~0; 1.1818 + LJBits keys, newKeys = 0; 1.1819 + 1.1820 + do { 1.1821 + keys = menuReadPad(); 1.1822 + newKeys = keys & ~lastKeys; 1.1823 + lastKeys = keys; 1.1824 + rest(30); 1.1825 + } while (!(newKeys & (VKEY_ROTL | VKEY_ROTR))); 1.1826 +} 1.1827 + 1.1828 +int pickSkin(void) { 1.1829 + FONT *oldFont = font; 1.1830 + font = (FONT *)aver16; 1.1831 + install_mouse(); 1.1832 + int got = file_select_ex("Choose a skin:", skinName, "skin", sizeof(skinName), 600, 400); 1.1833 + drop_mouse(); 1.1834 + remove_mouse(); 1.1835 + font = oldFont; 1.1836 + return got ? 0 : -1; 1.1837 +} 1.1838 + 1.1839 +void calcElev(LJView *v) { 1.1840 + int blkH = v->plat->skin->blkH; 1.1841 + int ceiling = v->field->ceiling; 1.1842 + int elev = (LJ_PF_VIS_HT - ceiling) * blkH; 1.1843 + 1.1844 + if (elev > 480 - ceiling * blkH) { 1.1845 + elev = 480 - ceiling * blkH; 1.1846 + } 1.1847 + if (elev < 0) { 1.1848 + elev = 0; 1.1849 + } 1.1850 + v->plat->skin->pfElev = elev; 1.1851 +} 1.1852 + 1.1853 +int main(const int argc, const char *const *argv) { 1.1854 + const char *cmdLineDemo = NULL; 1.1855 + int lastPreset = -2; // start out with full custom 1.1856 + LJPCSkin skin = { 1.1857 + .baseY = 552, 1.1858 + .blkW = 24, 1.1859 + .blkH = 24 1.1860 + }; 1.1861 + LJField p[MAX_PLAYERS]; 1.1862 + LJControl control[2] = { 1.1863 + { 1.1864 + .replaySrc = 0, 1.1865 + .replayDst = 0 1.1866 + } 1.1867 + }; 1.1868 + LJPCView platView[MAX_PLAYERS] = { 1.1869 + { 1.1870 + .skin = &skin 1.1871 + }, 1.1872 + { 1.1873 + .skin = &skin 1.1874 + } 1.1875 + }; 1.1876 + LJView mainView[MAX_PLAYERS] = { 1.1877 + { 1.1878 + .field = &p[0], 1.1879 + .control = &control[0], 1.1880 + .plat = &platView[0], 1.1881 + .backDirty = ~0 1.1882 + }, 1.1883 + { 1.1884 + .field = &p[1], 1.1885 + .control = &control[1], 1.1886 + .plat = &platView[1], 1.1887 + .backDirty = ~0, 1.1888 + } 1.1889 + }; 1.1890 + 1.1891 + struct LJPrefs prefs = { 1.1892 + .number = { 1.1893 + [OPTIONS_TRAILS] = 1, 1.1894 + [OPTIONS_AUTO_PAUSE] = 1, 1.1895 + [OPTIONS_AUTO_RECORD] = 0, 1.1896 + [OPTIONS_WINDOWED] = 1 1.1897 + } 1.1898 + }; 1.1899 + 1.1900 + // as of 0.46, we're starting to make it a bit more 2-player-clean 1.1901 + int nPlayers = 1; 1.1902 + 1.1903 + allegro_init(); 1.1904 + ljpathInit(argc > 0 ? argv[0] : "."); 1.1905 + install_timer(); 1.1906 + initOptions(prefs.number); 1.1907 + loadOptions(&prefs); 1.1908 + loadSkinFile(&skin, skinName); 1.1909 + 1.1910 + if (argc > 1) { 1.1911 + if (argv[1][0] == '-') { 1.1912 + if (!ustrcmp("--help", argv[1]) 1.1913 + || !ustrcmp("-h", argv[1])) { 1.1914 + allegro_message("Usage: lj [DEMOFILE]\n"); 1.1915 + return 0; 1.1916 + } 1.1917 + } else { 1.1918 + cmdLineDemo = argv[1]; 1.1919 + } 1.1920 + } 1.1921 + 1.1922 + if (openWindow(prefs.number[OPTIONS_WINDOWED]) != 0) { 1.1923 + allegro_message("LOCKJAW fatal error: Could not open an %dx%d pixel %s.\n" 1.1924 + "Trying %s next time.\n", 1.1925 + skinW, skinH, 1.1926 + prefs.number[OPTIONS_WINDOWED] ? "window": "screen mode", 1.1927 + prefs.number[OPTIONS_WINDOWED] ? "the full screen": "a window"); 1.1928 + prefs.number[OPTIONS_WINDOWED] = !prefs.number[OPTIONS_WINDOWED]; 1.1929 + saveOptions(&prefs); 1.1930 + return EXIT_FAILURE; 1.1931 + } 1.1932 + drawCoprNotice(); 1.1933 + LOCK_FUNCTION(incCurTime); 1.1934 + LOCK_VARIABLE(curTime); 1.1935 + install_int_ex(incCurTime, BPM_TO_TIMER(LJ_TICK_RATE)); 1.1936 + 1.1937 + jpgalleg_init(); 1.1938 + set_color_conversion(COLORCONV_NONE); 1.1939 + { 1.1940 + char path[PATH_MAX]; 1.1941 + if (ljpathFind_r(path, "lj.dat")) { 1.1942 + dat = load_datafile(path); 1.1943 + } 1.1944 + } 1.1945 + set_color_conversion(COLORCONV_TOTAL); 1.1946 + if(!dat) { 1.1947 + closeWindow(); 1.1948 + allegro_message("LOCKJAW fatal error: Could not load datafile lj.dat\n"); 1.1949 + return 1; 1.1950 + } 1.1951 + 1.1952 + { 1.1953 + const DATAFILE *aver16dat = find_datafile_object(dat, "Aver16_bmp"); 1.1954 + aver16 = aver16dat ? aver16dat->dat : font; 1.1955 + const DATAFILE *aver32dat = find_datafile_object(dat, "Aver32_bmp"); 1.1956 + aver32 = aver32dat ? aver32dat->dat : aver16; 1.1957 + } 1.1958 + 1.1959 + LOCK_FUNCTION(amnesia); 1.1960 + LOCK_VARIABLE(redrawWholeScreen); 1.1961 + 1.1962 + // If we can be notified on switching out, take this notification. 1.1963 + if (set_display_switch_mode(SWITCH_BACKGROUND) >= 0 1.1964 + || set_display_switch_mode(SWITCH_BACKAMNESIA) >= 0) { 1.1965 + set_display_switch_callback(SWITCH_OUT, requestPause); 1.1966 + } 1.1967 + set_display_switch_callback(SWITCH_IN, amnesia); 1.1968 + 1.1969 + install_keyboard(); 1.1970 + initKeys(); 1.1971 + 1.1972 + for (int i = 0; i < MAX_PLAYERS; ++i) { 1.1973 + p[i].seed = time(NULL) + 123456789*i; 1.1974 + } 1.1975 + 1.1976 + reserve_voices(8, 0); 1.1977 + set_volume_per_voice(0); 1.1978 +#ifdef ALLEGRO_WINDOWS 1.1979 + // Under Windows, use the Allegro mixer because on my machine 1.1980 + // and probably others, the built-in mixer will replace the very 1.1981 + // beginning of one sound with the end of the last sound played 1.1982 + // on that voice. 1.1983 + withSound = !install_sound(DIGI_DIRECTAMX(0), MIDI_NONE, NULL); 1.1984 +#else 1.1985 + withSound = !install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL); 1.1986 +#endif 1.1987 + skin.bgm = LJMusic_new(); 1.1988 + { 1.1989 + char path[PATH_MAX]; 1.1990 + if (ljpathFind_r(path, "sound.dat")) { 1.1991 + sound_dat = load_datafile(path); 1.1992 + } 1.1993 + } 1.1994 + 1.1995 + for (int i = 0; i < MAX_PLAYERS; ++i) { 1.1996 + unpackOptions(&mainView[i], &prefs); 1.1997 + } 1.1998 + if (loadSkin(&mainView[0], skinName) < 0) { 1.1999 + mainCleanup(platView); 1.2000 + return EXIT_FAILURE; 1.2001 + } else { 1.2002 + 1.2003 + } 1.2004 + 1.2005 + if(!skin.blocks) { 1.2006 + mainCleanup(platView); 1.2007 + allegro_message("Blocks image \"%s\" not found.\n", 1.2008 + p[0].rotationSystem 1.2009 + ? ljblocksSegaName 1.2010 + : ljblocksSRSName); 1.2011 + return 1; 1.2012 + } 1.2013 + 1.2014 + srand(time(NULL)); 1.2015 + 1.2016 + // Wait for copyright notice to be displayed "conspicuously" 1.2017 + if (curTime < 180) { 1.2018 + rest(3100 - curTime * 16); 1.2019 + } 1.2020 + 1.2021 + for (int action = cmdLineDemo ? TITLE_REPLAY : title(); 1.2022 + action > TITLE_EXIT && !wantsClose; 1.2023 + action = title()) { 1.2024 + switch (action) { 1.2025 + case TITLE_PLAY: 1.2026 + for (int preset = getPreset(lastPreset); 1.2027 + preset != -1 && !wantsClose; 1.2028 + preset = getPreset(lastPreset)) 1.2029 + { 1.2030 + lastPreset = preset; 1.2031 + for (int i = 0; i < MAX_PLAYERS; ++i) { 1.2032 + unpackOptions(&mainView[i], &prefs); 1.2033 + } 1.2034 + if (preset >= 0) { 1.2035 + presetStart(); 1.2036 + presetAdd(preset); 1.2037 + for (int i = 0; i < MAX_PLAYERS; ++i) { 1.2038 + unpackOptions(&mainView[i], &prefs); 1.2039 + presetFinish(&mainView[i]); 1.2040 + } 1.2041 + } 1.2042 + // reload the skin 1.2043 + if (loadSkin(&mainView[0], skinName) < 0) { 1.2044 + mainCleanup(platView); 1.2045 + return EXIT_FAILURE; 1.2046 + }; 1.2047 + for (int i = 0; i < nPlayers; ++i) { 1.2048 + calcElev(&mainView[0]); 1.2049 + pcInit(&mainView[0], &prefs); 1.2050 + } 1.2051 + wantPause = 0; 1.2052 + platView[0].wantRecord = prefs.number[OPTIONS_AUTO_RECORD]; 1.2053 + LJMusic_start(skin.bgm, 1.2054 + 4096, // mix buffer size 1.2055 + bgmVolume); // volume scale 1.2056 + 1.2057 + LJView *const players[MAX_PLAYERS] = {&mainView[0], &mainView[1]}; 1.2058 + play(players, nPlayers); 1.2059 + LJMusic_stop(skin.bgm); 1.2060 + if (!wantsClose) { 1.2061 + gameOverAnimation(&platView[0], &p[0], 1.2062 + control[0].countdown <= 0); 1.2063 + debrief(&mainView[0]); 1.2064 + } 1.2065 + } 1.2066 + break; 1.2067 + 1.2068 + case TITLE_REPLAY: 1.2069 + { 1.2070 + if (cmdLineDemo) { 1.2071 + ustrzcpy(demoFilename, sizeof(demoFilename) - 1, 1.2072 + cmdLineDemo); 1.2073 + cmdLineDemo = NULL; 1.2074 + } else if (pickReplay() < 0) { 1.2075 + break; 1.2076 + } 1.2077 + 1.2078 + unpackOptions(&mainView[0], &prefs); 1.2079 + calcElev(&mainView[0]); 1.2080 + pcInit(&mainView[0], &prefs); 1.2081 + wantPause = 0; 1.2082 + platView[0].wantRecord = 0; 1.2083 + LJMusic_start(skin.bgm, 1.2084 + 4096, // mix buffer size 1.2085 + bgmVolume); // volume scale 1.2086 + 1.2087 + LJView *const players[2] = {&mainView[0], &mainView[1]}; 1.2088 + 1.2089 + p->gimmick = -1; // gimmick must be < 0 to activate replay 1.2090 + play(players, 1); 1.2091 + LJMusic_stop(skin.bgm); 1.2092 + if (p[0].gimmick < 0) { 1.2093 + badReplay(); 1.2094 + } else { 1.2095 + if (!wantsClose) { 1.2096 + gameOverAnimation(&platView[0], &p[0], 1.2097 + control[0].countdown <= 0); 1.2098 + debrief(&mainView[0]); 1.2099 + } 1.2100 + } 1.2101 + } 1.2102 + break; 1.2103 + 1.2104 + case TITLE_SKIN: 1.2105 + pickSkin(); 1.2106 + 1.2107 + // if resolution changed, reopen the window 1.2108 + { 1.2109 + int oldW = skinW; 1.2110 + int oldH = skinH; 1.2111 + loadSkinFile(&skin, skinName); 1.2112 + if (skinH != oldH || skinW != oldW) { 1.2113 + destroySystemBitmaps(&platView[0]); 1.2114 + openWindow(prefs.number[OPTIONS_WINDOWED]); 1.2115 + } 1.2116 + } 1.2117 + 1.2118 + // reload the skin 1.2119 + if (loadSkin(&mainView[0], skinName) < 0) { 1.2120 + mainCleanup(platView); 1.2121 + return EXIT_FAILURE; 1.2122 + }; 1.2123 + 1.2124 + // save options 1.2125 + saveOptions(&prefs); 1.2126 + break; 1.2127 + 1.2128 + case TITLE_OPTIONS: 1.2129 + { 1.2130 + int oldWindowed = prefs.number[OPTIONS_WINDOWED]; 1.2131 + options(&mainView[0], prefs.number); 1.2132 + if (oldWindowed != prefs.number[OPTIONS_WINDOWED]) { 1.2133 + destroySystemBitmaps(&platView[0]); 1.2134 + openWindow(prefs.number[OPTIONS_WINDOWED]); 1.2135 + } 1.2136 + } 1.2137 + saveOptions(&prefs); 1.2138 + 1.2139 + // reload the skin if the player changed the rotation system 1.2140 + unpackOptions(&mainView[0], &prefs); 1.2141 + if (wantsClose) { 1.2142 + break; 1.2143 + } 1.2144 + if (loadSkin(&mainView[0], skinName) < 0) { 1.2145 + mainCleanup(platView); 1.2146 + return EXIT_FAILURE; 1.2147 + }; 1.2148 + break; 1.2149 + 1.2150 + case TITLE_KEYS: 1.2151 + configureKeys(); 1.2152 + break; 1.2153 + 1.2154 + } 1.2155 + } 1.2156 + 1.2157 + mainCleanup(platView); 1.2158 + return 0; 1.2159 +} END_OF_MAIN();