Mercurial > hg > index.fcgi > lj > lj046-2players
diff src/gimmicks.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/gimmicks.c Fri Mar 13 00:39:12 2009 -0700 1.3 @@ -0,0 +1,295 @@ 1.4 +/* Gimmick code for LOCKJAW, an implementation of the Soviet Mind Game 1.5 + 1.6 +Copyright (C) 2006-2007 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 "lj.h" 1.29 +#include "ljcontrol.h" 1.30 + 1.31 +void initSpeed(LJField *p); // in speed.c 1.32 +void setSpeed(LJField *p, LJControl *c); // in speed.c 1.33 +int updLevelAfterPiece(LJField *p); // in speed.c 1.34 +int updLevelAfterLines(LJField *p, size_t nLines); // in speed.c 1.35 + 1.36 +void initGimmicks(LJField *p) { 1.37 + initSpeed(p); 1.38 + p->speed.entryDelay = 0; /* new pieces will increase this */ 1.39 + p->speed.lineDelay = 0; 1.40 + if (p->garbageStyle == LJGARBAGE_DRILL 1.41 + || p->gimmick == LJGM_DRILL) { 1.42 + p->garbageRandomness = 255; 1.43 + p->garbage = p->ceiling - 2; 1.44 + } else if (p->garbageStyle == LJGARBAGE_ZIGZAG) { 1.45 + setupZigzagField(p, p->ceiling * 3 / 4); 1.46 + } 1.47 + 1.48 +} 1.49 + 1.50 +LJBits gimmicks(LJField *p, LJControl *c) { 1.51 + LJBits changed = 0; 1.52 + 1.53 + // In rhythm, lock the tetromino if the player 1.54 + // isn't keeping up with the rate 1.55 + if (p->speedState.curve == LJSPD_RHYTHM 1.56 + || p->speedState.curve == LJSPD_RHYTHMZERO) { 1.57 + p->bpmCounter += p->speedState.level; 1.58 + 1.59 + if (p->bpmCounter >= 0) { 1.60 + if (p->state == LJS_LANDED) { 1.61 + p->stateTime = 0; 1.62 + } else if (p->state == LJS_FALLING) { 1.63 + p->speed.gravity = ljitofix(LJ_PF_HT); 1.64 + } 1.65 + } 1.66 + 1.67 + // In rhythm, increase BPM periodically 1.68 + p->speedupCounter += p->speedState.level; 1.69 + if(p->speedupCounter >= 60 * 60 * 64) { 1.70 + p->speedupCounter -= 60 * 60 * 64; 1.71 + p->speedState.level += 10; 1.72 + p->sounds |= LJSND_SECTIONUP; 1.73 + } 1.74 + } 1.75 + 1.76 + // For each piece, set the entry and line delays. 1.77 + // Don't set it twice when spawning to replace the 1.78 + // first held piece (both LJSND_SPAWN and LJSND_HOLD 1.79 + // on the same piece). 1.80 + if ((p->sounds & (LJSND_SPAWN | LJSND_HOLD)) 1.81 + == LJSND_SPAWN) { 1.82 + setSpeed(p, c); 1.83 + } 1.84 + 1.85 + if (p->sounds & LJSND_LOCK) { 1.86 + if (p->gimmick == LJGM_ITEMS) { 1.87 + if (p->nPieces >= 7) { 1.88 + p->canRotate = 0; 1.89 + p->speed.gravity = ljitofix(1); 1.90 + } 1.91 + } 1.92 + 1.93 + // Garbage in simulated multiplayer 1.94 + int simGarbage = p->nPieces >= 7 1.95 + && p->garbageStyle >= LJGARBAGE_1 1.96 + && p->garbageStyle <= LJGARBAGE_4 1.97 + && (p->curPiece[1] == LJP_I 1.98 + || ((p->pieceSet == LJRAND_SZ 1.99 + || p->pieceSet == LJRAND_JLOSTZ) 1.100 + && p->nPieces % 7 == 3)); 1.101 + if (simGarbage) { 1.102 + p->garbage += p->garbageStyle; 1.103 + } 1.104 + 1.105 + // Banana attack in "Vs. with Items" gimmick 1.106 + if (p->gimmick == LJGM_ITEMS 1.107 + && (ljRand(p) & 0xF00) == 0xF00) { 1.108 + shuffleColumns(p); 1.109 + changed |= (1 << LJ_PF_HT) - 1; 1.110 + } 1.111 + } 1.112 + return changed; 1.113 +} 1.114 + 1.115 + 1.116 + 1.117 +const char hotlineRows[LJ_PF_HT] = 1.118 +{ 1.119 + 0, 0, 0, 0, 1, 1.120 + 0, 0, 0, 0, 2, 1.121 + 0, 0, 0, 3, 0, 1.122 + 0, 4, 0, 5, 6, 1.123 + 0, 0, 0, 0 1.124 +}; 1.125 + 1.126 +static const char garbageScoreTable[] = { 0, 0, 1, 2, 4 }; 1.127 +static const char tSpinGarbageScoreTable[] = { 0, 2, 4, 6, 6 }; 1.128 +static const unsigned char squareScoreTable[] = 1.129 + {1, 1, 1, 2, 3, 5, 8, 13, 1.130 + 21, 34, 55, 89, 144, 200}; 1.131 + 1.132 +static const char tdsScoreTable[] = {0, 1, 3, 5, 8}; 1.133 +static const char tdsTSScoreTable[] = {4, 8, 12, 16}; 1.134 +static const char nesScoreTable[] = {0, 4, 10, 30, 120}; 1.135 + 1.136 + 1.137 +/** 1.138 + * Computes the score and outgoing garbage for lines 1.139 + * and adds them to the player's total. 1.140 + * @param p The playfield 1.141 + * @param lines Bit array where 1 means a line clear on this row. 1.142 + */ 1.143 +void addLinesScore(LJField *p, LJBits lines) { 1.144 + const int nLines = countOnes(lines); 1.145 + int oldLines = p->nLinesThisPiece; 1.146 + int tdsSection = p->lines / 10 + 1; 1.147 + 1.148 + p->lines += nLines; 1.149 + if (updLevelAfterLines(p, nLines)) { 1.150 + p->sounds |= LJSND_SECTIONUP; 1.151 + } 1.152 + 1.153 + switch (p->scoreStyle) { 1.154 + 1.155 + case LJSCORE_TNT64: 1.156 + if (nLines < 1) { 1.157 + return; 1.158 + } 1.159 + for (int i = nLines; i > 0; --i) { 1.160 + if (oldLines > sizeof(squareScoreTable) - 1) { 1.161 + oldLines = sizeof(squareScoreTable) - 1; 1.162 + } 1.163 + p->score += 100 * squareScoreTable[oldLines++]; 1.164 + } break; 1.165 + 1.166 + case LJSCORE_NES: 1.167 + { 1.168 + int garbageLevel = (nLines > 4) ? 4 : nLines; 1.169 + int value = nesScoreTable[garbageLevel]; 1.170 + 1.171 + p->score += 10 * tdsSection * value; 1.172 + if (nLines >= 4) { 1.173 + p->sounds |= LJSND_SETB2B; 1.174 + } 1.175 + } break; 1.176 + 1.177 + case LJSCORE_TDS: 1.178 + { 1.179 + // Garbage based scoring system. 1.180 + int garbageLevel = (nLines > 4) ? 4 : nLines; 1.181 + int chain, value; 1.182 + 1.183 + if (p->isSpin) { 1.184 + chain = (nLines >= 1); 1.185 + value = tdsTSScoreTable[garbageLevel]; 1.186 + 1.187 + // TDS gives fewer points for a T-spin single 1.188 + // that involves a wall kick. 1.189 + if (p->isSpin == 2 && nLines < 2) { 1.190 + value >>= 2; 1.191 + } 1.192 + } else { 1.193 + chain = (nLines >= 4); 1.194 + value = tdsScoreTable[garbageLevel]; 1.195 + } 1.196 + if (chain && p->chain && nLines >= 1) { // b2b 1.197 + value = value * 3 / 2; 1.198 + } 1.199 + 1.200 + if (tdsSection > 20) { 1.201 + tdsSection = 20; 1.202 + } 1.203 + p->score += 100 * tdsSection * value; 1.204 + if (nLines >= 1) { 1.205 + if (chain) { 1.206 + p->sounds |= LJSND_SETB2B; 1.207 + if (p->chain) { 1.208 + p->sounds |= LJSND_B2B; 1.209 + } 1.210 + } 1.211 + p->chain = chain; 1.212 + } 1.213 + } break; 1.214 + 1.215 + case LJSCORE_LJ: 1.216 + case LJSCORE_LJ_NERF: 1.217 + if (nLines >= 1) { 1.218 + // Garbage based scoring system. 1.219 + int garbageLevel = (nLines > 4) ? 4 : nLines; 1.220 + unsigned int chain, garbage; 1.221 + 1.222 + if (p->isSpin) { 1.223 + chain = (nLines >= 1); 1.224 + garbage = tSpinGarbageScoreTable[garbageLevel]; 1.225 + if (p->scoreStyle == LJSCORE_LJ_NERF) { 1.226 + garbage /= 2; 1.227 + } 1.228 + garbage += (chain && p->chain); 1.229 + } else { 1.230 + chain = (nLines >= 4); 1.231 + garbage = garbageScoreTable[garbageLevel] 1.232 + + (chain && p->chain); 1.233 + } 1.234 + p->score += 100 * nLines + 200 * garbage; 1.235 + p->outGarbage += garbage; 1.236 + if (chain) { 1.237 + p->sounds |= LJSND_SETB2B; 1.238 + if (p->chain) { 1.239 + p->sounds |= LJSND_B2B; 1.240 + } 1.241 + } 1.242 + p->chain = chain; 1.243 + } break; 1.244 + 1.245 + case LJSCORE_HOTLINE: 1.246 + for (int y = 0; y < 24; ++y) { 1.247 + if (lines & (1 << y)) { 1.248 + p->score += 100 * hotlineRows[y]; 1.249 + } 1.250 + } break; 1.251 + 1.252 + } 1.253 + 1.254 + p->nLinesThisPiece += nLines; 1.255 +} 1.256 + 1.257 +/** 1.258 + * Puts a zigzag pattern of garbage into the playfield. 1.259 + * Useful as a test suite for calcZigzagGrade() in debrief.c. 1.260 + * @param p the playfield to set up 1.261 + * @param height the number of rows to set up 1.262 + */ 1.263 +void setupZigzagField(LJField *p, size_t height) { 1.264 + unsigned int hole = p->leftWall; 1.265 + int delta = 1; 1.266 + 1.267 + if (height > p->ceiling) { 1.268 + height = p->ceiling; 1.269 + } 1.270 + 1.271 + // set up example pattern 1.272 + for (size_t y = 0; y < height; ++y) { 1.273 + for (size_t x = p->leftWall; x < p->rightWall; ++x) { 1.274 + 1.275 + // A block should be in all cells of the row 1.276 + // except for the hole cell, which should be empty. 1.277 + if (x == hole) { 1.278 + p->b[y][x] = 0; 1.279 + } else { 1.280 + p->b[y][x] = 0x80; 1.281 + } 1.282 + } 1.283 + 1.284 + if (hole == p->rightWall - 1) { 1.285 + delta = -1; 1.286 + } else if (hole == p->leftWall) { 1.287 + delta = 1; 1.288 + } 1.289 + hole += delta; 1.290 + } 1.291 + 1.292 + // clear rows above pattern 1.293 + for (size_t y = height; y < LJ_PF_HT; ++y) { 1.294 + for (size_t x = p->leftWall; x < p->rightWall; ++x) { 1.295 + p->b[y][x] = 0; 1.296 + } 1.297 + } 1.298 +}