1 /*
2 cursesdisplay.c
3
4 Copyright (C) 2010-2019 Amf
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <libintl.h>
24 #include <locale.h>
25
26 #ifdef CHROMA_CURSES_HEADER
27 #include CHROMA_CURSES_HEADER
28 #else
29 #ifdef __WIN32__
30 #include <curses.h>
31 #else
32 #include <ncurses.h>
33 #endif
34 #endif
35
36 #include "chroma.h"
37 #include "menu.h"
38 #include "level.h"
39 #include "display.h"
40 #include "colours.h"
41 #include "actions.h"
42 #include "util.h"
43 #include "xmlparser.h"
44
45 char options_colours[FILENAME_MAX] = COLOURS_DEFAULT;
46 int options_curses_delay = 1;
47 int options_curses_replay_delay = 1;
48 int options_debug = 0;
49 #ifdef XOR_COMPATIBILITY
50 int options_xor_options = 0;
51 int options_xor_mode = 1;
52 int options_xor_display = 0;
53 #endif
54 #ifdef ENIGMA_COMPATIBILITY
55 int options_enigma_options = 0;
56 int options_enigma_mode = 1;
57 #endif
58
59
60 extern struct colours* pdisplaycolours;
61 extern int *editor_piece_maps[];
62 extern char *action_name[];
63 extern char *action_shortname[];
64
65 /* Translation table for colours.
66 This is necessary as some versions of curses interchange red and blue.
67 */
68 short colourtrans[] = {COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE};
69
70 int dp_attr[256], dp_col[256];
71 char dp_char[256];
72
73 int actions[KEY_MAX];
74
75 int display_size_x, display_size_y;
76 int display_offset_x, display_offset_y;
77 int display_focus_x, display_focus_y;
78 int display_start_x, display_start_y;
79 int display_end_x, display_end_y;
80 int display_border = 7;
81
82 void display_piece(struct level* plevel, int piece);
83
84 int display_colourpairs = 0;
85 short display_cpfore[64];
86 short display_cpback[64];
87
88 short colourpair_red;
89 short colourpair_green;
90 short colourpair_yellow;
91 short colourpair_blue;
92 short colourpair_cyan;
93 short colourpair_magenta;
94 short colourpair_cyan;
95 short colourpair_white;
96 short colourpair_menu;
97 short colourpair_menugrey;
98
99 char *display_keyname(int i);
100 void display_addkeytomenu(struct menu* pmenu, int action, char *text);
101 void display_keys();
102 void display_debug();
103 void display_initcolours();
104
105 void display_options_othergames();
106
display_newcolourpair(short foreground,short background)107 short display_newcolourpair(short foreground, short background)
108 {
109 short i;
110
111 for(i = 1; i <= display_colourpairs; i ++)
112 {
113 if(foreground == display_cpfore[i] && background == display_cpback[i])
114 return i;
115 }
116 display_colourpairs ++;
117 display_cpfore[display_colourpairs] = foreground;
118 display_cpback[display_colourpairs] = background;
119
120 init_pair(display_colourpairs, foreground, background);
121
122 return display_colourpairs;
123 }
124
display_init(int argc,char ** argv)125 void display_init(int argc, char **argv)
126 {
127 setlocale(LC_CTYPE, "");
128 atexit(display_quit);
129 initscr();
130 raw();
131 noecho();
132 keypad(stdscr, TRUE);
133 curs_set(0);
134 start_color();
135
136 getmaxyx(stdscr, display_size_y, display_size_x);
137
138 colourpair_red = display_newcolourpair(COLOR_RED, COLOR_BLACK);
139 colourpair_green = display_newcolourpair(COLOR_GREEN, COLOR_BLACK);
140 colourpair_yellow = display_newcolourpair(COLOR_YELLOW, COLOR_BLACK);
141 colourpair_blue = display_newcolourpair(COLOR_BLUE, COLOR_BLACK);
142 colourpair_magenta = display_newcolourpair(COLOR_MAGENTA, COLOR_BLACK);
143 colourpair_cyan = display_newcolourpair(COLOR_CYAN, COLOR_BLACK);
144 colourpair_white = display_newcolourpair(COLOR_WHITE, COLOR_BLACK);
145 colourpair_menu = display_newcolourpair(COLOR_CYAN, COLOR_BLUE);
146 colourpair_menugrey = display_newcolourpair(COLOR_CYAN, COLOR_BLACK);
147
148 display_options_load();
149 colours_init();
150 display_initcolours();
151 }
152
display_initcolours()153 void display_initcolours()
154 {
155 int i;
156 short fg, bg;
157
158 #ifdef PDCURSES
159 short tg;
160 #endif
161
162 for(i = 0; i < PIECE_MAX; i ++)
163 {
164 fg = pdisplaycolours->foreground[i];
165 if(fg < 0 || fg > 7)
166 fg = 7;
167
168 bg = pdisplaycolours->background[i];
169 if(bg < 0 || bg > 7)
170 bg = 0;
171
172 #ifdef PDCURSES
173 /* PDCurses doesn't handle reverse colours well; we swap them manually */
174 if(pdisplaycolours->reverse[i])
175 {
176 tg = fg; fg = bg; bg = tg;
177 }
178 #endif
179
180 dp_attr[i] = COLOR_PAIR(display_newcolourpair(colourtrans[fg], colourtrans[bg]));
181
182 if(pdisplaycolours->bold[i])
183 dp_attr[i] |= A_BOLD;
184
185 #ifndef PDCURSES
186 if(pdisplaycolours->reverse[i])
187 dp_attr[i] |= A_REVERSE;
188 #endif
189 }
190
191 }
192
display_quit()193 void display_quit()
194 {
195 clear();
196 refresh();
197 endwin();
198 }
199
display_hide()200 void display_hide()
201 {
202 clear();
203 refresh();
204 getch();
205 }
206
display_piece(struct level * plevel,int piece)207 void display_piece(struct level* plevel, int piece)
208 {
209 int p;
210
211 if(piece < 0 || piece >= PIECE_MAX)
212 return;
213
214 /* Use the colours of PIECE_PLAYER_ONE for the active player,
215 and the colours of PIECE_PLAYER_TWO for the inactive one. */
216 p = piece;
217 if(p == PIECE_PLAYER_ONE || p == PIECE_PLAYER_TWO)
218 {
219 if(plevel->player != 2)
220 {
221 if(plevel->player != (p & 1))
222 p = PIECE_PLAYER_TWO;
223 else
224 p = PIECE_PLAYER_ONE;
225 }
226 else
227 p = PIECE_PLAYER_ONE;
228 }
229
230 addch(pdisplaycolours->character[piece] | dp_attr[p]);
231 }
232
display_moves(struct level * plevel,struct level * plevelreplay)233 void display_moves(struct level* plevel, struct level* plevelreplay)
234 {
235 static int length = 0;
236 int i;
237 char buffer[256];
238 int moves, moves2;
239
240 moves = 0;
241 if(plevel->move_current != NULL)
242 {
243 /* If move_current->mover_first == NULL, we've actually undone all of
244 the current move, and are just about to move back to the previous
245 one; we treat it as the previous one for counting purposes */
246 if(plevel->move_current->mover_first != NULL)
247 moves = plevel->move_current->count;
248 else
249 moves = plevel->move_current->count - 1;
250 }
251
252 moves2 = -1;
253 if(plevelreplay != NULL)
254 {
255 moves2 = 0;
256 if(plevelreplay->move_last != NULL)
257 moves2 = plevelreplay->move_last->count;
258 }
259 /* Similarly, move_current->mover_first == NULL complicates things here */
260 else if(plevel->move_current != plevel->move_last
261 || (plevel->move_current != NULL && plevel->move_current->mover_first == NULL))
262 {
263 if(plevel->move_last != NULL)
264 moves2 = plevel->move_last->count;
265 }
266
267 if(moves2 != -1)
268 sprintf(buffer, "%s%d/%d",
269 plevel->flags & LEVELFLAG_PAUSED ? gettext("paused ") :
270 plevelreplay != NULL ? gettext("replay ") : "",
271 moves, moves2);
272 else
273 sprintf(buffer, "%s%d",
274 plevel->flags & LEVELFLAG_PAUSED ? gettext("paused ") : "",
275 moves);
276
277 if(plevel->flags & LEVELFLAG_FAILED)
278 sprintf(buffer, gettext("failed"));
279
280 attron(COLOR_PAIR(colourpair_cyan));
281
282 /* Blank previous display only if necessary */
283 if(utf8strlen(buffer) < length)
284 {
285 for(i = 0; i < length; i ++)
286 mvprintw(display_size_y - 1, display_size_x - 2 - length + i, " ");
287 }
288
289 length = utf8strlen(buffer);
290
291 mvprintw(display_size_y - 1, display_size_x - utf8strlen(buffer) - 2, "%s", buffer);
292 attroff(COLOR_PAIR(colourpair_cyan));
293
294 move(display_size_y - 1, display_size_x - 1);
295 display_piece(plevel, PIECE_PLAYER_ONE + plevel->player);
296 refresh();
297 }
298
display_stars(struct level * plevel)299 void display_stars(struct level* plevel)
300 {
301 static int length = 0;
302 char buffer[256];
303 int i;
304
305 sprintf(buffer, "%d/%d", plevel->stars_caught, plevel->stars_total);
306
307 if(plevel->stars_exploded != 0)
308 sprintf(buffer, gettext("%d lost"), plevel->stars_exploded);
309
310 if(plevel->flags & LEVELFLAG_SOLVED && !(plevel->flags & LEVELFLAG_FAILED))
311 sprintf(buffer, gettext("solved"));
312
313 attron(COLOR_PAIR(colourpair_yellow));
314
315 /* Blank previous display only if necessary */
316 if(utf8strlen(buffer) < length)
317 {
318 for(i = 0; i < length; i ++)
319 mvprintw(display_size_y - 1, i + 2, " ");
320 }
321
322 length = utf8strlen(buffer);
323
324 mvprintw(display_size_y - 1, 2, "%s", buffer);
325 attroff(COLOR_PAIR(colourpair_yellow));
326
327 move(display_size_y - 1, 0);
328 display_piece(plevel, PIECE_STAR);
329 }
330
display_focus(struct level * plevel)331 int display_focus(struct level* plevel)
332 {
333 int px, py;
334 int ox, oy;
335 #ifdef XOR_COMPATIBILITY
336 int redraw;
337 #endif
338
339 getmaxyx(stdscr, display_size_y, display_size_x);
340
341 #ifdef XOR_COMPATIBILITY
342 if(plevel->mode == MODE_XOR && options_xor_display)
343 {
344 if(display_start_x != plevel->view_x[plevel->player] || display_start_y != plevel->view_y[plevel->player])
345 redraw = 1;
346 else
347 redraw = 0;
348
349 display_start_x = plevel->view_x[plevel->player];
350 display_start_y = plevel->view_y[plevel->player];
351 display_end_x = display_start_x + 8;
352 display_end_y = display_start_y + 8;
353
354 return redraw;
355 }
356 #endif
357
358 ox = display_start_x;
359 oy = display_start_y;
360 px = plevel->player_x[plevel->player];
361 py = plevel->player_y[plevel->player];
362
363 if(plevel->size_x < display_size_x)
364 {
365 display_start_x = 0;
366 display_end_x = plevel->size_x;
367 }
368 else
369 {
370 if(px < display_start_x + display_border)
371 display_start_x = px - display_border;
372 if(px >= display_start_x + display_size_x - display_border)
373 display_start_x = px - display_size_x + display_border;
374 if(display_start_x < 0)
375 display_start_x = 0;
376 if(display_start_x + display_size_x > plevel->size_x)
377 display_start_x = plevel->size_x - display_size_x;
378 display_end_x = display_start_x + display_size_x;
379
380 }
381 if(plevel->size_y < display_size_y - 1)
382 {
383 display_start_y = 0;
384 display_end_y = plevel->size_y;
385 }
386 else
387 {
388 if(py < display_start_y + display_border)
389 display_start_y = py - display_border;
390 if(py >= display_start_y + display_size_y - 1 - display_border)
391 display_start_y = py - display_size_y + 1 + display_border;
392 if(display_start_y < 0)
393 display_start_y = 0;
394 if(display_start_y + display_size_y - 1 > plevel->size_y)
395 display_start_y = plevel->size_y - display_size_y + 1;
396 display_end_y = display_start_y + display_size_y - 1;
397
398 }
399
400 if(ox != display_start_x || oy != display_start_y)
401 return 1;
402 else
403 return 0;
404 }
405
display_level(struct level * plevel)406 void display_level(struct level* plevel)
407 {
408 int x, y;
409 int p;
410
411 clear();
412
413 getmaxyx(stdscr, display_size_y, display_size_x);
414
415 if(display_start_x < 0)
416 display_start_x = 0;
417 if(display_start_y < 0)
418 display_start_y = 0;
419
420 if(display_end_x > plevel->size_x)
421 display_end_x = plevel->size_x;
422 if(display_end_y > plevel->size_y)
423 display_end_y = plevel->size_y;
424
425 display_offset_x = (display_size_x - (display_end_x - display_start_x))/2;
426 display_offset_y = (display_size_y - (display_end_y - display_start_y))/2;
427
428 for(y = display_start_y; y < display_end_y; y++)
429 {
430 move(y + display_offset_y - display_start_y, display_offset_x);
431 for(x = display_start_x; x < display_end_x; x++)
432 {
433 p = level_piece(plevel, x, y);
434 #ifdef XOR_COMPATIBILITY
435 if(plevel->switched && (p == PIECE_SPACE || p == PIECE_WALL))
436 p = PIECE_DARKNESS;
437 #endif
438 display_piece(plevel, p);
439 }
440 }
441 }
442
display_play(struct level * plevel,struct level * plevelreplay)443 void display_play(struct level* plevel, struct level* plevelreplay)
444 {
445 int key;
446 int quit;
447 struct mover* pmover;
448 int redraw;
449 int x, y;
450 int p;
451 int playermove;
452 int delay;
453 int fast;
454 int pass;
455 int c;
456 short cp;
457 char font_logo_colours[] = "1326454646644";
458 char buffer[256];
459
460 quit = 0;
461 redraw = 1;
462 fast = 0;
463
464 while(!quit)
465 {
466 redraw += display_focus(plevel);
467
468 if(redraw)
469 {
470 display_level(plevel);
471
472 if(plevel->title != NULL)
473 {
474 y = display_size_y - 1;
475 x = (display_size_x - utf8strlen(plevel->title) - (plevel->flags & LEVELFLAG_TESTING ? utf8strlen(gettext("testing: ")) : 0) ) / 2;
476 if(x < 0)
477 x = 0;
478 move(y, x);
479
480 if(plevel->flags & LEVELFLAG_TESTING)
481 {
482 attron(COLOR_PAIR(colourpair_cyan));
483 printw(gettext("testing: "));
484 attroff(COLOR_PAIR(colourpair_cyan));
485 }
486 if((strncmp(gettext(plevel->title), "chroma", 6) == 0))
487 {
488 strcpy(buffer, gettext(plevel->title));
489
490 for(x = 0; x < strlen(buffer); x ++)
491 {
492 cp = colourpair_white;
493 if(x < strlen(font_logo_colours))
494 c = font_logo_colours[x] - '0';
495 else
496 {
497 printw("%s", buffer + x);
498 x = strlen(buffer);
499 break;
500 }
501 switch(c)
502 {
503 case 1:
504 cp = colourpair_red;
505 break;
506 case 2:
507 cp = colourpair_green;
508 break;
509 case 3:
510 cp = colourpair_yellow;
511 break;
512 case 4:
513 cp = colourpair_blue;
514 break;
515 case 5:
516 cp = colourpair_magenta;
517 break;
518 case 6:
519 cp = colourpair_cyan;
520 break;
521 default:
522 cp = colourpair_white;
523 break;
524 }
525 addch((*(buffer + x)) | COLOR_PAIR(cp));
526 }
527 }
528 else
529 printw("%s", plevel->title);
530 }
531
532 display_moves(plevel, plevelreplay);
533 display_stars(plevel);
534
535 refresh();
536 curs_set(0);
537 redraw = 0;
538 }
539
540 /* If there are movers, plot and then evolve them */
541 if(plevel->mover_first != NULL && !(plevel->flags & LEVELFLAG_PAUSED))
542 {
543 /* Plot movers in two passes - first spaces, then non-spaces.
544 This is counter-intuitive, but makes undoing the player work. */
545 for(pass = 0; pass < 2; pass ++)
546 {
547 pmover = plevel->mover_first;
548 while(pmover != NULL)
549 {
550 if((pass == 0 && pmover->piece != PIECE_SPACE) ||
551 (pass == 1 && pmover->piece == PIECE_SPACE))
552 {
553 pmover = pmover->next;
554 continue;
555 }
556 x = pmover->x;
557 y = pmover->y;;
558 if(x >= display_start_x && x < display_end_x && y>= display_start_y && y < display_end_y)
559 {
560 move(display_offset_y - display_start_y + y, display_offset_x - display_start_x + x);
561 p = pmover->piece;
562 #ifdef XOR_COMPATIBILITY
563 if(plevel->switched && (p == PIECE_SPACE || p == PIECE_WALL))
564 p = PIECE_DARKNESS;
565 #endif
566 if(p != PIECE_GONE)
567 display_piece(plevel, p);
568 }
569 pmover = pmover->next;
570 }
571 }
572
573 /* Debug movers */
574 if(options_debug & DEBUG_ORDER)
575 {
576 /* Display the movers */
577 pmover = plevel->mover_first;
578 y = 0;
579 while(pmover != NULL && y < display_size_y - 1)
580 {
581 if(pmover->piece != PIECE_GONE)
582 {
583 move(y ++, 0);
584 display_piece(plevel, pmover->piece);
585 printw(" %2d,%2d ", pmover->x, pmover->y);
586
587 }
588 pmover = pmover->next;
589 }
590 while(y < display_size_y - 1)
591 mvprintw(y++, 0, " ");
592
593 /* Display the stack if our game engine uses it */
594 if(0
595 #ifdef XOR_COMPATIBILITY
596 || (plevel->mode == MODE_XOR && options_xor_mode)
597 #endif
598 #ifdef ENIGMA_COMPATIBILITY
599 || (plevel->mode == MODE_ENIGMA && options_enigma_mode)
600 #endif
601 )
602 {
603 pmover = plevel->stack_first;
604 y = 0;
605 while(pmover != NULL && y < display_size_y - 1)
606 {
607 if(pmover->piece != PIECE_GONE)
608 {
609 move(y ++, display_size_x - 8);
610 display_piece(plevel, pmover->piece);
611 printw(" %2d,%2d ", pmover->x, pmover->y);
612
613 }
614 pmover = pmover->next;
615 }
616 while(y < display_size_y - 1)
617 mvprintw(y++, display_size_x - 8, " ");
618 }
619 }
620
621 refresh();
622
623 /* Evolve movers */
624 if(!(plevel->flags & LEVELFLAG_UNDO))
625 {
626 if(level_evolve(plevel))
627 redraw += display_focus(plevel);
628 level_storemovers(plevel);
629 }
630 else
631 {
632 if(level_undo(plevel))
633 plevel->flags |= LEVELFLAG_UNDO;
634 else
635 plevel->flags &= ~LEVELFLAG_UNDO;
636 }
637 }
638
639 /* Determine which delay to use */
640 delay = options_curses_delay;
641 if(plevelreplay != NULL)
642 {
643 if(plevel->mover_first == NULL && plevelreplay->move_current != NULL)
644 delay = options_curses_replay_delay;
645
646 if(fast)
647 delay = 0;
648
649 if(plevelreplay->flags & LEVELFLAG_UNDO)
650 {
651 if(plevel->move_current == NULL && plevel->mover_first == NULL)
652 {
653 if(options_curses_replay_delay != 0)
654 delay = options_curses_replay_delay;
655 else
656 delay = 1;
657 }
658 }
659 else
660 {
661 if(plevel->mover_first == NULL && plevelreplay->move_current == NULL)
662 delay = -1;
663 }
664 }
665 else
666 {
667 if(fast)
668 delay = 0;
669
670 if(plevel->mover_first == NULL)
671 {
672 delay = -1;
673 fast = 0;
674 }
675 }
676
677 if(delay > 0)
678 halfdelay(delay);
679
680 if(delay != 0)
681 {
682 key = getch();
683 if(key < 0 || key >= KEY_MAX)
684 key = 0;
685 if(key >= 'a' && key <='z')
686 key -= 32;
687 }
688 else
689 key = 0;
690
691 if(delay > 0)
692 cbreak();
693
694 playermove = MOVE_NONE;
695
696 switch(actions[key])
697 {
698 case ACTION_REDRAW:
699 redraw = 1;
700 break;
701
702 case ACTION_HIDE:
703 display_hide();
704 redraw = 1;
705 break;
706
707 case ACTION_QUIT:
708 quit = 1;
709 break;
710
711 case ACTION_FAST:
712 fast = 1 - fast;
713 break;
714
715 case ACTION_LEFT:
716 if(plevelreplay != NULL)
717 {
718 plevelreplay->flags |= LEVELFLAG_UNDO;
719 plevelreplay->flags &= ~LEVELFLAG_PAUSED;
720 }
721 else
722 playermove = MOVE_LEFT;
723 break;
724
725 case ACTION_RIGHT:
726 if(plevelreplay != NULL)
727 {
728 plevelreplay->flags &= ~LEVELFLAG_UNDO;
729 plevelreplay->flags &= ~LEVELFLAG_PAUSED;
730 }
731 else
732 playermove = MOVE_RIGHT;
733 break;
734
735 case ACTION_UP:
736 if(plevelreplay != NULL)
737 plevelreplay->flags |= LEVELFLAG_PAUSED;
738 else
739 playermove = MOVE_UP;
740 break;
741
742 case ACTION_DOWN:
743 if(plevelreplay != NULL)
744 plevelreplay->flags |= LEVELFLAG_PAUSED;
745 else
746 playermove = MOVE_DOWN;
747 break;
748
749 case ACTION_PAUSE:
750 if(plevelreplay != NULL)
751 {
752 if(plevelreplay->flags & LEVELFLAG_PAUSED)
753 plevelreplay->flags &= ~LEVELFLAG_PAUSED;
754 else
755 plevelreplay->flags |= LEVELFLAG_PAUSED;
756 }
757 else if(plevel->mover_first != NULL)
758 {
759 if(plevel->flags & LEVELFLAG_PAUSED)
760 plevel->flags &= ~LEVELFLAG_PAUSED;
761 else
762 plevel->flags |= LEVELFLAG_PAUSED;
763 plevel->flags |= LEVELFLAG_MOVES;
764 }
765 break;
766
767 case ACTION_SWAP:
768 if(plevelreplay == NULL)
769 playermove = MOVE_SWAP;
770 break;
771
772 case ACTION_UNDO:
773 if(plevelreplay == NULL)
774 {
775 if(plevel->mover_first == NULL && !(plevel->flags & LEVELFLAG_UNDO))
776 {
777 if(level_undo(plevel))
778 plevel->flags |= LEVELFLAG_UNDO;
779 else
780 plevel->flags &= ~LEVELFLAG_UNDO;
781 playermove = MOVE_NONE;
782 }
783 }
784 break;
785
786 case ACTION_REDO:
787 playermove = MOVE_REDO;
788 break;
789
790 default:
791 break;
792 }
793
794 /* Are we replaying the level? */
795 if(plevelreplay != NULL)
796 {
797 /* Is it time for another move? */
798 if(plevel->mover_first == NULL && !(plevelreplay->flags & LEVELFLAG_PAUSED))
799 {
800 /* Moving backwards through replay */
801 if(plevelreplay->flags & LEVELFLAG_UNDO)
802 {
803 if(level_undo(plevel))
804 {
805 plevel->flags |= LEVELFLAG_UNDO;
806 if(plevelreplay->move_current != NULL)
807 plevelreplay->move_current = plevelreplay->move_current->previous;
808 else
809 plevelreplay->move_current = plevelreplay->move_last;
810 }
811 else
812 plevel->flags &= ~LEVELFLAG_UNDO;
813 }
814 /* Moving forwards through replay */
815 else
816 {
817 if(plevelreplay->move_current != NULL)
818 {
819 playermove = plevelreplay->move_current->direction;
820 plevelreplay->move_current = plevelreplay->move_current->next;
821 }
822 }
823 }
824 }
825
826 /* Can't move if we've failed or solved the level */
827 if(plevel->flags & (LEVELFLAG_FAILED | LEVELFLAG_SOLVED))
828 playermove = MOVE_NONE;
829
830 /* If we can move, make the move */
831 if(playermove != MOVE_NONE && plevel->mover_first == NULL)
832 level_move(plevel, playermove);
833
834 /* Display things changed by the move */
835 if(plevel->flags & LEVELFLAG_MOVES)
836 {
837 display_moves(plevel, plevelreplay);
838 plevel->flags ^= LEVELFLAG_MOVES;
839 }
840
841 if(plevel->flags & LEVELFLAG_STARS)
842 {
843 display_stars(plevel);
844 plevel->flags ^= LEVELFLAG_STARS;
845 }
846
847 if(plevel->flags & LEVELFLAG_SWITCH)
848 {
849 redraw = 1;
850 plevel->flags ^= LEVELFLAG_SWITCH;
851 }
852
853 #ifdef XOR_COMPATIBILITY
854 if(plevel->flags & LEVELFLAG_MAP)
855 {
856 /* No sensible way to handle this in curses */
857 plevel->flags ^= LEVELFLAG_MAP;
858 }
859 #endif
860
861 if(!(plevel->flags & LEVELFLAG_SOLVED) && plevel->flags & LEVELFLAG_EXIT)
862 {
863 plevel->flags |= LEVELFLAG_SOLVED;
864 display_stars(plevel);
865 }
866
867 if(!(plevel->flags & LEVELFLAG_FAILED) && plevel->alive[0] == 0 && plevel->alive[1] ==0)
868 {
869 plevel->flags |= LEVELFLAG_FAILED;
870 display_moves(plevel, plevelreplay);
871 }
872 }
873 }
874
display_edit(struct level * plevel)875 void display_edit(struct level* plevel)
876 {
877 int key;
878 int quit;
879 static int editor_piece = PIECE_SPACE;
880 int redraw, moved, pmoved;
881 int i;
882 int player;
883 int piece_count;
884
885 redraw = 1;
886 moved = 1;
887 pmoved = 1;
888
889 /* Store player */
890 player = plevel->player;
891 plevel->player = 2;
892
893 piece_count = 0;
894 while(editor_piece_maps[plevel->mode][piece_count] != PIECE_GONE)
895 piece_count ++;
896
897 if(editor_piece > piece_count)
898 editor_piece = 0;
899
900 quit = 0;
901 while(!quit)
902 {
903 redraw += display_focus(plevel);
904
905 if(redraw)
906 {
907 redraw = 0;
908
909 display_level(plevel);
910
911 for(i = 0; i < piece_count; i ++)
912 {
913 move(display_size_y - 1, 1 + i * 2);
914 display_piece(plevel, editor_piece_maps[plevel->mode][i]);
915 }
916
917 move(display_size_y - 1, display_size_x - 4);
918 printw("[ ]");
919
920 curs_set(1);
921
922 pmoved = 1;
923 }
924
925 if(pmoved)
926 {
927 pmoved = 0;
928
929 move(display_size_y - 1, editor_piece * 2);
930 printw(">");
931 move(display_size_y - 1, 2 + editor_piece * 2);
932 printw("<");
933 move(display_size_y - 1, display_size_x - 3);
934 display_piece(plevel, editor_piece_maps[plevel->mode][editor_piece]);
935 moved = 1;
936 }
937
938 if(moved)
939 {
940 moved = 0;
941
942 move(display_offset_y - display_start_y + plevel->player_y[2], display_offset_x - display_start_x + plevel->player_x[2]);
943 refresh();
944 }
945
946 key = getch();
947 if(key < 0 || key >= KEY_MAX)
948 key = 0;
949 if(key >= 'a' && key <='z')
950 key -= 32;
951
952 switch(actions[key])
953 {
954 case ACTION_REDRAW:
955 redraw = 1;
956 break;
957
958 case ACTION_HIDE:
959 display_hide();
960 redraw = 1;
961 break;
962
963 case ACTION_QUIT:
964 quit = 1;
965 break;
966
967 case ACTION_LEFT:
968 if(plevel->player_x[2] > 0)
969 {
970 plevel->player_x[2] --; moved = 1;
971 }
972 break;
973
974 case ACTION_RIGHT:
975 if(plevel->player_x[2] < plevel->size_x - 1)
976 {
977 plevel->player_x[2] ++; moved = 1;
978 }
979 break;
980
981 case ACTION_UP:
982 if(plevel->player_y[2] > 0)
983 {
984 plevel->player_y[2] --; moved = 1;
985 }
986 break;
987
988 case ACTION_DOWN:
989 if(plevel->player_y[2] < plevel->size_y -1)
990 {
991 plevel->player_y[2] ++; moved = 1;
992 }
993 break;
994
995 case ACTION_SWAP:
996 level_setpiece(plevel, plevel->player_x[2], plevel->player_y[2], editor_piece_maps[plevel->mode][editor_piece]);
997 display_piece(plevel, editor_piece_maps[plevel->mode][editor_piece]);
998 moved = 1;
999 break;
1000
1001 case ACTION_PIECE_LEFT:
1002 move(display_size_y - 1, editor_piece * 2);
1003 printw(" ");
1004 move(display_size_y - 1, 2 + editor_piece * 2);
1005 printw(" ");
1006 editor_piece --;
1007 if(editor_piece < 0)
1008 editor_piece = piece_count - 1;
1009 pmoved = 1;
1010 break;
1011
1012 case ACTION_PIECE_RIGHT:
1013 move(display_size_y - 1, editor_piece * 2);
1014 printw(" ");
1015 move(display_size_y - 1, 2 + editor_piece * 2);
1016 printw(" ");
1017 editor_piece ++;
1018 if(editor_piece >= piece_count)
1019 editor_piece = 0;
1020 pmoved = 1;
1021 break;
1022 }
1023 }
1024
1025 /* Restore real player */
1026 plevel->player = player;
1027 }
1028
display_type()1029 int display_type()
1030 {
1031 return DISPLAY_CURSES;
1032 }
1033
display_options()1034 void display_options()
1035 {
1036 struct menu* pmenu;
1037 struct menu* pcoloursmenu;
1038 struct menuentry* pentrycolours;
1039 struct menuentry* pentryspeed;
1040 struct menuentry* pentryreplayspeed;
1041 char buffer[256];
1042 int ok;
1043 int result;
1044
1045 pmenu = menu_new(gettext("Display Options"));
1046
1047 menuentry_new(pmenu, gettext("Return to previous menu"), 'Q', 0);
1048 menuentry_new(pmenu, "", 0, MENU_SPACE);
1049
1050 menuentry_new(pmenu, gettext("Save Options"), 'S', 0);
1051 menuentry_new(pmenu, "", 0, MENU_SPACE);
1052
1053 pentrycolours = menuentry_new(pmenu, gettext("Colour Scheme"), 'C', 0);
1054 menuentry_new(pmenu, "", 0, MENU_SPACE);
1055
1056 pentryspeed = menuentry_new(pmenu, gettext("Move Speed"), 'M', MENU_SCROLLABLE);
1057 pentryreplayspeed = menuentry_new(pmenu, gettext("Replay Speed"), 'R', MENU_SCROLLABLE);
1058 menuentry_new(pmenu, "", 0, MENU_SPACE);
1059
1060 menuentry_new(pmenu, gettext("Change Keys"), 'K', 0);
1061
1062 /* XOR and Enigma options are only visible once an appropriate level has
1063 * been seen so as not to confuse those simply playing Chroma levels */
1064 if(0
1065 #ifdef XOR_COMPATIBILITY
1066 || options_xor_options
1067 #endif
1068 #ifdef ENIGMA_COMPATIBILITY
1069 || options_enigma_options
1070 #endif
1071 )
1072 {
1073 menuentry_new(pmenu, "", 0, MENU_SPACE);
1074 menuentry_new(pmenu, gettext("Other Games Options"), 'X', 0);
1075 }
1076
1077 if(options_debug & DEBUG_MENU)
1078 {
1079 menuentry_new(pmenu, "", 0, MENU_SPACE);
1080 menuentry_new(pmenu, gettext("Debug Options"), 'D', 0);
1081 }
1082
1083 ok = 0;
1084 while(!ok)
1085 {
1086 if(pdisplaycolours == NULL)
1087 menuentry_extratext(pentrycolours, gettext("** NONE **"), NULL, NULL);
1088 else if(pdisplaycolours->title == NULL)
1089 menuentry_extratext(pentrycolours, gettext("[untitled colours]"), NULL, NULL);
1090 else if(pdisplaycolours->flags & COLOURS_TRANSLATE)
1091 menuentry_extratext(pentrycolours, gettext(pdisplaycolours->title), NULL, NULL);
1092 else
1093 menuentry_extratext(pentrycolours, pdisplaycolours->title, NULL, NULL);
1094
1095 switch(options_curses_delay)
1096 {
1097 case -1:
1098 sprintf(buffer, gettext("after a key is pressed"));
1099 break;
1100 case 0:
1101 sprintf(buffer, gettext("instantaneous"));
1102 break;
1103 default:
1104 sprintf(buffer, gettext("%d00 milliseconds"), options_curses_delay);
1105 break;
1106 }
1107 menuentry_extratext(pentryspeed, buffer, NULL, NULL);
1108
1109 switch(options_curses_replay_delay)
1110 {
1111 case -1:
1112 sprintf(buffer, gettext("after a key is pressed"));
1113 break;
1114 case 0:
1115 sprintf(buffer, gettext("instantaneous"));
1116 break;
1117 default:
1118 sprintf(buffer, gettext("%d00 milliseconds"), options_curses_replay_delay);
1119 break;
1120 }
1121 menuentry_extratext(pentryreplayspeed, buffer, NULL, NULL);
1122
1123 result = menu_process(pmenu);
1124 if(result == MENU_QUIT)
1125 ok = 1;
1126
1127 if(result == MENU_SELECT && pmenu->entry_selected != NULL)
1128 {
1129 switch(pmenu->entry_selected->key)
1130 {
1131 case 'Q':
1132 ok = 1;
1133 break;
1134
1135 case 'C':
1136 pcoloursmenu = colours_menu();
1137 if(menu_process(pcoloursmenu) == MENU_SELECT)
1138 {
1139 if(pcoloursmenu->entry_selected != NULL && pcoloursmenu->entry_selected->value != NULL)
1140 {
1141 strcpy(options_colours, pcoloursmenu->entry_selected->value);
1142 colours_init();
1143 display_initcolours();
1144 }
1145 }
1146 menu_delete(pcoloursmenu);
1147 break;
1148
1149 case 'S':
1150 display_options_save();
1151 ok = 1;
1152 break;
1153
1154 case 'K':
1155 display_keys();
1156 break;
1157
1158 case 'X':
1159 display_options_othergames();
1160 break;
1161
1162 case 'D':
1163 display_debug();
1164 break;
1165 }
1166 }
1167
1168 if(result == MENU_SCROLLLEFT && pmenu->entry_selected != NULL)
1169 {
1170 switch(pmenu->entry_selected->key)
1171 {
1172 case 'M':
1173 options_curses_delay --;
1174 if(options_curses_delay < -1)
1175 options_curses_delay = 10;
1176 #ifdef PDCURSES
1177 if(options_curses_delay > 0)
1178 options_curses_delay = 0;
1179 #endif
1180 break;
1181 case 'R':
1182 options_curses_replay_delay --;
1183 if(options_curses_replay_delay < -1)
1184 options_curses_replay_delay = 10;
1185
1186 #ifdef PDCURSES
1187 if(options_curses_replay_delay > 0)
1188 options_curses_replay_delay = 0;
1189 #endif
1190 break;
1191 }
1192 }
1193
1194 if(result == MENU_SCROLLRIGHT && pmenu->entry_selected != NULL)
1195 {
1196 switch(pmenu->entry_selected->key)
1197 {
1198 case 'M':
1199 options_curses_delay ++;
1200 if(options_curses_delay > 10)
1201 options_curses_delay = -1;
1202 #ifdef PDCURSES
1203 if(options_curses_delay > 0)
1204 options_curses_delay = 0;
1205 #endif
1206 break;
1207 case 'R':
1208 options_curses_replay_delay ++;
1209 if(options_curses_replay_delay > 10)
1210 options_curses_replay_delay = -1;
1211 #ifdef PDCURSES
1212 if(options_curses_replay_delay > 0)
1213 options_curses_replay_delay = 0;
1214 #endif
1215 break;
1216 }
1217 }
1218 }
1219
1220 menu_delete(pmenu);
1221 }
1222
display_keyfixed(int i)1223 int display_keyfixed(int i)
1224 {
1225 if(i == 0 || i == KEY_RESIZE || i == 27 || i == 'Q' || i == '\n' || i == '\r' || i == KEY_UP || i == KEY_DOWN || i == KEY_LEFT || i == KEY_RIGHT)
1226 return 1;
1227
1228 return 0;
1229 }
1230
display_keyname(int i)1231 char *display_keyname(int i)
1232 {
1233 static char buffer[4];
1234
1235 if(i == '\t')
1236 return "TAB";
1237 if(i == '\n')
1238 return "ENTER";
1239 if(i == 27)
1240 return "ESCAPE";
1241 if(i == 32)
1242 return "SPACE";
1243 if(i == KEY_DC)
1244 return "DELETE";
1245 if(i == KEY_IC)
1246 return "INSERT";
1247
1248 if(keyname(i) == NULL)
1249 return "UNKNOWN";
1250
1251 if(strcmp(keyname(i), "NO KEY NAME") == 0)
1252 {
1253 if(i >= 0 && i < 32)
1254 {
1255 sprintf(buffer, "^%c", i + '@');
1256 return buffer;
1257 }
1258 if(i > 32 && i < 127)
1259 {
1260 sprintf(buffer, "%c", i);
1261 return buffer;
1262 }
1263
1264 return "UNKNOWN";
1265 }
1266
1267 if(strncmp(keyname(i), "KEY_", 4) == 0)
1268 return (char *)(keyname(i) + 4);
1269
1270 return (char *)keyname(i);
1271 }
1272
display_addkeytomenu(struct menu * pmenu,int action,char * text)1273 void display_addkeytomenu(struct menu* pmenu, int action, char *text)
1274 {
1275 struct menuentry *pentry;
1276 char buffer[256];
1277 int i;
1278
1279 sprintf(buffer, "%d", action);
1280 pentry = menuentry_newwithvalue(pmenu, text, 0, MENU_DOUBLE, buffer);
1281
1282 strcpy(buffer, "");
1283 for(i = 0; i < KEY_MAX; i ++)
1284 {
1285 if(actions[i] == action && i != KEY_RESIZE)
1286 {
1287 if(strlen(buffer) != 0)
1288 strcat(buffer,", ");
1289 strcat(buffer, "[");
1290 strcat(buffer, display_keyname(i));
1291 strcat(buffer, "]");
1292 }
1293 }
1294
1295 if(strcmp(buffer, "") == 0)
1296 strcpy(buffer, "(none)");
1297
1298 menuentry_extratext(pentry, NULL, NULL, buffer);
1299
1300 }
1301
display_keys()1302 void display_keys()
1303 {
1304 struct menu *pmenu;
1305 struct menu *psubmenu;
1306 struct menuentry *pentry;
1307 int action;
1308 int result;
1309 int redraw;
1310 int ok;
1311 int subok;
1312 char buffer[256];
1313 int i;
1314 int key;
1315
1316 ok = 0;
1317 while(!ok)
1318 {
1319 pmenu = menu_new(gettext("Keys"));
1320
1321 menuentry_new(pmenu, gettext("Quit and return to previous menu"), 'Q', 0);
1322 menuentry_new(pmenu, "", 0, MENU_SPACE);
1323
1324 display_addkeytomenu(pmenu, ACTION_LEFT, gettext(action_name[ACTION_LEFT]));
1325 display_addkeytomenu(pmenu, ACTION_RIGHT, gettext(action_name[ACTION_RIGHT]));
1326 display_addkeytomenu(pmenu, ACTION_UP, gettext(action_name[ACTION_UP]));
1327 display_addkeytomenu(pmenu, ACTION_DOWN, gettext(action_name[ACTION_DOWN]));
1328 display_addkeytomenu(pmenu, ACTION_SWAP, gettext(action_name[ACTION_SWAP]));
1329 display_addkeytomenu(pmenu, ACTION_UNDO, gettext(action_name[ACTION_UNDO]));
1330 display_addkeytomenu(pmenu, ACTION_REDO, gettext(action_name[ACTION_REDO]));
1331 display_addkeytomenu(pmenu, ACTION_FAST, gettext(action_name[ACTION_FAST]));
1332 display_addkeytomenu(pmenu, ACTION_PAUSE, gettext(action_name[ACTION_PAUSE]));
1333 display_addkeytomenu(pmenu, ACTION_QUIT, gettext(action_name[ACTION_QUIT]));
1334 display_addkeytomenu(pmenu, ACTION_REDRAW, gettext(action_name[ACTION_REDRAW]));
1335 display_addkeytomenu(pmenu, ACTION_HIDE, gettext(action_name[ACTION_HIDE]));
1336 display_addkeytomenu(pmenu, ACTION_PIECE_LEFT, gettext(action_name[ACTION_PIECE_LEFT]));
1337 display_addkeytomenu(pmenu, ACTION_PIECE_RIGHT, gettext(action_name[ACTION_PIECE_RIGHT]));
1338
1339 menu_assignletters(pmenu);
1340
1341 result = menu_process(pmenu);
1342
1343 if(result == MENU_QUIT)
1344 ok = 1;
1345
1346 if(result == MENU_SELECT)
1347 {
1348 if(pmenu->entry_selected->key == 'Q')
1349 ok = 1;
1350 else if(pmenu->entry_selected->value != NULL)
1351 {
1352 redraw = MENUREDRAW_ALL;
1353 subok = 0;
1354 while(!subok)
1355 {
1356 action = atoi(pmenu->entry_selected->value);
1357
1358 sprintf(buffer, gettext("Set keys for '%s'"), gettext(action_name[action]));
1359 psubmenu = menu_new(buffer);
1360
1361 menuentry_new(psubmenu, gettext("Quit and return to previous menu"), 'Q', 0);
1362 menuentry_new(psubmenu, "", 0, MENU_SPACE);
1363
1364 for(i = 0; i < KEY_MAX; i ++)
1365 {
1366 if(actions[i] == action && i != KEY_RESIZE)
1367 {
1368 sprintf(buffer, "[%s]", display_keyname(i));
1369 pentry = menuentry_new(psubmenu, buffer, 0, MENU_GREY);
1370 if(display_keyfixed(i))
1371 menuentry_extratext(pentry, gettext("(fixed)"), NULL, NULL);
1372 }
1373 }
1374 menuentry_new(psubmenu, "", 0, MENU_SPACE);
1375
1376 menuentry_new(psubmenu, gettext("Press a key to add or remove it from this list."), 0, MENU_NOTE | MENU_CENTRE);
1377
1378 menu_display(psubmenu, redraw);
1379 redraw = MENUREDRAW_ENTRIES;
1380 menu_delete(psubmenu);
1381
1382 key = getch();
1383 if(key == KEY_RESIZE)
1384 {
1385 getmaxyx(stdscr, display_size_y, display_size_x);
1386 redraw = MENUREDRAW_ALL;
1387 }
1388
1389 if(key >= 'a' && key <='z')
1390 key -=32;
1391
1392 if(key == 27 || key == 'q' || key == 'Q' || key== '\n')
1393 subok = 1;
1394 else if(!display_keyfixed(key))
1395 {
1396 if(actions[key] == action)
1397 actions[key] = ACTION_NONE;
1398 else
1399 actions[key] = action;
1400 }
1401 }
1402 }
1403 }
1404
1405 menu_delete(pmenu);
1406 }
1407 }
1408
display_debug()1409 void display_debug()
1410 {
1411 struct menu* pmenu;
1412 struct menuentry* pentrymovers;
1413 struct menuentry* pentryhidden;
1414 int ok;
1415 int result;
1416
1417 pmenu = menu_new(gettext("Debug Options"));
1418
1419 menuentry_new(pmenu, gettext("Return to previous menu"), 'Q', 0);
1420
1421 menuentry_new(pmenu, "", 0, MENU_SPACE);
1422
1423 pentrymovers = menuentry_new(pmenu, gettext("Display order of movers"), 'O', MENU_SCROLLABLE);
1424 pentryhidden = menuentry_new(pmenu, gettext("Show hidden items"), 'H', MENU_SCROLLABLE);
1425
1426 ok = 0;
1427 while(!ok)
1428 {
1429 menuentry_extratext(pentrymovers, options_debug & DEBUG_ORDER ? gettext("yes") : gettext("no"), NULL, NULL);
1430 menuentry_extratext(pentryhidden, options_debug & DEBUG_HIDDEN ? gettext("yes") : gettext("no"), NULL, NULL);
1431
1432 result = menu_process(pmenu);
1433 if(result == MENU_QUIT)
1434 ok = 1;
1435
1436 if((result == MENU_SELECT || result == MENU_SCROLLLEFT || result == MENU_SCROLLRIGHT) && pmenu->entry_selected != NULL)
1437 {
1438 switch(pmenu->entry_selected->key)
1439 {
1440 case 'Q':
1441 ok = 1;
1442 break;
1443
1444 case 'O':
1445 options_debug ^= DEBUG_ORDER;
1446 break;
1447
1448 case 'H':
1449 options_debug ^= DEBUG_HIDDEN;
1450 break;
1451 }
1452
1453 pmenu->redraw = MENUREDRAW_CHANGED;
1454 pmenu->entry_selected->redraw = 1;
1455 }
1456
1457 }
1458
1459 menu_delete(pmenu);
1460 }
1461
display_options_othergames()1462 void display_options_othergames()
1463 {
1464 struct menu* pmenu;
1465 #ifdef XOR_COMPATIBILITY
1466 struct menuentry* pentryxormode;
1467 struct menuentry* pentryxordisplay;
1468 #endif
1469 #ifdef ENIGMA_COMPATIBILITY
1470 struct menuentry* pentryenigmamode;
1471 #endif
1472
1473 int ok;
1474 int result;
1475
1476 pmenu = menu_new(gettext("Other Games Options"));
1477
1478 menuentry_new(pmenu, gettext("Return to previous menu"), 'Q', 0);
1479
1480 menuentry_new(pmenu, "", 0, MENU_SPACE);
1481
1482 #ifdef XOR_COMPATIBILITY
1483 pentryxormode = menuentry_new(pmenu, gettext("XOR Engine"), 'X', options_xor_options ? 0 : MENU_INVISIBLE | MENU_GREY);
1484 pentryxordisplay = menuentry_new(pmenu, gettext("XOR Display"), 'D', options_xor_options ? 0 : MENU_INVISIBLE | MENU_GREY);
1485 if(options_xor_options)
1486 menuentry_new(pmenu, "", 0, MENU_SPACE);
1487 #endif
1488
1489 #ifdef ENIGMA_COMPATIBILITY
1490 pentryenigmamode = menuentry_new(pmenu, gettext("Enigma Engine"), 'E', options_enigma_options ? 0 : MENU_INVISIBLE | MENU_GREY);
1491 #endif
1492
1493 ok = 0;
1494 while(!ok)
1495 {
1496 #ifdef XOR_COMPATIBILITY
1497 menuentry_extratext(pentryxormode, options_xor_mode ? gettext("exact") : gettext("approximate"), NULL, NULL);
1498 menuentry_extratext(pentryxordisplay, options_xor_display ? gettext("partial") : gettext("full"), NULL, NULL);
1499 #endif
1500 #ifdef ENIGMA_COMPATIBILITY
1501 menuentry_extratext(pentryenigmamode, options_enigma_mode ? gettext("exact") : gettext("approximate"), NULL, NULL);
1502 #endif
1503
1504 result = menu_process(pmenu);
1505 if(result == MENU_QUIT)
1506 ok = 1;
1507
1508 if(result == MENU_SELECT && pmenu->entry_selected != NULL)
1509 {
1510 switch(pmenu->entry_selected->key)
1511 {
1512 case 'Q':
1513 ok = 1;
1514 break;
1515
1516 #ifdef XOR_COMPATIBILITY
1517 case 'X':
1518 options_xor_mode = 1 - options_xor_mode;
1519 break;
1520
1521 case 'D':
1522 options_xor_display = 1 - options_xor_display;
1523 break;
1524 #endif
1525
1526 #ifdef ENIGMA_COMPATIBILITY
1527 case 'E':
1528 options_enigma_mode = 1 - options_enigma_mode;
1529 break;
1530 #endif
1531 }
1532
1533 pmenu->redraw = MENUREDRAW_CHANGED;
1534 pmenu->entry_selected->redraw = 1;
1535 }
1536
1537 }
1538
1539 menu_delete(pmenu);
1540 }
1541
display_options_save()1542 void display_options_save()
1543 {
1544 FILE *file;
1545 char filename[FILENAME_MAX];
1546 int i;
1547
1548 getfilename("curses.chroma", filename, 1, 0);
1549
1550 file = fopen(filename, "w");
1551 if(file == NULL)
1552 {
1553 warning("Unable to save options");
1554 return;
1555 }
1556
1557 fprintf(file, "<!-- Chroma curses options \n"
1558 " This file is automatically generated. -->\n"
1559 "\n"
1560 "<chroma type=\"options\">\n");
1561
1562 fprintf(file, " <colour scheme=\"%s\" />\n", options_colours);
1563
1564 if(options_curses_delay == -1)
1565 fprintf(file, " <move speed=\"key\" />\n");
1566 else
1567 fprintf(file, " <move speed=\"%d\" />\n", options_curses_delay * 100);
1568
1569 if(options_curses_replay_delay == -1)
1570 fprintf(file, " <replay speed=\"key\" />\n");
1571 else
1572 fprintf(file, " <replay speed=\"%d\" />\n", options_curses_replay_delay * 100);
1573
1574 #ifdef XOR_COMPATIBILITY
1575 if(options_xor_options)
1576 fprintf(file, " <xor mode=\"%s\" display=\"%s\" />\n", options_xor_mode ? "exact" : "approximate", options_xor_display ? "partial" : "full");
1577 #endif
1578 #ifdef ENIGMA_COMPATIBILITY
1579 if(options_enigma_options)
1580 fprintf(file, " <enigma mode=\"%s\" />\n", options_enigma_mode ? "exact" : "approximate");
1581 #endif
1582
1583 fprintf(file, " <!-- Set <debug menu=\"yes\" /> to change debug options within Chroma -->\n");
1584 fprintf(file, " <debug ");
1585 fprintf(file, "menu=\"%s\" ", options_debug & DEBUG_MENU ? "yes" : "no");
1586 fprintf(file, "order=\"%s\" ", options_debug & DEBUG_ORDER ? "yes" : "no");
1587 fprintf(file, "hidden=\"%s\" ", options_debug & DEBUG_HIDDEN ? "yes" : "no");
1588 fprintf(file,"/>\n");
1589
1590 fprintf(file, " <keys>\n");
1591
1592 for(i = 0; i < KEY_MAX; i ++)
1593 {
1594 if(actions[i] != ACTION_NONE && i != KEY_RESIZE)
1595 fprintf(file, " <key name=\"%s\" action=\"%s\" />\n", display_keyname(i), action_shortname[actions[i]]);
1596 }
1597
1598 fprintf(file, " </keys>\n");
1599
1600 fprintf(file, "</chroma>\n");
1601
1602 fclose(file);
1603 }
1604
display_options_load()1605 void display_options_load()
1606 {
1607 struct parser* pparser;
1608 char filename[FILENAME_MAX];
1609 int state;
1610 int i;
1611 int key, action;
1612
1613 /* Sensible defaults */
1614 #ifdef PDCURSES
1615 /* halfdelay() is broken in PDCurses */
1616 options_curses_delay = 0;
1617 options_curses_replay_delay = 0;
1618 #else
1619 options_curses_delay = 1;
1620 options_curses_replay_delay = 1;
1621 #endif
1622 #ifdef XOR_COMPATIBILITY
1623 options_xor_options = 0;
1624 options_xor_mode = 1;
1625 options_xor_display = 0;
1626 #endif
1627 #ifdef ENIGMA_COMPATIBILITY
1628 options_enigma_options = 0;
1629 options_enigma_mode = 1;
1630 #endif
1631 options_debug = 0;
1632
1633 getfilename("colours", filename, 0, 1);
1634 snprintf(options_colours, sizeof(options_colours), "%s/%s", filename, COLOURS_DEFAULT);
1635
1636 getfilename("curses.chroma", filename, 0, 0);
1637
1638 for(i = 0; i < KEY_MAX; i ++)
1639 {
1640 actions[i] = ACTION_NONE;
1641 }
1642
1643 /* Fixed keys */
1644 actions[KEY_RESIZE] = ACTION_REDRAW;
1645 actions[KEY_UP] = ACTION_UP;
1646 actions[KEY_DOWN] = ACTION_DOWN;
1647 actions[KEY_LEFT] = ACTION_LEFT;
1648 actions[KEY_RIGHT] = ACTION_RIGHT;
1649 actions['\r'] = ACTION_SWAP;
1650 actions['\n'] = ACTION_SWAP;
1651 actions['Q'] = ACTION_QUIT;
1652 actions[27] = ACTION_QUIT;
1653
1654 /* Sensible default keys */
1655 if(!isfile(filename))
1656 {
1657 actions[12] = ACTION_REDRAW;
1658 actions[' '] = ACTION_SWAP;
1659 actions['F'] = ACTION_FAST;
1660 actions[KEY_BACKSPACE] = ACTION_UNDO;
1661 actions[KEY_DC] = ACTION_UNDO;
1662 actions['U'] = ACTION_UNDO;
1663 actions[KEY_IC] = ACTION_REDO;
1664 actions['Y'] = ACTION_REDO;
1665 actions['Z'] = ACTION_PIECE_LEFT;
1666 actions['X'] = ACTION_PIECE_RIGHT;
1667 actions[KEY_PPAGE] = ACTION_PIECE_LEFT;;
1668 actions[KEY_NPAGE] = ACTION_PIECE_RIGHT;
1669 actions['P'] = ACTION_PAUSE;
1670 return;
1671 }
1672
1673 /* Parse XML file */
1674 /*
1675 <chroma type="options">
1676 <colour scheme="filename" />
1677 <move speed="speed" />
1678 <replay speed="speed" />
1679 <xor mode="mode" />
1680 <debug menu="yes/no" movers="yes/no" />
1681 <keys>
1682 <key name="name" action="action" />
1683 </keys>
1684 </chroma>
1685 */
1686
1687 pparser = parser_new(filename);
1688
1689 enum {
1690 OPTIONSPARSER_END, /* End of file */
1691 OPTIONSPARSER_OUTSIDE, /* Outside of <chroma> */
1692 OPTIONSPARSER_CHROMA, /* Inside <chroma> */
1693 OPTIONSPARSER_KEYS /* Inside <keys> */
1694 };
1695
1696 state = OPTIONSPARSER_OUTSIDE;
1697 key = 0;
1698 action = 0;
1699
1700 while(state != OPTIONSPARSER_END)
1701 {
1702 switch(parser_parse(pparser))
1703 {
1704 case PARSER_END:
1705 state = OPTIONSPARSER_END;
1706 break;
1707
1708 case PARSER_ELEMENT_START:
1709 switch(state)
1710 {
1711 case OPTIONSPARSER_CHROMA:
1712 if(parser_match(pparser, 0, "keys"))
1713 state = OPTIONSPARSER_KEYS;
1714 break;
1715
1716 case OPTIONSPARSER_KEYS:
1717 if(parser_match(pparser, 0, "key"))
1718 {
1719 key = 0;
1720 action = ACTION_NONE;
1721 }
1722 break;
1723
1724 default:
1725 break;
1726 }
1727 break;
1728
1729 case PARSER_ELEMENT_END:
1730 switch(state)
1731 {
1732 case OPTIONSPARSER_KEYS:
1733 if(parser_match(pparser, 0, "keys"))
1734 {
1735 state = OPTIONSPARSER_CHROMA;
1736 }
1737 if(parser_match(pparser, 0, "key"))
1738 {
1739 if(key != 0 && !display_keyfixed(key))
1740 actions[key] = action;
1741 }
1742 break;
1743
1744 default:
1745 break;
1746 }
1747 break;
1748
1749 case PARSER_CONTENT:
1750 break;
1751
1752 case PARSER_ATTRIBUTE:
1753 switch(state)
1754 {
1755 case OPTIONSPARSER_OUTSIDE:
1756 if(parser_match(pparser, 2, "chroma") && parser_match(pparser, 1, "type"))
1757 {
1758 if(parser_match(pparser, 0, "options"))
1759 state = OPTIONSPARSER_CHROMA;
1760 }
1761 break;
1762
1763 case OPTIONSPARSER_CHROMA:
1764 if(parser_match(pparser, 2, "colour") && parser_match(pparser, 1, "scheme"))
1765 {
1766 strncpy(options_colours, parser_text(pparser, 0), FILENAME_MAX);
1767 }
1768 if(parser_match(pparser, 2, "move") && parser_match(pparser, 1, "speed"))
1769 {
1770 if(parser_match(pparser, 0, "key"))
1771 options_curses_delay = -1;
1772 else
1773 options_curses_delay = atoi(parser_text(pparser, 0)) / 100;
1774 #ifdef PDCURSES
1775 if(options_curses_delay > 0)
1776 options_curses_delay = 0;
1777 #endif
1778 }
1779 if(parser_match(pparser, 2, "replay") && parser_match(pparser, 1, "speed"))
1780 {
1781 if(parser_match(pparser, 0, "key"))
1782 options_curses_replay_delay = -1;
1783 else
1784 options_curses_replay_delay = atoi(parser_text(pparser, 0)) / 100;
1785 #ifdef PDCURSES
1786 if(options_curses_replay_delay > 0)
1787 options_curses_replay_delay = 0;
1788 #endif
1789 }
1790 #ifdef XOR_COMPATIBILITY
1791 if(parser_match(pparser, 2, "xor") && parser_match(pparser, 1, "mode"))
1792 {
1793 options_xor_options = 1;
1794
1795 if(parser_match(pparser, 0, "approximate"))
1796 options_xor_mode = 0;
1797 if(parser_match(pparser, 0, "exact"))
1798 options_xor_mode = 1;
1799 }
1800 if(parser_match(pparser, 2, "xor") && parser_match(pparser, 1, "display"))
1801 {
1802 options_xor_options = 1;
1803
1804 if(parser_match(pparser, 0, "full"))
1805 options_xor_display = 0;
1806 if(parser_match(pparser, 0, "partial"))
1807 options_xor_display = 1;
1808 }
1809 #endif
1810 #ifdef ENIGMA_COMPATIBILITY
1811 if(parser_match(pparser, 2, "enigma") && parser_match(pparser, 1, "mode"))
1812 {
1813 options_enigma_options = 1;
1814
1815 if(parser_match(pparser, 0, "approximate"))
1816 options_enigma_mode = 0;
1817 if(parser_match(pparser, 0, "exact"))
1818 options_enigma_mode = 1;
1819 }
1820 #endif
1821 if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "menu"))
1822 {
1823 if(parser_match(pparser, 0, "yes"))
1824 options_debug |= DEBUG_MENU;
1825 }
1826 if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "order"))
1827 {
1828 if(parser_match(pparser, 0, "yes"))
1829 options_debug |= DEBUG_ORDER;
1830 }
1831 if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "hidden"))
1832 {
1833 if(parser_match(pparser, 0, "yes"))
1834 options_debug |= DEBUG_HIDDEN;
1835 }
1836 break;
1837
1838 case OPTIONSPARSER_KEYS:
1839 if(parser_match(pparser, 2, "key") && parser_match(pparser, 1, "name"))
1840 {
1841 for(i = 0; i < KEY_MAX; i ++)
1842 {
1843 if(parser_match(pparser, 0, display_keyname(i)))
1844 {
1845 key = i;
1846 i = KEY_MAX;
1847 }
1848 }
1849 }
1850 if(parser_match(pparser, 2, "key") && parser_match(pparser, 1, "action"))
1851 {
1852 for(i = ACTION_KEY_MIN; i < ACTION_KEY_MAX; i ++)
1853 {
1854 if(parser_match(pparser, 0, action_shortname[i]))
1855 {
1856 action = i;
1857 i = ACTION_KEY_MAX;
1858 }
1859 }
1860 }
1861 break;
1862 }
1863 break;
1864
1865 case PARSER_ERROR:
1866 state = OPTIONSPARSER_END;
1867 break;
1868 }
1869 }
1870
1871 parser_delete(pparser);
1872 }
1873