view src/options.c @ 3:17286938e22a

change DS alt. rotate key to rotate twice
author paulo@localhost
date Wed, 08 Apr 2009 21:50:13 -0700
parents
children
line source
1 /* options code for LOCKJAW, an implementation of the Soviet Mind Game
3 Copyright (C) 2007-2008 Damian Yerrick <tepples+lj@spamcop.net>
5 This work is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 Original game concept and design by Alexey Pajitnov.
20 The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg,
21 or The Tetris Company LLC.
23 */
26 #include <string.h>
27 #include <stdio.h>
28 #include "options.h"
29 #include "ljplay.h"
30 #include "ljlocale.h"
32 #ifdef HAS_FPU
33 #define siprintf sprintf
34 #define MIN_SHADOW 0
35 #define N_SHADOWS LJSHADOW_N_STYLES
36 #define FACTORY_SHADOW 0
37 #include "ljpc.h"
38 #else
39 #define MIN_SHADOW LJSHADOW_COLORED
40 #define N_SHADOWS (LJSHADOW_N_STYLES - 2)
41 #define FACTORY_SHADOW MIN_SHADOW
42 #endif
44 const FourCC optionsBoolNames[2] = {
45 {"Off"}, {"On"}
46 };
48 const FourCC gimmickNames[LJGM_N_GIMMICKS] = {
49 {"Mara"}, {"Line"}, {"Time"}, {"DrlA"}, {"Item"}, {"Keys"}
50 };
52 const FourCC optionsGarbageNames[LJGARBAGE_N_STYLES] = {
53 {"Off"}, {"Lv.1"}, {"Lv.2"}, {"Lv.3"}, {"Lv.4"},
54 {"HRD"}, {"DrlG"}, {"Zigz"}
55 };
57 const FourCC optionsScoringNames[LJSCORE_N_STYLES] = {
58 {"LJ"}, {"Fibo"}, {"HotL"}, {"TDSl"}, {"NESl"}, {"LJns"}
59 };
61 const FourCC optionsKickLimitNames[N_KICK_LIMITS] = {
62 {"Off"}, {"1"}, {"2"}, {"3"}, {"4"}, {"5"}, {"Inf"}
63 };
65 const FourCC optionsSpeedCurveNames[] = {
66 {"Zero"}, {"Rhy0"}, {"Exp"}, {"Rh20"},
67 {"TGM2"}, {"TAPD"}, {"TAD3"},
68 {"NESc"}, {"GBc"}, {"GBHL"}
69 };
71 static const FourCC optionsWindowedNames[2] = {
72 {"FulS"}, {"Wind"}
73 };
75 const FourCC optionsShadowNames[LJSHADOW_N_STYLES] = {
76 {"tl25"}, {"tl50"}, {"tlsC"}, {"tlsM"}, {"Off"}, {"invF"}
77 };
79 const FourCC optionsLockDelayNames[2] = {
80 {"SBSC"}, {"Inf"}
81 };
83 const FourCC optionsSBSCNames[2] = {
84 {"SBSC"}, {.i=0}
85 };
87 const FourCC optionsTspinNames[] = {
88 {"Off"}, {"Imob"}, {"CrnT"}, {"WKT"}
89 };
91 const FourCC optionsLockdownNames[] = {
92 {"OLD"}, {"EntR"}, {"StpR"}, {"MovR"}
93 };
95 const FourCC optionsHoldStyleNames[] = {
96 {"Off"}, {"HldE"}, {"HldR"}, {"HldN"}
97 };
99 const FourCC optionsDropScoringNames[LJDROP_N_STYLES] = {
100 {"None"}, {"ConD"}, {"S1H1"}, {"S1H2"}
101 };
103 const FourCC optionsGravNames[] = {
104 {"Naiv"}, {"Stky"}, {"byCo"}, {"Casc"}
105 };
107 const FourCC optionsPieceSetNames[LJRAND_N_PIECE_SETS] = {
108 {"IJLO"},
109 {"JLOT"},
110 {"SZSZ"},
111 {"IIII"},
112 {"AllP"},
113 {"TTTT"}
114 };
116 const FourCC optionsRandNames[LJRAND_N_RANDS] = {
117 {"Unif"},
118 {"Bag"},
119 {"Bag+"},
120 {"Bag2"},
121 {"Hist"},
122 {"His6"}
123 };
125 const FourCC optionsLineDelayNames[] = {
126 {"SBSC"}, {.i=0}
127 };
129 const FourCC optionsZangiNames[] = {
130 {"Slid"}, {"Lock"}, {"LocU"}
131 };
133 const FourCC optionsGluingNames[] = {
134 {"Off"}, {"Squ"}, {"Stky"}, {"byCo"}
135 };
137 const FourCC optionsRotNames[N_ROTATION_SYSTEMS] = {
138 {"SRS"}, {"Sega"}, {"ARS"}, {"Tngn"},
139 {"NRSR"}, {"NRSL"}, {"TOD4"}, {"TDX"}
140 };
143 const OptionsLine commonOptionsMenu[] = {
144 {{"gimm"}, gimmickNames,
145 0, LJGM_N_GIMMICKS, 0 },
146 {{"pfw"}, NULL,
147 4, LJ_PF_WID - 4 + 1, 10 },
148 {{"pfh"}, NULL,
149 8, LJ_PF_VIS_HT - 8 + 1, LJ_PF_VIS_HT },
150 {{"vzsp"}, optionsBoolNames,
151 0, 2, 1 },
152 {{"spdc"}, optionsSpeedCurveNames,
153 0, LJSPD_N_CURVES, LJSPD_EXP },
154 {{"are"}, NULL,
155 0, 121, 0, OPTSTYLE_FRAMES },
156 {{"piec"}, optionsPieceSetNames,
157 0, LJRAND_N_PIECE_SETS, LJRAND_4BLK },
158 {{"rand"}, optionsRandNames,
159 0, LJRAND_N_RANDS, LJRAND_BAG },
161 {{"hold"}, optionsHoldStyleNames,
162 0, LJHOLD_N_STYLES, LJHOLD_EMPTY },
163 {{"rots"}, optionsRotNames,
164 0, N_ROTATION_SYSTEMS, 0 },
165 {{"upkl"}, optionsKickLimitNames,
166 0, N_KICK_LIMITS, N_KICK_LIMITS - 1 },
167 {{"lock"}, optionsLockdownNames,
168 0, LJLOCK_N_STYLES, LJLOCK_MOVE },
169 {{"sldt"}, optionsLockDelayNames,
170 0, 129, 0, OPTSTYLE_FRAMES },
171 {{"deep"}, optionsBoolNames,
172 0, 2, 0 },
174 {{"clrd"}, optionsSBSCNames,
175 0, 121, 0, OPTSTYLE_FRAMES },
176 {{"grav"}, optionsGravNames,
177 0, LJGRAV_N_ALGOS, LJGRAV_NAIVE },
178 {{"glue"}, optionsGluingNames,
179 0, LJGLUING_N_STYLES, LJGLUING_NONE },
180 {{"lsco"}, optionsScoringNames,
181 0, LJSCORE_N_STYLES },
182 {{"dsco"}, optionsDropScoringNames,
183 0, LJDROP_N_STYLES, 0 },
184 {{"tspn"}, optionsTspinNames,
185 0, LJTS_N_ALGOS, LJTS_TDS },
186 {{"garb"}, optionsGarbageNames,
187 0, LJGARBAGE_N_STYLES, 0 },
189 {{"dasd"}, NULL,
190 1, 120, 10, OPTSTYLE_FRAMES },
191 {{"dass"}, NULL,
192 0, 10, 1, OPTSTYLE_FRAC_G },
193 {{"idas"}, optionsBoolNames,
194 0, 2, 1 },
195 {{"irs"}, optionsBoolNames,
196 0, 2, 1 },
197 {{"8way"}, optionsBoolNames,
198 0, 2, 0 },
200 {{"sfds"}, NULL,
201 1, 3, 1, OPTSTYLE_FRAC_G },
202 {{"sfdl"}, optionsZangiNames,
203 0, LJZANGI_N_STYLES, LJZANGI_SLIDE },
204 {{"hrdl"}, optionsZangiNames,
205 0, LJZANGI_N_STYLES, LJZANGI_LOCK },
207 {{"tls"}, optionsShadowNames + MIN_SHADOW,
208 MIN_SHADOW, N_SHADOWS, FACTORY_SHADOW },
209 {{"invs"}, optionsBoolNames,
210 0, 2, 0 },
211 {{"next"}, NULL,
212 0, LJ_NEXT_PIECES + 1, 6 },
213 {{"srph"}, optionsBoolNames,
214 0, 2, 1 },
216 #ifdef HAS_FPU
217 {{"inpv"}, NULL,
218 0, LJ_NEXT_PIECES + 1, 0 },
219 {{"mblr"}, optionsBoolNames,
220 0, 2, 1 },
221 {{"lidp"}, optionsBoolNames,
222 0, 2, 1 },
223 {{"rec"}, optionsBoolNames,
224 0, 2, 0 },
225 {{"wndw"}, optionsWindowedNames,
226 0, 2, 1 }
227 #endif
228 };
230 const OptionsPage optionsPages[] = {
231 {OPTIONS_GIMMICK, "Game"},
232 {OPTIONS_WIDTH, "Rules: Well"},
233 {OPTIONS_HOLD_PIECE, "Rules: Movement"},
234 {OPTIONS_LINE_DELAY, "Rules: Line clear"},
235 {OPTIONS_SIDEWAYS_DELAY, "Control: Movement"},
236 {OPTIONS_SOFT_DROP_SPEED, "Control: Drop"},
237 {OPTIONS_SHADOW, "Display"},
238 #ifdef HAS_FPU
239 {OPTIONS_MENU_LEN, "PC"},
240 {PC_OPTIONS_MENU_LEN, NULL}
241 #else
242 {OPTIONS_MENU_LEN, NULL}
243 #endif
244 };
246 void setOptionsValueToFourCC(char *dst, FourCC f) {
247 const char *valueName = ljGetFourCCName(f);
248 if (valueName) {
249 strncpy(dst,
250 valueName,
251 OPTIONS_VALUE_LEN - 1);
252 dst[OPTIONS_VALUE_LEN - 1] = 0;
253 } else {
254 strncpy(dst, f.c, 4);
255 dst[4] = 0;
256 }
257 }
259 struct DisabledOption {
260 unsigned char name;
261 unsigned char value;
262 unsigned char name2;
263 char reason[45];
264 };
266 /*
267 Semantics:
268 If option name is set to value,
269 then gray out option name2 and draw its value as reason.
270 */
271 #define N_DISABLED_OPTIONS 12
272 const struct DisabledOption disabledOptions[N_DISABLED_OPTIONS] = {
273 { OPTIONS_LOCKDOWN, LJLOCK_NOW,
274 OPTIONS_SOFT_DROP, "Lockdown is immediate" },
275 { OPTIONS_LOCKDOWN, LJLOCK_NOW,
276 OPTIONS_HARD_DROP, "Lockdown is immediate" },
277 { OPTIONS_LOCKDOWN, LJLOCK_NOW,
278 OPTIONS_LOCK_DELAY, "Lockdown is immediate" },
279 { OPTIONS_LOCK_DELAY, 128,
280 OPTIONS_LOCKDOWN, "Lockdown is manual" },
281 { OPTIONS_SPEED_CURVE, LJSPD_DEATH,
282 OPTIONS_SOFT_DROP_SPEED,"Death: pieces land instantly" },
283 { OPTIONS_SPEED_CURVE, LJSPD_RHYTHM,
284 OPTIONS_SOFT_DROP_SPEED,"Rhythm: pieces land instantly" },
285 { OPTIONS_SPEED_CURVE, LJSPD_DEATH,
286 OPTIONS_SMOOTH_GRAVITY, "Death: pieces land instantly" },
287 { OPTIONS_SPEED_CURVE, LJSPD_RHYTHM,
288 OPTIONS_SMOOTH_GRAVITY, "Rhythm: pieces land instantly" },
289 { OPTIONS_SPEED_CURVE, LJSPD_DEATH,
290 OPTIONS_SOFT_DROP, "Death: pieces land instantly" },
291 { OPTIONS_SPEED_CURVE, LJSPD_RHYTHM,
292 OPTIONS_SOFT_DROP, "Rhythm: pieces land instantly" },
293 { OPTIONS_SPEED_CURVE, LJSPD_DEATH,
294 OPTIONS_HARD_DROP, "Death: pieces land instantly" },
295 { OPTIONS_SPEED_CURVE, LJSPD_RHYTHM,
296 OPTIONS_HARD_DROP, "Rhythm: pieces land instantly" },
297 };
299 const char *isDisabledOption(const unsigned char *prefs, int y) {
300 for (int i = 0; i < N_DISABLED_OPTIONS; ++i) {
301 if (y == disabledOptions[i].name2) {
302 int name = disabledOptions[i].name;
303 int value = disabledOptions[i].value;
305 if (prefs[name] == value) {
306 return disabledOptions[i].reason;
307 }
308 }
309 }
310 return NULL;
311 }
314 const char *getOptionsValueStr(char *dst, int line, int value) {
315 const OptionsLine *l = &(commonOptionsMenu[line]);
316 FourCC f = {.i = 0};
317 const char *desc = NULL;
319 switch (l->style) {
320 case OPTSTYLE_DEFAULT:
321 if (l->valueNames) {
322 f = l->valueNames[value - l->minValue];
323 } else {
324 siprintf(dst, "%d", value);
325 }
326 break;
328 case OPTSTYLE_FRAMES:
329 if (l->valueNames
330 && value == l->minValue
331 && l->valueNames[0].i) {
333 // override first with name 0
334 f = l->valueNames[0];
335 } else if (l->valueNames
336 && value == l->minValue
337 + l->nValues - 1
338 && l->valueNames[1].i) {
340 // override second with name 1
341 f = l->valueNames[1];
342 } else {
343 if (value >= 60) {
344 int ds = value / 6;
345 int s = ds / 10;
346 ds -= s * 10;
347 siprintf(dst, "%d/60 s (%d.%d s)", value, s, ds);
348 } else {
349 int ms = value * 50 / 3;
350 siprintf(dst, "%d/60 s (%d ms)", value, ms);
351 }
352 } break;
354 case OPTSTYLE_FRAC_G:
355 if (value > 6) {
356 int dHz = 600 / value;
357 int Hz = dHz / 10;
358 dHz -= Hz * 10;
359 siprintf(dst, "1/%dG (%d.%d Hz)", value, Hz, dHz);
360 } else if (value > 0) {
361 if (value > 1) {
362 dst[0] = '1';
363 dst[1] = '/';
364 dst += 2;
365 }
366 siprintf(dst, "%dG (%d Hz)", value, 60 / value);
367 } else {
368 strcpy(dst, "Instant");
369 }
370 break;
372 default:
373 strncpy(dst, "Unknown option style.", OPTIONS_VALUE_LEN - 1);
374 dst[OPTIONS_VALUE_LEN - 1] = 0;
375 break;
376 }
378 /* If we have a fourCC, use it. */
379 if (f.i != 0) {
380 setOptionsValueToFourCC(dst, f);
381 desc = ljGetFourCCDesc(f);
382 }
383 return desc;
384 }
386 void unpackCommonOptions(LJView *v, const unsigned char *prefs) {
387 if (prefs[OPTIONS_GIMMICK] < 255)
388 v->field->gimmick = prefs[OPTIONS_GIMMICK];
389 if (prefs[OPTIONS_WIDTH] < 255) {
390 int width = prefs[OPTIONS_WIDTH];
391 v->field->leftWall = (LJ_PF_WID - width) / 2;
392 v->field->rightWall = v->field->leftWall + width;
393 }
394 if (prefs[OPTIONS_HEIGHT] < 255)
395 v->field->ceiling = prefs[OPTIONS_HEIGHT];
396 if (prefs[OPTIONS_ENTER_ABOVE] < 255)
397 v->field->enterAbove = prefs[OPTIONS_ENTER_ABOVE];
398 if (prefs[OPTIONS_SPEED_CURVE] < 255)
399 v->field->speedState.curve = prefs[OPTIONS_SPEED_CURVE];
400 if (prefs[OPTIONS_ENTRY_DELAY] < 255)
401 v->field->areStyle = prefs[OPTIONS_ENTRY_DELAY];
402 if (prefs[OPTIONS_PIECE_SET] < 255)
403 v->field->pieceSet = prefs[OPTIONS_PIECE_SET];
404 if (prefs[OPTIONS_RANDOMIZER] < 255)
405 v->field->randomizer = prefs[OPTIONS_RANDOMIZER];
407 if (prefs[OPTIONS_ROTATION_SYSTEM] < 255)
408 v->field->rotationSystem = prefs[OPTIONS_ROTATION_SYSTEM];
409 if (prefs[OPTIONS_FLOOR_KICKS] < 255)
410 v->field->maxUpwardKicks = prefs[OPTIONS_FLOOR_KICKS] == N_KICK_LIMITS - 1
411 ? 128
412 : prefs[OPTIONS_FLOOR_KICKS];
413 if (prefs[OPTIONS_HOLD_PIECE] < 255)
414 v->field->holdStyle = prefs[OPTIONS_HOLD_PIECE];
415 if (prefs[OPTIONS_LOCKDOWN] < 255)
416 v->field->lockReset = prefs[OPTIONS_LOCKDOWN];
417 if (prefs[OPTIONS_LOCK_DELAY] < 255)
418 v->field->setLockDelay = prefs[OPTIONS_LOCK_DELAY];
419 if (prefs[OPTIONS_BOTTOM_BLOCKS] < 255)
420 v->field->bottomBlocks = prefs[OPTIONS_BOTTOM_BLOCKS];
422 if (prefs[OPTIONS_LINE_DELAY] < 255)
423 v->field->setLineDelay = prefs[OPTIONS_LINE_DELAY];
424 if (prefs[OPTIONS_T_SPIN] < 255)
425 v->field->tSpinAlgo = prefs[OPTIONS_T_SPIN];
426 if (prefs[OPTIONS_CLEAR_GRAVITY] < 255)
427 v->field->clearGravity = prefs[OPTIONS_CLEAR_GRAVITY];
428 if (prefs[OPTIONS_GLUING] < 255)
429 v->field->gluing = prefs[OPTIONS_GLUING];
430 if (prefs[OPTIONS_SCORING] < 255)
431 v->field->scoreStyle = prefs[OPTIONS_SCORING];
432 if (prefs[OPTIONS_DROP_SCORING] < 255)
433 v->field->dropScoreStyle = prefs[OPTIONS_DROP_SCORING];
434 if (prefs[OPTIONS_GARBAGE] < 255)
435 v->field->garbageStyle = prefs[OPTIONS_GARBAGE];
437 if (prefs[OPTIONS_SIDEWAYS_DELAY] < 255)
438 v->control->dasDelay = prefs[OPTIONS_SIDEWAYS_DELAY];
439 if (prefs[OPTIONS_SIDEWAYS_SPEED] < 255)
440 v->control->dasSpeed = prefs[OPTIONS_SIDEWAYS_SPEED];
441 if (v->control->dasDelay < v->control->dasSpeed) {
442 v->control->dasDelay = v->control->dasSpeed;
443 }
444 if (prefs[OPTIONS_INITIAL_SIDEWAYS] < 255)
445 v->control->initialDAS = prefs[OPTIONS_INITIAL_SIDEWAYS];
446 if (prefs[OPTIONS_IRS] < 255)
447 v->control->initialRotate = prefs[OPTIONS_IRS];
448 if (prefs[OPTIONS_DIAGONAL_MOTION] < 255)
449 v->control->allowDiagonals = prefs[OPTIONS_DIAGONAL_MOTION];
450 if (prefs[OPTIONS_SOFT_DROP_SPEED] < 255)
451 v->control->softDropSpeed = prefs[OPTIONS_SOFT_DROP_SPEED] - 1;
452 if (prefs[OPTIONS_SOFT_DROP] < 255)
453 v->control->softDropLock = prefs[OPTIONS_SOFT_DROP];
454 if (prefs[OPTIONS_HARD_DROP] < 255)
455 v->control->hardDropLock = prefs[OPTIONS_HARD_DROP];
457 if (prefs[OPTIONS_SHADOW] < 255)
458 v->hideShadow = prefs[OPTIONS_SHADOW];
459 if (prefs[OPTIONS_HIDE_PF] < 255)
460 v->hidePF = prefs[OPTIONS_HIDE_PF];
461 if (prefs[OPTIONS_NEXT_PIECES] < 255)
462 v->nextPieces = prefs[OPTIONS_NEXT_PIECES];
463 if (prefs[OPTIONS_SMOOTH_GRAVITY] < 255)
464 v->smoothGravity = prefs[OPTIONS_SMOOTH_GRAVITY];
465 }
467 void initOptions(unsigned char *prefs) {
468 for (int i = 0; i < OPTIONS_MENU_LEN; ++i) {
469 prefs[i] = commonOptionsMenu[i].startValue;
470 }
471 }
473 LJBits menuReadPad(void);
474 void vsync(void);
476 void options(LJView *v, unsigned char *prefs) {
477 int page = 0, y = 0;
478 int redraw = 2;
479 int done = 0;
480 LJBits lastKeys = ~0;
481 int dasDir = 0;
482 int dasCounter = 0;
483 const OptionsLine const *optionsMenu = commonOptionsMenu;
484 int lastClock = getTime();
485 int erase = -1;
487 optionsWinInit();
489 while (!done) {
490 if (redraw) {
491 if (redraw == 2) {
492 redraw = 1;
493 optionsDrawPage(page, prefs);
494 }
495 if (redraw == 1) {
496 if (erase >= 0) {
497 vsync();
498 optionsDrawRow(prefs,
499 erase - optionsPages[page].start,
500 erase, prefs[erase], 0);
501 erase = -1;
502 }
503 optionsDrawRow(prefs,
504 y - optionsPages[page].start,
505 y, prefs[y], 1);
506 redraw = 0;
507 }
508 } else {
509 optionsIdle();
510 }
512 LJBits keys = menuReadPad();
513 LJBits sounds = 0;
514 int lastY = y;
515 LJBits newKeys = keys & ~lastKeys;
516 LJBits dasKeys = 0;
518 if (getTime() != lastClock) {
519 // Handle DAS within options (fixed at 250 ms 30 Hz)
520 lastClock = getTime();
521 if (keys & dasDir) {
522 ++dasCounter;
523 if (dasCounter >= 15) {
524 dasCounter -= 2;
525 dasKeys = dasDir;
526 }
527 } else {
528 dasCounter = 0;
529 }
530 }
532 if (newKeys & VKEY_UP
533 || ((dasKeys & VKEY_UP)
534 && y > optionsPages[page].start)) {
535 dasDir = VKEY_UP;
536 if (y <= 0) {
537 while (optionsPages[page].name) {
538 ++page;
539 }
540 y = optionsPages[page].start - 1;
541 } else {
542 --y;
543 }
544 }
545 if (newKeys & VKEY_DOWN
546 || ((dasKeys & VKEY_DOWN)
547 && y < optionsPages[page + 1].start - 1)) {
548 dasDir = VKEY_DOWN;
549 ++y;
550 if (y >= optionsPages[page + 1].start
551 && !optionsPages[page + 1].name) {
552 y = 0;
553 }
554 }
556 if (!isDisabledOption(prefs, y)) {
557 if ((newKeys | dasKeys) & VKEY_RIGHT) {
558 int num = prefs[y] + 1;
560 if (num >= optionsMenu[y].minValue + optionsMenu[y].nValues) {
561 prefs[y] = optionsMenu[y].minValue;
562 } else {
563 prefs[y] = num;
564 }
566 sounds |= LJSND_ROTATE;
567 // XXX: need to redraw the whole box (redraw = 2)
568 // if options have become enabled or disabled
569 redraw = 1;
570 dasDir = VKEY_RIGHT;
571 }
573 if ((newKeys | dasKeys) & VKEY_LEFT) {
574 int num = prefs[y] - 1;
576 if (num < optionsMenu[y].minValue) {
577 prefs[y] = optionsMenu[y].minValue + optionsMenu[y].nValues - 1;
578 } else {
579 prefs[y] = num;
580 }
582 sounds |= LJSND_ROTATE;
583 redraw = 1;
584 dasDir = VKEY_LEFT;
585 }
586 }
588 // Rotate left: Go to the top of the previous page if it exists.
589 if (newKeys & VKEY_ROTL) {
590 if (page > 0) {
591 y = optionsPages[page - 1].start;
592 } else {
593 y = 0;
594 }
595 }
597 // Rotate right: If on last page, finish;
598 // otherwise, go to the top of the next page.
599 if (newKeys & VKEY_ROTR) {
600 if (!optionsPages[page + 1].name) {
601 done = 1;
602 } else {
603 y = optionsPages[page + 1].start;
604 }
605 }
607 // Start: finish
608 if (newKeys & VKEY_START) {
609 done = 1;
610 }
612 if (lastY != y) {
613 sounds |= LJSND_SHIFT;
615 // calculate which page the cursor has moved to
616 int lastPage = page;
617 while (y < optionsPages[page].start) {
618 --page;
619 }
620 while (y >= optionsPages[page + 1].start) {
621 ++page;
622 }
624 if (lastPage == page) {
625 erase = lastY;
626 if (redraw < 1) {
627 redraw = 1;
628 }
629 } else {
630 // turning the page
631 sounds |= LJSND_HOLD;
632 redraw = 2; // redraw the whole screen
633 }
634 }
635 lastKeys = keys;
637 if (done) {
638 sounds |= LJSND_LINE;
639 }
640 playSoundEffects(v, sounds, 100);
641 }
642 }