1 /*
2 * typespeed - measures your typing speed
3 * Copyright (C) 1999-2003 Jani Ollikainen <bestis@iki.fi>
4 * & Jaakko Manelius <jman@iki.fi>
5 * Copyright (C) 2006-2008 Tobias Stoeckmann <tobias@bugol.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 /*
23 * menu.c - menu & misc drawing functions
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif /* HAVE_CONFIG_H */
29
30 #ifdef HAVE_SYS_PARAM_H
31 #include <sys/param.h>
32 #endif /* HAVE_SYS_PARAM_H */
33
34 #include <curses.h>
35 #include <ctype.h>
36 #include <errno.h>
37
38 #ifdef HAVE_STDLIB_H
39 #include <stdlib.h>
40 #endif /* HAVE_STDLIB_H */
41
42 #ifdef HAVE_STRING_H
43 #include <string.h>
44 #endif /* HAVE_STRING_H */
45
46 #include "gettext.h"
47 #include "typespeed.h"
48
49 #define _(string) gettext(string)
50
51 extern int chooseruleset(void);
52 extern int choosewordfile(int);
53 extern void closenetwork(void);
54 extern unsigned long cstrl(char *);
55 extern void xerrx(int, const char *, ...);
56 extern int initnetwork(char *, int);
57 extern unsigned short level(int);
58 extern void liima_mvgetnstr(int, int, char *, int);
59 extern void multipmenu(void);
60 extern void xcolor_set(short);
61
62 static int dochange(int);
63 void drawmenu(void);
64 void drawscreen(void);
65 void drawstatus(unsigned);
66 int fileselmenu(int, struct finfo *, const char *);
67 static int getnum(const char *, unsigned long, unsigned long);
68 void initstatus(char *);
69 void multipmenu(void);
70 void nowordlist(void);
71 void optionmenu(void);
72 void pressanykey(int, int);
73 void rulesmenu(void);
74 void showhighscores(void);
75 void tellstory(void);
76
77 extern int misses;
78 extern char *rankki[11];
79
80 struct positions {
81 int rank;
82 int score;
83 int wpm;
84 int speed;
85 int misses;
86 } pos;
87
88 /* Switches "number" from 1 to 0 or any other value to 1. */
89 static int
dochange(int number)90 dochange(int number)
91 {
92 return (number == 1) ? 0 : 1;
93 }
94
95 /* Draws main menu onto screen. */
96 void
drawmenu(void)97 drawmenu(void)
98 {
99 drawscreen();
100
101 mvaddstr( 3, 30, "Typespeed ");
102 addstr(TVERSION);
103
104 mvaddstr( 5, 30, _("1. Test Your Speed"));
105 if (opt.net == NET)
106 mvaddstr( 6, 30, _("2. Close Connection"));
107 else
108 mvaddstr( 6, 30, _("2. Network Head2Head"));
109 mvaddstr( 7, 30, _("3. Story/Credits/RTFM!"));
110 mvaddstr( 8, 30, _("4. Show Highscores"));
111 mvaddstr( 9, 30, _("5. Options"));
112 mvaddstr(10, 30, _("6. Game Rules"));
113 mvaddstr(11, 30, _("7. Quit"));
114
115 mvaddstr(13, 30, _("Choose: "));
116 }
117
118 /* Clears screen and draws status bar. */
119 void
drawscreen(void)120 drawscreen(void)
121 {
122 clear();
123
124 xcolor_set(4);
125 move(22, 0);
126 hline(ACS_HLINE, 80);
127 mvaddch(23, 1, '>');
128 mvaddch(23, 21, '<');
129
130 xcolor_set(5);
131 mvaddstr(23, 23,
132 _("Rank: %------- Score: %--- WPM: %-- CPS: %---- Misses: %-"));
133
134 /* sets color to 2: NET, CHE need this */
135 drawstatus(0);
136
137 switch (opt.net) {
138 case H2H:
139 mvaddstr(22, 70, "H2H");
140 break;
141 case NET:
142 mvaddstr(22, 70, "NET");
143 break;
144 default:
145 break;
146 }
147
148 if (opt.cheat)
149 mvaddstr(22, 73, "CHE");
150
151 xcolor_set(5);
152 }
153
154 /*
155 * Prints status values - no descriptions though!
156 * Cursor will be put into "inputpos" (y = 21, x = 2 + inputpos).
157 */
158 void
drawstatus(unsigned inputpos)159 drawstatus(unsigned inputpos)
160 {
161 mvprintw(23, pos.rank, "%-9s", rankki[level(now.score)]);
162
163 xcolor_set(3);
164 mvprintw(23, pos.score, "%4lu", now.score);
165 mvprintw(23, pos.wpm, "%3lu", now.wpm);
166 mvprintw(23, pos.speed, now.speed < 10.0f ? " %2.2f" : "%2.2f",
167 now.speed);
168 mvprintw(23, pos.misses, "%2lu", misses);
169
170 xcolor_set(2);
171 move(23, 2 + inputpos);
172 }
173
174 /* Prints file selection menu. */
175 int
fileselmenu(int tot,struct finfo * thingie,const char * title)176 fileselmenu(int tot, struct finfo *thingie, const char *title)
177 {
178 int a, cpos, k;
179
180 drawscreen();
181 xcolor_set(4);
182 mvprintw(2, 5, _("Choose a %s (UP/DOWN/ENTER):"), title);
183 if (tot > 17)
184 for (k = 0; k < 17; k++)
185 mvaddstr(k + 4, 5, thingie[k].descr);
186 else
187 for (k = 0; k < tot; k++)
188 mvaddstr(k + 4, 5, thingie[k].descr);
189
190 cpos = k = 0;
191 mvaddstr(4, 1, "->");
192
193 for (a = 1; a;) {
194 /*
195 * And thanks for xmunkki, he made my day and
196 * "solved" this, what i didn't get right (or did
197 * but i used 2 extra variables. :)
198 */
199 switch (getch()) {
200 case 27:
201 return -1;
202 break;
203 case KEY_UP:
204 case 'k':
205 if (k <= 0)
206 break;
207 k--;
208 if (cpos > 0) {
209 cpos--;
210 break;
211 }
212 move(20, 5);
213 deleteln();
214 move(4, 5);
215 insertln();
216 mvaddstr(4, 5, thingie[k].descr);
217 break;
218 case KEY_DOWN:
219 case 'j':
220 if (k > tot - 2)
221 break;
222 k++;
223 if (cpos < 16 && cpos < tot - 1) {
224 cpos++;
225 break;
226 }
227 move(4, 5);
228 deleteln();
229 move(20, 5);
230 insertln();
231 mvaddstr(20, 5, thingie[k].descr);
232 break;
233 case ' ':
234 case '\n':
235 a = 0;
236 break;
237 default:
238 break;
239 }
240 mvaddstr(cpos + 3, 1, " ");
241 mvaddstr(cpos + 5, 1, " ");
242 mvaddstr(cpos + 4, 1, "->");
243 }
244
245 return k;
246 }
247
248 /*
249 * Asks player about a specific value named by "string". If the entered
250 * value is smaller or equal min, or larger or equal max, 0 is returned.
251 * If a string (and not a number) has been entered, 0 is returned, too.
252 *
253 * Sets rules.hightype to 0, so a customized rule set cannot reach top10.
254 */
255 static int
getnum(const char * string,unsigned long min,unsigned long max)256 getnum(const char *string, unsigned long min, unsigned long max)
257 {
258 unsigned long res;
259 char entr[11];
260 char clr[80];
261
262 memset(clr, ' ', sizeof(clr));
263 clr[sizeof(clr) - 1] = '\0';
264
265 mvaddstr(17, 0, clr);
266 mvaddstr(17, 30, string);
267 liima_mvgetnstr(17, 31 + strlen(string), entr, sizeof(entr) - 1);
268 entr[sizeof(entr) - 1] = '\0';
269
270 if (!(res = cstrl(entr)) && errno)
271 return 0;
272 if (res > min && res < max) {
273 rules.hightype = 0;
274 return res;
275 }
276 return 0;
277 }
278
279 /*
280 * Note places where rank, score, wpm, cps, and misses have to
281 * be printed, so translators have a much easier job.
282 *
283 * "Rank: %------- Score: %--- WPM: %-- CPS: %---- Misses: %-"
284 */
285 void
initstatus(char * str)286 initstatus(char *str)
287 {
288 int field;
289 char *p;
290
291 for (field = 0, p = str; *p != '\0'; p++) {
292 if (*p != '%')
293 continue;
294
295 switch (field) {
296 case 0:
297 pos.rank = p - str + 23;
298 break;
299 case 1:
300 pos.score = p - str + 23;
301 break;
302 case 2:
303 pos.wpm = p - str + 23;
304 break;
305 case 3:
306 pos.speed = p - str + 23;
307 break;
308 case 4:
309 pos.misses = p - str + 23;
310 break;
311 default:
312 xerrx(1, "1wrong status format");
313 /* NOTREACHED */
314 }
315 field++;
316 }
317
318 if (field != 5)
319 xerrx(1, "2wrong status format");
320 }
321
322 /* Draws multiplayer menu. */
323 void
multipmenu(void)324 multipmenu(void)
325 {
326 int exitnow;
327 char port[6], serv[MAXHOSTNAMELEN];
328
329 exitnow = 0;
330 serv[0] = '\0';
331
332 do {
333 drawscreen();
334 mvaddstr(3, 30, "Typespeed ");
335 addstr(TVERSION);
336
337 mvaddstr( 5, 30, _("1. Server"));
338 mvaddstr( 6, 30, _("2. Client"));
339 mvaddstr( 7, 30, _("3. Port"));
340 mvaddstr( 8, 30, _("4. Player Name"));
341 mvaddstr( 9, 30, _("5. Return"));
342
343 xcolor_set(5);
344 mvprintw( 7, 50, "%d", opt.port);
345 mvprintw( 8, 50, "%s", opt.name);
346 mvaddstr(11, 30, _("Choose: "));
347
348 switch (getch()) {
349 case '1':
350 serv[0] = '\0';
351 exitnow = 1;
352 break;
353 case '2':
354 mvaddstr(11, 30, _("Enter Host:"));
355 liima_mvgetnstr(11, 31 +
356 strlen(_("Enter Host:")), serv,
357 sizeof(serv) - 1);
358 serv[sizeof(serv) - 1] = '\0';
359 exitnow = 1;
360 break;
361 case '3':
362 mvaddstr(11, 30, _("Enter Port:"));
363 liima_mvgetnstr(11, 31 + strlen(_("Enter Port:")), port,
364 sizeof(port) - 1);
365 port[sizeof(port) - 1] = '\0';
366 opt.port = strtol(port, NULL, 10);
367 if (opt.port <= 1024)
368 opt.port = 6025;
369 exitnow = 0;
370 break;
371 case '4':
372 mvaddstr(11, 30, _("Enter Name:"));
373 liima_mvgetnstr(11, 31 +
374 strlen(_("Enter Name:")), opt.name,
375 sizeof(opt.name) - 1);
376 opt.name[sizeof(opt.name) - 1] = '\0';
377 exitnow = 0;
378 break;
379 case '5':
380 return;
381 /* NOTREACHED */
382 break;
383 default:
384 break;
385 }
386 } while (!exitnow);
387
388 drawscreen();
389 mvaddstr(3, 30, "Typespeed ");
390 addstr(TVERSION);
391
392 mvaddstr(5, 30, _("When you get connect, choose a word list and"));
393 mvaddstr(6, 30, _("then wait for the game start. The game starts as"));
394 mvaddstr(7, 30, _("soon as the other player has chosen a word list."));
395 refresh();
396
397 initnetwork(serv, 1);
398 flushinp();
399 }
400
401 /*
402 * Prints "No word lists found...".
403 * This function just exists to split all output out of file.c.
404 */
405 void
nowordlist(void)406 nowordlist(void)
407 {
408 drawscreen();
409 mvaddstr(1, 5, _("No word lists found..."));
410 pressanykey(3, 5);
411 }
412
413 /* Draws option menu. */
414 void
optionmenu(void)415 optionmenu(void)
416 {
417 int wheretogo;
418
419 do {
420 drawscreen();
421
422 mvaddstr(3, 30, "Typespeed ");
423 addstr(TVERSION);
424
425 mvaddstr(5, 30, _("1. Colors"));
426 mvaddstr(6, 30, _("2. Cheat"));
427 mvaddstr(7, 30, _("3. Return"));
428
429 xcolor_set(3);
430 if (opt.usecolors)
431 mvaddstr(5, 44, _("ON"));
432 if (opt.cheat)
433 mvaddstr(6, 44, _("ON"));
434
435 xcolor_set(6);
436 if (!opt.usecolors)
437 mvaddstr(5, 44, _("OFF"));
438 if (!opt.cheat)
439 mvaddstr(6, 44, _("OFF"));
440
441 xcolor_set(5);
442 mvaddstr(12, 30, _("Choose: "));
443
444 switch (wheretogo = getch()) {
445 case '2':
446 opt.cheat = dochange(opt.cheat);
447 break;
448 case '1':
449 if (opt.usecolors)
450 xcolor_set(2);
451 opt.usecolors = dochange(opt.usecolors);
452 break;
453 default:
454 break;
455 }
456 } while (wheretogo != '3');
457 }
458
459 /* This function waits until a key has been pressed */
460 void
pressanykey(int y,int x)461 pressanykey(int y, int x)
462 {
463 flushinp();
464 xcolor_set(4);
465 mvaddstr(y, x, _("Press any key to continue..."));
466 getch();
467 }
468
469 /* Draws rules menu. */
470 void
rulesmenu(void)471 rulesmenu(void)
472 {
473 int wheretogo;
474 long tmp;
475
476 do {
477 drawscreen();
478
479 mvaddstr( 3, 30, "Typespeed ");
480 addstr(TVERSION);
481
482 mvaddstr( 5, 30, _("Current Rule Set:"));
483
484 mvaddstr( 7, 30, _("1. Load Another Rule Set"));
485 mvaddstr( 8, 30, _("2. Misses Limit:"));
486 mvaddstr( 9, 30, _("3. Word Length:"));
487 addstr(" -");
488 mvaddstr(10, 30, _("4. Words On Screen:"));
489 addstr(" -");
490 mvaddstr(11, 30, _(" High Score Enabled:"));
491 mvaddstr(12, 30, _("6. Speed Step:"));
492 mvaddstr(13, 30, _("7. Speed Range:"));
493 addstr(" -");
494 mvaddstr(14, 30, _("8. Smoothness:"));
495 mvaddstr(15, 30, _("9. Return"));
496
497 xcolor_set(3);
498 mvprintw( 5, 31 + strlen(_("Current Rule Set:")), "%s",
499 rules.name);
500 mvprintw( 8, 31 + strlen(_("2. Misses Limit:")), "%d",
501 rules.misses);
502 mvprintw( 9, 31 + strlen(_("3. Word Length:")), "%2d",
503 rules.minlen);
504 mvprintw( 9, 36 + strlen(_("3. Word Length:")), "%2d",
505 rules.maxlen);
506 mvprintw(10, 31 + strlen(_("4. Words On Screen:")), "%2d",
507 rules.minwords);
508 mvprintw(10, 36 + strlen(_("4. Words On Screen:")), "%2d",
509 rules.maxwords);
510 if (rules.hightype)
511 mvaddstr(11, 31 + strlen(_(" High Score Enabled:")),
512 _("yes"));
513 else
514 mvaddstr(11, 31 + strlen(_(" High Score Enabled:")),
515 _("no"));
516 mvprintw(12, 31 + strlen(_("6. Speed Step:")), "%3d",
517 rules.step);
518 mvprintw(13, 31 + strlen(_("7. Speed Range:")), "%2d",
519 rules.minspeed);
520 if (rules.maxspeed)
521 mvprintw(13, 36 + strlen(_("7. Speed Range:")), "%2d",
522 rules.maxspeed);
523 else
524 mvaddstr(13, 37 + strlen(_("7. Speed Range:")),
525 _("0 (unlimited)"));
526 if (rules.smooth)
527 mvaddstr(14, 31 + strlen(_("8. Smoothness:")),
528 _("yes"));
529 else
530 mvaddstr(14, 31 + strlen(_("8. Smoothness:")), _("no"));
531 xcolor_set(5);
532 mvaddstr(17, 30, _("Choose: "));
533
534 switch (wheretogo = getch()) {
535 case '1':
536 chooseruleset();
537 break;
538 case '2':
539 if ((tmp = getnum(_("Misses Limit:"), 0, 100)))
540 rules.misses = tmp;
541 break;
542 case '3':
543 if ((tmp = getnum(_("Min Length:"), 0, 20)))
544 rules.minlen = tmp;
545 if ((tmp = getnum(_("Max Length:"), 0, 20)))
546 rules.maxlen = tmp;
547 if (rules.minlen > rules.maxlen)
548 rules.maxlen = rules.minlen;
549 break;
550 case '4':
551 if ((tmp = getnum(_("Min Words:"), 0, 23)))
552 rules.minwords = tmp;
553 if ((tmp = getnum(_("Max Words:"), 0, 23)))
554 rules.maxwords = tmp;
555 if (rules.minwords > rules.maxwords)
556 rules.maxwords = rules.minwords;
557 break;
558 case '6':
559 if ((tmp = getnum(_("Speed Step:"), 0, 1000)))
560 rules.step = tmp;
561 break;
562 case '7':
563 if ((tmp = getnum(_("Min Speed:"), 0, 100)))
564 rules.minspeed = tmp;
565 rules.maxspeed = getnum(_("Max Speed:"), 0, 100);
566 if (rules.minspeed > rules.maxspeed)
567 rules.maxspeed = 0;
568 break;
569 case '8':
570 rules.smooth = dochange(rules.smooth);
571 rules.hightype = 0;
572 break;
573 default:
574 break;
575 }
576 } while (wheretogo != '9');
577 }
578
579 /* Shows high scores. */
580 void
showhighscores(void)581 showhighscores(void)
582 {
583 choosewordfile(1);
584 }
585
586 /* Draws credits onto screen. */
587 void
tellstory(void)588 tellstory(void)
589 {
590 drawscreen();
591
592 mvaddstr( 3, 2, "Typespeed ");
593 addstr(TVERSION);
594 mvaddstr( 5, 2,
595 _("Typespeed is made by Jani Ollikainen <bestis@iki.fi>"));
596 mvaddstr( 6, 2,
597 _(" & Jaakko Manelius <jman@iki.fi>"));
598 mvaddstr( 7, 2,
599 _("Current maintainer is Tobias Stoeckmann <tobias@bugol.de>"));
600
601 mvaddstr( 9, 2,
602 _("Typespeed's idea is ripped from ztspeed (a dos game made"));
603 mvaddstr(10, 2,
604 _("by Zorlim). Typespeed doesn't give scores that can be"));
605 mvaddstr(11, 2,
606 _("compared with ztspeed anymore. We wanted to use our way."));
607 mvaddstr(13, 2,
608 _("Idea of the game should be clear to anyone, just type and"));
609 mvaddstr(14, 2,
610 _("type it fast, or be a lewser."));
611 mvaddstr(16, 2,
612 _("Bugs/Ideas/Comments to <tobias@bugol.de>"));
613
614 pressanykey(18, 2);
615 }
616
617