comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:230cf0be7cea
1 /* options code for LOCKJAW, an implementation of the Soviet Mind Game
2
3 Copyright (C) 2007-2008 Damian Yerrick <tepples+lj@spamcop.net>
4
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.
9
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.
14
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
18
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.
22
23 */
24
25
26 #include <string.h>
27 #include <stdio.h>
28 #include "options.h"
29 #include "ljplay.h"
30 #include "ljlocale.h"
31
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
43
44 const FourCC optionsBoolNames[2] = {
45 {"Off"}, {"On"}
46 };
47
48 const FourCC gimmickNames[LJGM_N_GIMMICKS] = {
49 {"Mara"}, {"Line"}, {"Time"}, {"DrlA"}, {"Item"}, {"Keys"}
50 };
51
52 const FourCC optionsGarbageNames[LJGARBAGE_N_STYLES] = {
53 {"Off"}, {"Lv.1"}, {"Lv.2"}, {"Lv.3"}, {"Lv.4"},
54 {"HRD"}, {"DrlG"}, {"Zigz"}
55 };
56
57 const FourCC optionsScoringNames[LJSCORE_N_STYLES] = {
58 {"LJ"}, {"Fibo"}, {"HotL"}, {"TDSl"}, {"NESl"}, {"LJns"}
59 };
60
61 const FourCC optionsKickLimitNames[N_KICK_LIMITS] = {
62 {"Off"}, {"1"}, {"2"}, {"3"}, {"4"}, {"5"}, {"Inf"}
63 };
64
65 const FourCC optionsSpeedCurveNames[] = {
66 {"Zero"}, {"Rhy0"}, {"Exp"}, {"Rh20"},
67 {"TGM2"}, {"TAPD"}, {"TAD3"},
68 {"NESc"}, {"GBc"}, {"GBHL"}
69 };
70
71 static const FourCC optionsWindowedNames[2] = {
72 {"FulS"}, {"Wind"}
73 };
74
75 const FourCC optionsShadowNames[LJSHADOW_N_STYLES] = {
76 {"tl25"}, {"tl50"}, {"tlsC"}, {"tlsM"}, {"Off"}, {"invF"}
77 };
78
79 const FourCC optionsLockDelayNames[2] = {
80 {"SBSC"}, {"Inf"}
81 };
82
83 const FourCC optionsSBSCNames[2] = {
84 {"SBSC"}, {.i=0}
85 };
86
87 const FourCC optionsTspinNames[] = {
88 {"Off"}, {"Imob"}, {"CrnT"}, {"WKT"}
89 };
90
91 const FourCC optionsLockdownNames[] = {
92 {"OLD"}, {"EntR"}, {"StpR"}, {"MovR"}
93 };
94
95 const FourCC optionsHoldStyleNames[] = {
96 {"Off"}, {"HldE"}, {"HldR"}, {"HldN"}
97 };
98
99 const FourCC optionsDropScoringNames[LJDROP_N_STYLES] = {
100 {"None"}, {"ConD"}, {"S1H1"}, {"S1H2"}
101 };
102
103 const FourCC optionsGravNames[] = {
104 {"Naiv"}, {"Stky"}, {"byCo"}, {"Casc"}
105 };
106
107 const FourCC optionsPieceSetNames[LJRAND_N_PIECE_SETS] = {
108 {"IJLO"},
109 {"JLOT"},
110 {"SZSZ"},
111 {"IIII"},
112 {"AllP"},
113 {"TTTT"}
114 };
115
116 const FourCC optionsRandNames[LJRAND_N_RANDS] = {
117 {"Unif"},
118 {"Bag"},
119 {"Bag+"},
120 {"Bag2"},
121 {"Hist"},
122 {"His6"}
123 };
124
125 const FourCC optionsLineDelayNames[] = {
126 {"SBSC"}, {.i=0}
127 };
128
129 const FourCC optionsZangiNames[] = {
130 {"Slid"}, {"Lock"}, {"LocU"}
131 };
132
133 const FourCC optionsGluingNames[] = {
134 {"Off"}, {"Squ"}, {"Stky"}, {"byCo"}
135 };
136
137 const FourCC optionsRotNames[N_ROTATION_SYSTEMS] = {
138 {"SRS"}, {"Sega"}, {"ARS"}, {"Tngn"},
139 {"NRSR"}, {"NRSL"}, {"TOD4"}, {"TDX"}
140 };
141
142
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 },
160
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 },
173
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 },
188
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 },
199
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 },
206
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 },
215
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 };
229
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 };
245
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 }
258
259 struct DisabledOption {
260 unsigned char name;
261 unsigned char value;
262 unsigned char name2;
263 char reason[45];
264 };
265
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 };
298
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;
304
305 if (prefs[name] == value) {
306 return disabledOptions[i].reason;
307 }
308 }
309 }
310 return NULL;
311 }
312
313
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;
318
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;
327
328 case OPTSTYLE_FRAMES:
329 if (l->valueNames
330 && value == l->minValue
331 && l->valueNames[0].i) {
332
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) {
339
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;
353
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;
371
372 default:
373 strncpy(dst, "Unknown option style.", OPTIONS_VALUE_LEN - 1);
374 dst[OPTIONS_VALUE_LEN - 1] = 0;
375 break;
376 }
377
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 }
385
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];
406
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];
421
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];
436
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];
456
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 }
466
467 void initOptions(unsigned char *prefs) {
468 for (int i = 0; i < OPTIONS_MENU_LEN; ++i) {
469 prefs[i] = commonOptionsMenu[i].startValue;
470 }
471 }
472
473 LJBits menuReadPad(void);
474 void vsync(void);
475
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;
486
487 optionsWinInit();
488
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 }
511
512 LJBits keys = menuReadPad();
513 LJBits sounds = 0;
514 int lastY = y;
515 LJBits newKeys = keys & ~lastKeys;
516 LJBits dasKeys = 0;
517
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 }
531
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 }
555
556 if (!isDisabledOption(prefs, y)) {
557 if ((newKeys | dasKeys) & VKEY_RIGHT) {
558 int num = prefs[y] + 1;
559
560 if (num >= optionsMenu[y].minValue + optionsMenu[y].nValues) {
561 prefs[y] = optionsMenu[y].minValue;
562 } else {
563 prefs[y] = num;
564 }
565
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 }
572
573 if ((newKeys | dasKeys) & VKEY_LEFT) {
574 int num = prefs[y] - 1;
575
576 if (num < optionsMenu[y].minValue) {
577 prefs[y] = optionsMenu[y].minValue + optionsMenu[y].nValues - 1;
578 } else {
579 prefs[y] = num;
580 }
581
582 sounds |= LJSND_ROTATE;
583 redraw = 1;
584 dasDir = VKEY_LEFT;
585 }
586 }
587
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 }
596
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 }
606
607 // Start: finish
608 if (newKeys & VKEY_START) {
609 done = 1;
610 }
611
612 if (lastY != y) {
613 sounds |= LJSND_SHIFT;
614
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 }
623
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;
636
637 if (done) {
638 sounds |= LJSND_LINE;
639 }
640 playSoundEffects(v, sounds, 100);
641 }
642 }