paulo@0: /* Game loop frontend for LOCKJAW, an implementation of the Soviet Mind Game paulo@0: paulo@0: Copyright (C) 2006 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[player], Elorg, paulo@0: or The Tetris Company LLC. paulo@0: paulo@0: */ paulo@0: paulo@0: #include "ljplay.h" paulo@0: #ifndef WITH_REPLAY paulo@0: #define WITH_REPLAY 0 paulo@0: #endif paulo@0: paulo@0: #if WITH_REPLAY paulo@0: #include "ljreplay.h" paulo@0: extern const char demoFilename[]; paulo@0: paulo@0: #endif paulo@0: paulo@0: paulo@0: void play(LJView *const v[], size_t nPlayers) { paulo@0: int canceled = 0; paulo@0: paulo@0: for (unsigned int player = 0; player < nPlayers; ++player) { paulo@0: LJField *const p = v[player]->field; paulo@0: LJControl *const ctl = v[player]->control; paulo@0: paulo@0: ctl->countdown = 6; paulo@0: ctl->presses = 0; paulo@0: paulo@0: /* Load replay if needed */ paulo@0: #if WITH_REPLAY paulo@0: if (p->gimmick < 0) { paulo@0: ctl->replaySrc = openReplay(demoFilename, p); paulo@0: if (!ctl->replaySrc) { paulo@0: return; paulo@0: } paulo@0: v[player]->backDirty = ~0; paulo@0: playRedrawScreen(v[player]); paulo@0: } else { paulo@0: ctl->replaySrc = 0; paulo@0: } paulo@0: #endif paulo@0: } paulo@0: if (!v[0]->control->replaySrc) { paulo@0: for (unsigned int player = 0; player < nPlayers; ++player) { paulo@0: LJField *const p = v[player]->field; paulo@0: newGame(p); paulo@0: initGimmicks(p); paulo@0: v[player]->nLockTimes = 0; paulo@0: v[player]->hideNext = 0; paulo@0: updField(v[player], ~0); paulo@0: } paulo@0: startingAnimation(v[0]); paulo@0: for (unsigned int player = 0; player < nPlayers; ++player) { paulo@0: v[player]->control->lastKeys = 0; paulo@0: v[player]->control->repressKeys = 0; paulo@0: blitField(v[player]); paulo@0: } paulo@0: } paulo@0: paulo@0: int lastTime = getTime(); paulo@0: paulo@0: while(v[0]->field->state != LJS_GAMEOVER && !canceled) { paulo@0: if (getTime() == lastTime) { paulo@0: yieldCPU(); paulo@0: } paulo@0: canceled |= ljHandleConsoleButtons(v[0]); paulo@0: while (getTime() - lastTime > 0) { paulo@0: for (unsigned int player = 0; player < nPlayers; ++player) { paulo@0: LJField *const p = v[player]->field; paulo@0: LJControl *const ctl = v[player]->control; paulo@0: LJInput in = {0, 0, 0, 0}; paulo@0: int curTime = getTime(); paulo@0: paulo@0: addKeysToInput(&in, readPad(player), p, ctl); paulo@0: paulo@0: // when returning from pause, catch up the speed control paulo@0: if (curTime - lastTime > 10 paulo@0: || curTime - lastTime < -10) { paulo@0: lastTime = curTime; paulo@0: } paulo@0: paulo@0: { paulo@0: int irsAttempted = (p->sounds & (LJSND_SPAWN | LJSND_HOLD)) paulo@0: && in.rotation && ctl->initialRotate; paulo@0: paulo@0: // The next line does ALL the game logic. paulo@0: LJBits updatedRows = frame(p, &in) | gimmicks(p, ctl); paulo@0: paulo@0: v[player]->backDirty |= updatedRows; paulo@0: if (irsAttempted && (p->sounds & LJSND_ROTATE)) { paulo@0: p->sounds |= LJSND_IRS; paulo@0: } paulo@0: } paulo@0: paulo@0: // items is a partly view-based gimmick paulo@0: if (p->gimmick == LJGM_ITEMS && (p->sounds & LJSND_SPAWN)) { paulo@0: v[player]->hideNext = (v[player]->field->nPieces > 7); paulo@0: v[player]->frontDirty |= LJ_DIRTY_NEXT; paulo@0: } paulo@0: paulo@0: // five, four, three, two, one paulo@0: int curCountdown = ctl->countdown; paulo@0: if (p->gimmick == LJGM_ULTRA && p->gameTime >= 10500) { paulo@0: curCountdown = (int)(10859 - p->gameTime) / 60; paulo@0: } else if (p->gimmick == LJGM_BTYPE) { paulo@0: curCountdown = 40 - p->lines; paulo@0: } else if (p->gimmick == LJGM_BABY) { paulo@0: curCountdown = (309 - v[player]->control->presses) / 10; paulo@0: } else if (p->gimmick == LJGM_DRILL && (p->sounds & LJSND_LINE)) { paulo@0: int line = bfffo(p->tempRows); paulo@0: if (line < curCountdown) paulo@0: curCountdown = line; paulo@0: } paulo@0: paulo@0: // we have to wait for the new piece to come out paulo@0: // so that the score is credited properly paulo@0: if (curCountdown <= 0 paulo@0: && (p->state == LJS_NEW_PIECE paulo@0: || p->state == LJS_FALLING paulo@0: || p->state == LJS_LANDED)) { paulo@0: p->state = LJS_GAMEOVER; paulo@0: } paulo@0: paulo@0: playSoundEffects(v[player], p->sounds, curCountdown); paulo@0: ctl->countdown = curCountdown; paulo@0: paulo@0: // Update speedometer paulo@0: if (p->sounds & LJSND_LOCK) { paulo@0: for (int i = N_SPEED_METER_PIECES - 2; i >= 0; --i) { paulo@0: v[player]->lockTime[i + 1] = v[player]->lockTime[i]; paulo@0: } paulo@0: v[player]->lockTime[0] = p->gameTime; paulo@0: if (v[player]->nLockTimes < N_SPEED_METER_PIECES) { paulo@0: ++v[player]->nLockTimes; paulo@0: } paulo@0: v[player]->frontDirty = LJ_DIRTY_SCORE; paulo@0: } paulo@0: paulo@0: // If the piece was just spawned, move the trail to the piece's paulo@0: // starting position and redraw next pieces. paulo@0: if (p->sounds & (LJSND_SPAWN | LJSND_HOLD)) { paulo@0: v[player]->trailY = p->y; paulo@0: v[player]->frontDirty |= LJ_DIRTY_NEXT; paulo@0: } paulo@0: } paulo@0: paulo@0: ++lastTime; paulo@0: } paulo@0: paulo@0: for (unsigned int player = 0; player < nPlayers; ++player) { paulo@0: LJField *const p = v[player]->field; paulo@0: if (p->state == LJS_GAMEOVER && v[player]->hidePF) { paulo@0: v[player]->hidePF = 0; paulo@0: v[player]->backDirty |= (1 << LJ_PF_VIS_HT) - 1; paulo@0: } paulo@0: paulo@0: updField(v[player], v[player]->backDirty); paulo@0: v[player]->frontDirty |= v[player]->backDirty; paulo@0: v[player]->backDirty = 0; paulo@0: paulo@0: // If piece is falling or landed, and it wasn't just spawned, paulo@0: // draw the piece and its shadow. paulo@0: paulo@0: if (p->sounds & (LJSND_SPAWN | LJSND_HOLD)) { paulo@0: // piece was just spawned, so don't draw the piece paulo@0: } paulo@0: // Otherwise, if the piece is falling or landed, draw it. paulo@0: else if (p->state == LJS_FALLING || p->state == LJS_LANDED paulo@0: || p->sounds & LJSND_LOCK) { paulo@0: if (v[player]->hideShadow != LJSHADOW_NO_FALLING) { paulo@0: if (p->state == LJS_FALLING && v[player]->hideShadow < LJSHADOW_NONE) { paulo@0: drawShadow(v[player]); paulo@0: } paulo@0: drawFallingPiece(v[player]); paulo@0: } paulo@0: } paulo@0: paulo@0: ljBeginDraw(v[player], getTime() - lastTime < 2); paulo@0: drawScore(v[player]); paulo@0: blitField(v[player]); paulo@0: ljEndDraw(v[player]); paulo@0: } paulo@0: } paulo@0: paulo@0: #if WITH_REPLAY paulo@0: { paulo@0: int player = 0; paulo@0: if (v[player]->control->replaySrc) { paulo@0: replayClose(v[player]->control->replaySrc); paulo@0: v[player]->control->replaySrc = NULL; paulo@0: } paulo@0: if (v[player]->control->replayDst) { paulo@0: replayClose(v[player]->control->replayDst); paulo@0: v[player]->control->replayDst = NULL; paulo@0: } paulo@0: } paulo@0: #endif paulo@0: }