annotate src/gimmicks.c @ 0:c84446dfb3f5

initial add
author paulo@localhost
date Fri, 13 Mar 2009 00:39:12 -0700
parents
children
rev   line source
paulo@0 1 /* Gimmick code for LOCKJAW, an implementation of the Soviet Mind Game
paulo@0 2
paulo@0 3 Copyright (C) 2006-2007 Damian Yerrick <tepples+lj@spamcop.net>
paulo@0 4
paulo@0 5 This work is free software; you can redistribute it and/or modify
paulo@0 6 it under the terms of the GNU General Public License as published by
paulo@0 7 the Free Software Foundation; either version 2 of the License, or
paulo@0 8 (at your option) any later version.
paulo@0 9
paulo@0 10 This program is distributed in the hope that it will be useful,
paulo@0 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
paulo@0 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
paulo@0 13 GNU General Public License for more details.
paulo@0 14
paulo@0 15 You should have received a copy of the GNU General Public License
paulo@0 16 along with this program; if not, write to the Free Software
paulo@0 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
paulo@0 18
paulo@0 19 Original game concept and design by Alexey Pajitnov.
paulo@0 20 The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg,
paulo@0 21 or The Tetris Company LLC.
paulo@0 22
paulo@0 23 */
paulo@0 24
paulo@0 25 #include "lj.h"
paulo@0 26 #include "ljcontrol.h"
paulo@0 27
paulo@0 28 void initSpeed(LJField *p); // in speed.c
paulo@0 29 void setSpeed(LJField *p, LJControl *c); // in speed.c
paulo@0 30 int updLevelAfterPiece(LJField *p); // in speed.c
paulo@0 31 int updLevelAfterLines(LJField *p, size_t nLines); // in speed.c
paulo@0 32
paulo@0 33 void initGimmicks(LJField *p) {
paulo@0 34 initSpeed(p);
paulo@0 35 p->speed.entryDelay = 0; /* new pieces will increase this */
paulo@0 36 p->speed.lineDelay = 0;
paulo@0 37 if (p->garbageStyle == LJGARBAGE_DRILL
paulo@0 38 || p->gimmick == LJGM_DRILL) {
paulo@0 39 p->garbageRandomness = 255;
paulo@0 40 p->garbage = p->ceiling - 2;
paulo@0 41 } else if (p->garbageStyle == LJGARBAGE_ZIGZAG) {
paulo@0 42 setupZigzagField(p, p->ceiling * 3 / 4);
paulo@0 43 }
paulo@0 44
paulo@0 45 }
paulo@0 46
paulo@0 47 LJBits gimmicks(LJField *p, LJControl *c) {
paulo@0 48 LJBits changed = 0;
paulo@0 49
paulo@0 50 // In rhythm, lock the tetromino if the player
paulo@0 51 // isn't keeping up with the rate
paulo@0 52 if (p->speedState.curve == LJSPD_RHYTHM
paulo@0 53 || p->speedState.curve == LJSPD_RHYTHMZERO) {
paulo@0 54 p->bpmCounter += p->speedState.level;
paulo@0 55
paulo@0 56 if (p->bpmCounter >= 0) {
paulo@0 57 if (p->state == LJS_LANDED) {
paulo@0 58 p->stateTime = 0;
paulo@0 59 } else if (p->state == LJS_FALLING) {
paulo@0 60 p->speed.gravity = ljitofix(LJ_PF_HT);
paulo@0 61 }
paulo@0 62 }
paulo@0 63
paulo@0 64 // In rhythm, increase BPM periodically
paulo@0 65 p->speedupCounter += p->speedState.level;
paulo@0 66 if(p->speedupCounter >= 60 * 60 * 64) {
paulo@0 67 p->speedupCounter -= 60 * 60 * 64;
paulo@0 68 p->speedState.level += 10;
paulo@0 69 p->sounds |= LJSND_SECTIONUP;
paulo@0 70 }
paulo@0 71 }
paulo@0 72
paulo@0 73 // For each piece, set the entry and line delays.
paulo@0 74 // Don't set it twice when spawning to replace the
paulo@0 75 // first held piece (both LJSND_SPAWN and LJSND_HOLD
paulo@0 76 // on the same piece).
paulo@0 77 if ((p->sounds & (LJSND_SPAWN | LJSND_HOLD))
paulo@0 78 == LJSND_SPAWN) {
paulo@0 79 setSpeed(p, c);
paulo@0 80 }
paulo@0 81
paulo@0 82 if (p->sounds & LJSND_LOCK) {
paulo@0 83 if (p->gimmick == LJGM_ITEMS) {
paulo@0 84 if (p->nPieces >= 7) {
paulo@0 85 p->canRotate = 0;
paulo@0 86 p->speed.gravity = ljitofix(1);
paulo@0 87 }
paulo@0 88 }
paulo@0 89
paulo@0 90 // Garbage in simulated multiplayer
paulo@0 91 int simGarbage = p->nPieces >= 7
paulo@0 92 && p->garbageStyle >= LJGARBAGE_1
paulo@0 93 && p->garbageStyle <= LJGARBAGE_4
paulo@0 94 && (p->curPiece[1] == LJP_I
paulo@0 95 || ((p->pieceSet == LJRAND_SZ
paulo@0 96 || p->pieceSet == LJRAND_JLOSTZ)
paulo@0 97 && p->nPieces % 7 == 3));
paulo@0 98 if (simGarbage) {
paulo@0 99 p->garbage += p->garbageStyle;
paulo@0 100 }
paulo@0 101
paulo@0 102 // Banana attack in "Vs. with Items" gimmick
paulo@0 103 if (p->gimmick == LJGM_ITEMS
paulo@0 104 && (ljRand(p) & 0xF00) == 0xF00) {
paulo@0 105 shuffleColumns(p);
paulo@0 106 changed |= (1 << LJ_PF_HT) - 1;
paulo@0 107 }
paulo@0 108 }
paulo@0 109 return changed;
paulo@0 110 }
paulo@0 111
paulo@0 112
paulo@0 113
paulo@0 114 const char hotlineRows[LJ_PF_HT] =
paulo@0 115 {
paulo@0 116 0, 0, 0, 0, 1,
paulo@0 117 0, 0, 0, 0, 2,
paulo@0 118 0, 0, 0, 3, 0,
paulo@0 119 0, 4, 0, 5, 6,
paulo@0 120 0, 0, 0, 0
paulo@0 121 };
paulo@0 122
paulo@0 123 static const char garbageScoreTable[] = { 0, 0, 1, 2, 4 };
paulo@0 124 static const char tSpinGarbageScoreTable[] = { 0, 2, 4, 6, 6 };
paulo@0 125 static const unsigned char squareScoreTable[] =
paulo@0 126 {1, 1, 1, 2, 3, 5, 8, 13,
paulo@0 127 21, 34, 55, 89, 144, 200};
paulo@0 128
paulo@0 129 static const char tdsScoreTable[] = {0, 1, 3, 5, 8};
paulo@0 130 static const char tdsTSScoreTable[] = {4, 8, 12, 16};
paulo@0 131 static const char nesScoreTable[] = {0, 4, 10, 30, 120};
paulo@0 132
paulo@0 133
paulo@0 134 /**
paulo@0 135 * Computes the score and outgoing garbage for lines
paulo@0 136 * and adds them to the player's total.
paulo@0 137 * @param p The playfield
paulo@0 138 * @param lines Bit array where 1 means a line clear on this row.
paulo@0 139 */
paulo@0 140 void addLinesScore(LJField *p, LJBits lines) {
paulo@0 141 const int nLines = countOnes(lines);
paulo@0 142 int oldLines = p->nLinesThisPiece;
paulo@0 143 int tdsSection = p->lines / 10 + 1;
paulo@0 144
paulo@0 145 p->lines += nLines;
paulo@0 146 if (updLevelAfterLines(p, nLines)) {
paulo@0 147 p->sounds |= LJSND_SECTIONUP;
paulo@0 148 }
paulo@0 149
paulo@0 150 switch (p->scoreStyle) {
paulo@0 151
paulo@0 152 case LJSCORE_TNT64:
paulo@0 153 if (nLines < 1) {
paulo@0 154 return;
paulo@0 155 }
paulo@0 156 for (int i = nLines; i > 0; --i) {
paulo@0 157 if (oldLines > sizeof(squareScoreTable) - 1) {
paulo@0 158 oldLines = sizeof(squareScoreTable) - 1;
paulo@0 159 }
paulo@0 160 p->score += 100 * squareScoreTable[oldLines++];
paulo@0 161 } break;
paulo@0 162
paulo@0 163 case LJSCORE_NES:
paulo@0 164 {
paulo@0 165 int garbageLevel = (nLines > 4) ? 4 : nLines;
paulo@0 166 int value = nesScoreTable[garbageLevel];
paulo@0 167
paulo@0 168 p->score += 10 * tdsSection * value;
paulo@0 169 if (nLines >= 4) {
paulo@0 170 p->sounds |= LJSND_SETB2B;
paulo@0 171 }
paulo@0 172 } break;
paulo@0 173
paulo@0 174 case LJSCORE_TDS:
paulo@0 175 {
paulo@0 176 // Garbage based scoring system.
paulo@0 177 int garbageLevel = (nLines > 4) ? 4 : nLines;
paulo@0 178 int chain, value;
paulo@0 179
paulo@0 180 if (p->isSpin) {
paulo@0 181 chain = (nLines >= 1);
paulo@0 182 value = tdsTSScoreTable[garbageLevel];
paulo@0 183
paulo@0 184 // TDS gives fewer points for a T-spin single
paulo@0 185 // that involves a wall kick.
paulo@0 186 if (p->isSpin == 2 && nLines < 2) {
paulo@0 187 value >>= 2;
paulo@0 188 }
paulo@0 189 } else {
paulo@0 190 chain = (nLines >= 4);
paulo@0 191 value = tdsScoreTable[garbageLevel];
paulo@0 192 }
paulo@0 193 if (chain && p->chain && nLines >= 1) { // b2b
paulo@0 194 value = value * 3 / 2;
paulo@0 195 }
paulo@0 196
paulo@0 197 if (tdsSection > 20) {
paulo@0 198 tdsSection = 20;
paulo@0 199 }
paulo@0 200 p->score += 100 * tdsSection * value;
paulo@0 201 if (nLines >= 1) {
paulo@0 202 if (chain) {
paulo@0 203 p->sounds |= LJSND_SETB2B;
paulo@0 204 if (p->chain) {
paulo@0 205 p->sounds |= LJSND_B2B;
paulo@0 206 }
paulo@0 207 }
paulo@0 208 p->chain = chain;
paulo@0 209 }
paulo@0 210 } break;
paulo@0 211
paulo@0 212 case LJSCORE_LJ:
paulo@0 213 case LJSCORE_LJ_NERF:
paulo@0 214 if (nLines >= 1) {
paulo@0 215 // Garbage based scoring system.
paulo@0 216 int garbageLevel = (nLines > 4) ? 4 : nLines;
paulo@0 217 unsigned int chain, garbage;
paulo@0 218
paulo@0 219 if (p->isSpin) {
paulo@0 220 chain = (nLines >= 1);
paulo@0 221 garbage = tSpinGarbageScoreTable[garbageLevel];
paulo@0 222 if (p->scoreStyle == LJSCORE_LJ_NERF) {
paulo@0 223 garbage /= 2;
paulo@0 224 }
paulo@0 225 garbage += (chain && p->chain);
paulo@0 226 } else {
paulo@0 227 chain = (nLines >= 4);
paulo@0 228 garbage = garbageScoreTable[garbageLevel]
paulo@0 229 + (chain && p->chain);
paulo@0 230 }
paulo@0 231 p->score += 100 * nLines + 200 * garbage;
paulo@0 232 p->outGarbage += garbage;
paulo@0 233 if (chain) {
paulo@0 234 p->sounds |= LJSND_SETB2B;
paulo@0 235 if (p->chain) {
paulo@0 236 p->sounds |= LJSND_B2B;
paulo@0 237 }
paulo@0 238 }
paulo@0 239 p->chain = chain;
paulo@0 240 } break;
paulo@0 241
paulo@0 242 case LJSCORE_HOTLINE:
paulo@0 243 for (int y = 0; y < 24; ++y) {
paulo@0 244 if (lines & (1 << y)) {
paulo@0 245 p->score += 100 * hotlineRows[y];
paulo@0 246 }
paulo@0 247 } break;
paulo@0 248
paulo@0 249 }
paulo@0 250
paulo@0 251 p->nLinesThisPiece += nLines;
paulo@0 252 }
paulo@0 253
paulo@0 254 /**
paulo@0 255 * Puts a zigzag pattern of garbage into the playfield.
paulo@0 256 * Useful as a test suite for calcZigzagGrade() in debrief.c.
paulo@0 257 * @param p the playfield to set up
paulo@0 258 * @param height the number of rows to set up
paulo@0 259 */
paulo@0 260 void setupZigzagField(LJField *p, size_t height) {
paulo@0 261 unsigned int hole = p->leftWall;
paulo@0 262 int delta = 1;
paulo@0 263
paulo@0 264 if (height > p->ceiling) {
paulo@0 265 height = p->ceiling;
paulo@0 266 }
paulo@0 267
paulo@0 268 // set up example pattern
paulo@0 269 for (size_t y = 0; y < height; ++y) {
paulo@0 270 for (size_t x = p->leftWall; x < p->rightWall; ++x) {
paulo@0 271
paulo@0 272 // A block should be in all cells of the row
paulo@0 273 // except for the hole cell, which should be empty.
paulo@0 274 if (x == hole) {
paulo@0 275 p->b[y][x] = 0;
paulo@0 276 } else {
paulo@0 277 p->b[y][x] = 0x80;
paulo@0 278 }
paulo@0 279 }
paulo@0 280
paulo@0 281 if (hole == p->rightWall - 1) {
paulo@0 282 delta = -1;
paulo@0 283 } else if (hole == p->leftWall) {
paulo@0 284 delta = 1;
paulo@0 285 }
paulo@0 286 hole += delta;
paulo@0 287 }
paulo@0 288
paulo@0 289 // clear rows above pattern
paulo@0 290 for (size_t y = height; y < LJ_PF_HT; ++y) {
paulo@0 291 for (size_t x = p->leftWall; x < p->rightWall; ++x) {
paulo@0 292 p->b[y][x] = 0;
paulo@0 293 }
paulo@0 294 }
paulo@0 295 }