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