annotate src/speed.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 /* Speed curve tables 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 #include "lj.h"
paulo@0 25 #include "ljcontrol.h"
paulo@0 26
paulo@0 27 static const LJFixed initialGravity[LJSPD_N_CURVES] = {
paulo@0 28 [LJSPD_RHYTHMZERO] = 0,
paulo@0 29 [LJSPD_RHYTHM] = LJITOFIX(20)
paulo@0 30 };
paulo@0 31
paulo@0 32 /**
paulo@0 33 * The default speed curve for each gimmick. A negative value
paulo@0 34 * means that the gimmick uses the player's chosen speed curve.
paulo@0 35 */
paulo@0 36 static const signed char defaultSpeedCurve[LJGM_N_GIMMICKS] = {
paulo@0 37 [LJGM_ATYPE] = -1,
paulo@0 38 [LJGM_BTYPE] = -1,
paulo@0 39 [LJGM_ULTRA] = -1,
paulo@0 40 [LJGM_DRILL] = -1,
paulo@0 41 [LJGM_ITEMS] = -1,
paulo@0 42 [LJGM_BABY] = LJSPD_ZERO
paulo@0 43 };
paulo@0 44
paulo@0 45 /* The new speed curve code ****************************************/
paulo@0 46
paulo@0 47 #define SCGRAV(n, d) ((n) * 2048 / (d))
paulo@0 48
paulo@0 49 enum {
paulo@0 50 SPEEDTYPE_RHYTHM,
paulo@0 51 SPEEDTYPE_TGM,
paulo@0 52 SPEEDTYPE_LINES,
paulo@0 53 SPEEDTYPE_PIECES
paulo@0 54 };
paulo@0 55
paulo@0 56 typedef struct LJSpeedStep {
paulo@0 57 unsigned short level, gravity;
paulo@0 58 signed char are, das, lock, line;
paulo@0 59 } LJSpeedStep;
paulo@0 60
paulo@0 61 typedef struct LJSpeedCurve {
paulo@0 62 char name[32];
paulo@0 63 unsigned char type, sectionLen;
paulo@0 64 unsigned char nSteps;
paulo@0 65 const LJSpeedStep steps[];
paulo@0 66 } LJSpeedCurve;
paulo@0 67
paulo@0 68 const LJSpeedCurve scMaster = {
paulo@0 69 "Master",
paulo@0 70 SPEEDTYPE_TGM, 100,
paulo@0 71 33,
paulo@0 72 {
paulo@0 73 { 0, SCGRAV( 4,256),25,16,30,40},
paulo@0 74 { 30, SCGRAV( 6,256),25,16,30,40},
paulo@0 75 { 35, SCGRAV( 8,256),25,16,30,40},
paulo@0 76 { 40, SCGRAV( 10,256),25,16,30,40},
paulo@0 77 { 50, SCGRAV( 12,256),25,16,30,40},
paulo@0 78 { 60, SCGRAV( 16,256),25,16,30,40},
paulo@0 79 { 70, SCGRAV( 32,256),25,16,30,40},
paulo@0 80 { 80, SCGRAV( 48,256),25,16,30,40},
paulo@0 81 { 90, SCGRAV( 64,256),25,16,30,40},
paulo@0 82 {100, SCGRAV( 80,256),25,16,30,40},
paulo@0 83 {120, SCGRAV( 96,256),25,16,30,40},
paulo@0 84 {140, SCGRAV(112,256),25,16,30,40},
paulo@0 85 {160, SCGRAV(128,256),25,16,30,40},
paulo@0 86 {170, SCGRAV(144,256),25,16,30,40},
paulo@0 87 {200, SCGRAV( 4,256),25,16,30,40},
paulo@0 88 {220, SCGRAV( 32,256),25,16,30,40},
paulo@0 89 {230, SCGRAV( 64,256),25,16,30,40},
paulo@0 90 {233, SCGRAV( 96,256),25,16,30,40},
paulo@0 91 {236, SCGRAV(128,256),25,16,30,40},
paulo@0 92 {239, SCGRAV(160,256),25,16,30,40},
paulo@0 93 {243, SCGRAV(192,256),25,16,30,40},
paulo@0 94 {247, SCGRAV(224,256),25,16,30,40},
paulo@0 95 {251, SCGRAV( 1,1), 25,16,30,40},
paulo@0 96 {300, SCGRAV( 2,1), 25,16,30,40},
paulo@0 97 {330, SCGRAV( 3,1), 25,16,30,40},
paulo@0 98 {360, SCGRAV( 4,1), 25,16,30,40},
paulo@0 99 {400, SCGRAV( 5,1), 25,16,30,40},
paulo@0 100 {450, SCGRAV( 3,1), 25,16,30,40},
paulo@0 101 {500, SCGRAV(20,1), 25,10,30,25},
paulo@0 102 {600, SCGRAV(20,1), 25,10,30,16},
paulo@0 103 {700, SCGRAV(20,1), 16,10,30,12},
paulo@0 104 {800, SCGRAV(20,1), 12,10,30, 6},
paulo@0 105 {900, SCGRAV(20,1), 12, 8,17, 6}
paulo@0 106 }
paulo@0 107 };
paulo@0 108
paulo@0 109 const LJSpeedCurve scDeath = {
paulo@0 110 "Death",
paulo@0 111 SPEEDTYPE_TGM, 100,
paulo@0 112 6,
paulo@0 113 {
paulo@0 114 { 0, SCGRAV(20,1), 18,12,30, 8},
paulo@0 115 {100, SCGRAV(20,1), 14,12,26, 0},
paulo@0 116 {200, SCGRAV(20,1), 14,11,22, 0},
paulo@0 117 {300, SCGRAV(20,1), 8,10,18, 6},
paulo@0 118 {400, SCGRAV(20,1), 7, 8,15, 5},
paulo@0 119 {500, SCGRAV(20,1), 6, 8,15, 4}
paulo@0 120 }
paulo@0 121 };
paulo@0 122
paulo@0 123 const LJSpeedCurve scNES = {
paulo@0 124 "NES",
paulo@0 125 SPEEDTYPE_LINES, 10,
paulo@0 126 15,
paulo@0 127 {
paulo@0 128 { 0, SCGRAV(1,48), 10,-1, 0,30},
paulo@0 129 { 10, SCGRAV(1,43), 10,-1, 0,30},
paulo@0 130 { 20, SCGRAV(1,38), 10,-1, 0,30},
paulo@0 131 { 30, SCGRAV(1,33), 10,-1, 0,30},
paulo@0 132 { 40, SCGRAV(1,28), 10,-1, 0,30},
paulo@0 133 { 50, SCGRAV(1,23), 10,-1, 0,30},
paulo@0 134 { 60, SCGRAV(1,18), 10,-1, 0,30},
paulo@0 135 { 70, SCGRAV(1,13), 10,-1, 0,30},
paulo@0 136 { 80, SCGRAV(1, 8), 10,-1, 0,30},
paulo@0 137 { 90, SCGRAV(1, 6), 10,-1, 0,30},
paulo@0 138 {100, SCGRAV(1, 5), 10,-1, 0,30},
paulo@0 139 {130, SCGRAV(1, 4), 10,-1, 0,30},
paulo@0 140 {160, SCGRAV(1, 3), 10,-1, 0,30},
paulo@0 141 {190, SCGRAV(1, 2), 10,-1, 0,30},
paulo@0 142 {290, SCGRAV(1, 1), 10,-1, 0,30},
paulo@0 143 }
paulo@0 144 };
paulo@0 145
paulo@0 146 const LJSpeedCurve scGB = {
paulo@0 147 "Game Boy",
paulo@0 148 SPEEDTYPE_LINES, 10,
paulo@0 149 18,
paulo@0 150 {
paulo@0 151 { 0, SCGRAV(1,53), 0,-1, 0,90},
paulo@0 152 { 10, SCGRAV(1,49), 0,-1, 0,90},
paulo@0 153 { 20, SCGRAV(1,45), 0,-1, 0,90},
paulo@0 154 { 30, SCGRAV(1,41), 0,-1, 0,90},
paulo@0 155 { 40, SCGRAV(1,37), 0,-1, 0,90},
paulo@0 156 { 50, SCGRAV(1,33), 0,-1, 0,90},
paulo@0 157 { 60, SCGRAV(1,28), 0,-1, 0,90},
paulo@0 158 { 70, SCGRAV(1,22), 0,-1, 0,90},
paulo@0 159 { 80, SCGRAV(1,17), 0,-1, 0,90},
paulo@0 160 { 90, SCGRAV(1,11), 0,-1, 0,90},
paulo@0 161 {100, SCGRAV(1,10), 0,-1, 0,90},
paulo@0 162 {110, SCGRAV(1, 9), 0,-1, 0,90},
paulo@0 163 {120, SCGRAV(1, 8), 0,-1, 0,90},
paulo@0 164 {130, SCGRAV(1, 7), 0,-1, 0,90},
paulo@0 165 {140, SCGRAV(1, 6), 0,-1, 0,90},
paulo@0 166 {160, SCGRAV(1, 5), 0,-1, 0,90},
paulo@0 167 {180, SCGRAV(1, 4), 0,-1, 0,90},
paulo@0 168 {200, SCGRAV(1, 3), 0,-1, 0,90},
paulo@0 169 }
paulo@0 170 };
paulo@0 171
paulo@0 172 const LJSpeedCurve scZero = {
paulo@0 173 "Zero",
paulo@0 174 SPEEDTYPE_LINES, 10,
paulo@0 175 1,
paulo@0 176 {
paulo@0 177 { 0, SCGRAV(0, 1),-1,-1,40,-1}
paulo@0 178 }
paulo@0 179 };
paulo@0 180
paulo@0 181 const LJSpeedCurve scExponential = {
paulo@0 182 "Exponential",
paulo@0 183 SPEEDTYPE_PIECES, 60,
paulo@0 184 34,
paulo@0 185 {
paulo@0 186 { 0, SCGRAV( 1,60),-1,-1,40,-1 },
paulo@0 187 { 30, SCGRAV( 1,42),-1,-1,40,-1 },
paulo@0 188 { 60, SCGRAV( 2,60),-1,-1,40,-1 },
paulo@0 189 { 90, SCGRAV( 2,42),-1,-1,40,-1 },
paulo@0 190 {120, SCGRAV( 4,60),-1,-1,40,-1 },
paulo@0 191 {150, SCGRAV( 4,42),-1,-1,40,-1 },
paulo@0 192 {180, SCGRAV( 8,60),-1,-1,40,-1 },
paulo@0 193 {210, SCGRAV( 8,42),-1,-1,40,-1 },
paulo@0 194 {240, SCGRAV( 16,60),-1,-1,40,-1 },
paulo@0 195 {270, SCGRAV( 16,42),-1,-1,40,-1 },
paulo@0 196 {300, SCGRAV( 32,60),-1,-1,40,-1 },
paulo@0 197 {330, SCGRAV( 32,42),-1,-1,40,-1 },
paulo@0 198 {360, SCGRAV( 64,60),-1,-1,40,-1 },
paulo@0 199 {390, SCGRAV( 64,42),-1,-1,40,-1 },
paulo@0 200 {420, SCGRAV(128,60),-1,-1,40,-1 },
paulo@0 201 {450, SCGRAV(128,42),-1,-1,40,-1 },
paulo@0 202 {480, SCGRAV(256,60),-1,-1,40,-1 },
paulo@0 203 {510, SCGRAV(256,42),-1,-1,40,-1 },
paulo@0 204 {540, SCGRAV(512,60),-1,-1,40,-1 },
paulo@0 205 {570, SCGRAV(512,42),-1,-1,40,-1 },
paulo@0 206 {600, SCGRAV( 20, 1),-1,-1,40,-1 },
paulo@0 207 {630, SCGRAV( 20, 1),-1,-1,30,-1 },
paulo@0 208 {660, SCGRAV( 20, 1),-1,-1,24,-1 },
paulo@0 209 {690, SCGRAV( 20, 1),-1,-1,20,-1 },
paulo@0 210 {720, SCGRAV( 20, 1),-1,-1,17,-1 },
paulo@0 211 {750, SCGRAV( 20, 1),-1,-1,15,-1 },
paulo@0 212 {780, SCGRAV( 20, 1),-1,-1,13,-1 },
paulo@0 213 {810, SCGRAV( 20, 1),-1,-1,12,-1 },
paulo@0 214 {840, SCGRAV( 20, 1),-1,-1,11,-1 },
paulo@0 215 {870, SCGRAV( 20, 1),-1,-1,10,-1 },
paulo@0 216 {900, SCGRAV( 20, 1),-1,-1, 9,-1 },
paulo@0 217 {930, SCGRAV( 20, 1),-1,-1, 8,-1 },
paulo@0 218 {960, SCGRAV( 20, 1),-1,-1, 7,-1 },
paulo@0 219 {990, SCGRAV( 20, 1),-1,-1, 6,-1 }
paulo@0 220 }
paulo@0 221 };
paulo@0 222
paulo@0 223 static const LJSpeedCurve *const newSpeedCurves[LJSPD_N_CURVES] = {
paulo@0 224 [LJSPD_EXP] = &scExponential,
paulo@0 225 [LJSPD_ZERO] = &scZero,
paulo@0 226 [LJSPD_TGM] = &scMaster,
paulo@0 227 [LJSPD_DEATH] = &scDeath,
paulo@0 228 [LJSPD_DEATH300] = &scDeath,
paulo@0 229 [LJSPD_NES] = &scNES,
paulo@0 230 [LJSPD_GB] = &scGB,
paulo@0 231 [LJSPD_GBHEART] = &scGB,
paulo@0 232 };
paulo@0 233
paulo@0 234 /**
paulo@0 235 * Performs a fast binary search of a speed step table.
paulo@0 236 */
paulo@0 237 static const LJSpeedStep *getSpeedStep(const LJSpeedStep *steps,
paulo@0 238 size_t nSteps, int level) {
paulo@0 239 unsigned int lo = 0;
paulo@0 240 unsigned int hi = nSteps;
paulo@0 241
paulo@0 242 while (hi - lo > 1) {
paulo@0 243 size_t mid = (hi + lo) / 2;
paulo@0 244 unsigned int here = steps[mid].level;
paulo@0 245 if (here == level) {
paulo@0 246 return &(steps[mid]);
paulo@0 247 } else if (here < level) {
paulo@0 248 lo = mid;
paulo@0 249 } else {
paulo@0 250 hi = mid;
paulo@0 251 }
paulo@0 252 }
paulo@0 253 return &(steps[lo]);
paulo@0 254 }
paulo@0 255
paulo@0 256 /**
paulo@0 257 * Updates the level after each piece has retired.
paulo@0 258 * @return nonzero iff a new section has happened
paulo@0 259 */
paulo@0 260 int updLevelAfterPiece(LJField *p) {
paulo@0 261 int curveID = p->speedState.curve;
paulo@0 262 const LJSpeedCurve *curve = newSpeedCurves[curveID];
paulo@0 263 if (!curve) {
paulo@0 264 return 0;
paulo@0 265 }
paulo@0 266 unsigned int sectionLen = curve->sectionLen;
paulo@0 267 unsigned int oldLevel = p->speedState.level;
paulo@0 268 unsigned int oldSection = oldLevel / sectionLen;
paulo@0 269 unsigned int oldSectionPos = oldLevel % sectionLen;
paulo@0 270
paulo@0 271 switch (curve->type) {
paulo@0 272 case SPEEDTYPE_TGM:
paulo@0 273 if (oldSectionPos + 1 >= sectionLen) {
paulo@0 274 return 0;
paulo@0 275 }
paulo@0 276 // otherwise fall through to +1 per piece
paulo@0 277
paulo@0 278 case SPEEDTYPE_PIECES:
paulo@0 279 p->speedState.level = oldLevel + 1;
paulo@0 280 break;
paulo@0 281
paulo@0 282 default:
paulo@0 283 return 0;
paulo@0 284 }
paulo@0 285
paulo@0 286 unsigned int newSection = p->speedState.level / sectionLen;
paulo@0 287 return newSection > oldSection;
paulo@0 288 }
paulo@0 289
paulo@0 290 /**
paulo@0 291 * Updates the level after lines have been cleared.
paulo@0 292 * @return nonzero iff a new section has happened
paulo@0 293 */
paulo@0 294 int updLevelAfterLines(LJField *p, unsigned int nLines) {
paulo@0 295 int curveID = p->speedState.curve;
paulo@0 296 const LJSpeedCurve *curve = newSpeedCurves[curveID];
paulo@0 297 if (!curve) {
paulo@0 298 return 0;
paulo@0 299 }
paulo@0 300 unsigned int sectionLen = curve->sectionLen;
paulo@0 301 unsigned int oldLevel = p->speedState.level;
paulo@0 302 unsigned int oldSection = oldLevel / sectionLen;
paulo@0 303
paulo@0 304 switch (curve->type) {
paulo@0 305 case SPEEDTYPE_TGM:
paulo@0 306 case SPEEDTYPE_LINES:
paulo@0 307 p->speedState.level = oldLevel + nLines;
paulo@0 308 break;
paulo@0 309
paulo@0 310 default:
paulo@0 311 return 0;
paulo@0 312 }
paulo@0 313
paulo@0 314 unsigned int newSection = p->speedState.level / sectionLen;
paulo@0 315 return newSection > oldSection;
paulo@0 316 }
paulo@0 317
paulo@0 318 void setSpeedNew(LJField *p, LJControl *c) {
paulo@0 319 int curveID = p->speedState.curve;
paulo@0 320 const LJSpeedCurve *curve = newSpeedCurves[curveID];
paulo@0 321 if (!curve) {
paulo@0 322 return;
paulo@0 323 }
paulo@0 324 const LJSpeedStep *step =
paulo@0 325 getSpeedStep(curve->steps, curve->nSteps, p->speedState.level);
paulo@0 326
paulo@0 327 p->speed.gravity = step->gravity << 5;
paulo@0 328
paulo@0 329 p->speed.entryDelay = step->are;
paulo@0 330 if (p->speed.entryDelay > p->areStyle) {
paulo@0 331 p->speed.entryDelay = p->areStyle;
paulo@0 332 }
paulo@0 333
paulo@0 334 if (step->das > 0 && c->dasDelay > step->das) {
paulo@0 335 c->dasDelay = step->das;
paulo@0 336 }
paulo@0 337
paulo@0 338 if (p->setLockDelay >= 128) {
paulo@0 339 p->speed.lockDelay = 127;
paulo@0 340 } else if (p->setLockDelay > 0) {
paulo@0 341 p->speed.lockDelay = p->setLockDelay;
paulo@0 342 } else if (step->lock > 0) {
paulo@0 343 p->speed.lockDelay = step->lock;
paulo@0 344 }
paulo@0 345
paulo@0 346 if (p->setLineDelay > 0) {
paulo@0 347 p->speed.lineDelay = p->setLineDelay;
paulo@0 348 } else if (step->line >= 0) {
paulo@0 349 p->speed.lineDelay = step->line;
paulo@0 350 } else {
paulo@0 351 p->speed.lineDelay = p->speed.entryDelay;
paulo@0 352 }
paulo@0 353 }
paulo@0 354
paulo@0 355
paulo@0 356
paulo@0 357 /* Old speed curve system is below this line ***********************/
paulo@0 358
paulo@0 359
paulo@0 360
paulo@0 361 void initSpeed(LJField *p) {
paulo@0 362 if (defaultSpeedCurve[p->gimmick] >= 0) {
paulo@0 363 p->speedState.curve = defaultSpeedCurve[p->gimmick];
paulo@0 364 }
paulo@0 365 p->speed.gravity = initialGravity[p->speedState.curve];
paulo@0 366
paulo@0 367 switch (p->speedState.curve) {
paulo@0 368 case LJSPD_RHYTHM:
paulo@0 369 case LJSPD_RHYTHMZERO:
paulo@0 370 p->speedState.level = 60;
paulo@0 371 p->bpmCounter = 0;
paulo@0 372 p->speedupCounter = 0;
paulo@0 373 break;
paulo@0 374 case LJSPD_TGM:
paulo@0 375 case LJSPD_DEATH:
paulo@0 376 case LJSPD_EXP:
paulo@0 377 p->speedState.level = -1;
paulo@0 378 break;
paulo@0 379 case LJSPD_DEATH300:
paulo@0 380 p->speedState.level = 300;
paulo@0 381 break;
paulo@0 382 case LJSPD_GBHEART:
paulo@0 383 p->speedState.level = 100;
paulo@0 384 break;
paulo@0 385 default:
paulo@0 386 p->speedState.level = 0;
paulo@0 387 break;
paulo@0 388 }
paulo@0 389 }
paulo@0 390
paulo@0 391
paulo@0 392 void setSpeed(LJField *p, LJControl *c) {
paulo@0 393 p->speed.entryDelay = p->areStyle;
paulo@0 394
paulo@0 395 // Default line delay is equal to the entry delay,
paulo@0 396 // but speed curves and setLineDelay can override this
paulo@0 397 p->speed.lineDelay = p->speed.entryDelay;
paulo@0 398 switch (p->speedState.curve) {
paulo@0 399
paulo@0 400 case LJSPD_RHYTHM:
paulo@0 401 case LJSPD_RHYTHMZERO:
paulo@0 402 // If we've already banked five pieces' worth of time,
paulo@0 403 // add 20 points instead of banking another.
paulo@0 404 if (p->bpmCounter <= -18000) {
paulo@0 405 // removed in 0.21 because other curves don't reward for drops
paulo@0 406 // p->score += 20;
paulo@0 407 } else {
paulo@0 408 p->bpmCounter -= 3600; // number of frames per minute
paulo@0 409 }
paulo@0 410 p->speed.lockDelay = 3600 / p->speedState.level;
paulo@0 411 p->speed.gravity = (p->speedState.curve == LJSPD_RHYTHM) ? ljitofix(20) : 0;
paulo@0 412 break;
paulo@0 413
paulo@0 414 default:
paulo@0 415 if (updLevelAfterPiece(p)) {
paulo@0 416 p->sounds |= LJSND_SECTIONUP;
paulo@0 417 }
paulo@0 418 setSpeedNew(p, c);
paulo@0 419 break;
paulo@0 420 }
paulo@0 421
paulo@0 422 if (p->setLockDelay >= 128) {
paulo@0 423 p->speed.lockDelay = 127;
paulo@0 424 } else if (p->setLockDelay > 0) {
paulo@0 425 p->speed.lockDelay = p->setLockDelay;
paulo@0 426 }
paulo@0 427 if (p->setLineDelay > 0) {
paulo@0 428 p->speed.lineDelay = p->setLineDelay;
paulo@0 429 }
paulo@0 430 }
paulo@0 431