1 /* NetWalk main program
2  * Ben Lynn
3  */
4 /*
5 Copyright (C) 2004 Benjamin Lynn (blynn@cs.stanford.edu)
6 
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 */
21 #include <assert.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <math.h>
25 #include <time.h>
26 
27 #include <sys/types.h> //for opendir et al.
28 #include <dirent.h>
29 
30 #include <sys/stat.h>
31 #include <unistd.h>
32 
33 #include <stdlib.h>
34 #include <signal.h>
35 
36 #include <SDL.h>
37 #include <SDL_ttf.h>
38 
39 #include "version.h"
40 
41 #include "widget.h"
42 #include "colour.h"
43 #include "game.h"
44 
45 #include "config.h"
46 #include "util.h"
47 
48 static int interrupted = 0;
49 
50 enum {
51     vsize = 18
52 };
53 
54 enum {
55     pulse_max = 1024
56 };
57 
58 enum {
59     state_game,
60     state_button,
61     state_quit
62 };
63 
64 enum {
65     level_easy = 0,
66     level_medium,
67     level_hard,
68     level_veryhard,
69     level_giant,
70     level_absurd,
71     level_max,
72     level_custom
73 };
74 
75 enum {
76     cellw = 24,
77     cellh = 24,
78     border = 1,
79     padding = 10
80 };
81 
82 char shifttable[256];
83 
84 struct hsentry_s {
85     char *name;
86     int time;
87 };
88 typedef struct hsentry_s *hsentry_ptr;
89 typedef struct hsentry_s hsentry_t[1];
90 
91 hsentry_t hstable[level_max];
92 char *level_name[level_max];
93 
94 //TODO use this somehow
95 struct gameparm_s {
96     int boardw, boardh;
97     int wrap;
98 };
99 typedef struct gameparm_s *gameparm_ptr;
100 typedef struct gameparm_s gameparm_t[1];
101 
102 gameparm_t gp;
103 
104 static config_t config;
105 
106 SDL_Surface *screen;
107 TTF_Font *font;
108 int lastmousex, lastmousey;
109 char *player_name;
110 extern int game_won;
111 int state;
112 
113 struct button_s {
114     struct widget_s widget;
115     char *text;
116     SDL_Surface *img;
117 };
118 typedef struct button_s button_t[1];
119 typedef struct button_s *button_ptr;
120 
121 struct label_s {
122     struct widget_s widget;
123     char *text;
124     SDL_Surface *img;
125 };
126 typedef struct label_s label_t[1];
127 typedef struct label_s *label_ptr;
128 
129 struct textbox_s {
130     struct widget_s widget;
131     char text[1024];
132     int i;
133     SDL_Surface *img;
134 };
135 typedef struct textbox_s textbox_t[1];
136 typedef struct textbox_s *textbox_ptr;
137 
138 struct arena_s {
139     struct widget_s widget;
140 };
141 typedef struct arena_s arena_t[1];
142 typedef struct arena_s *arena_ptr;
143 
144 struct menuitem_s {
145     struct widget_s widget;
146     char *text;
147     SDL_Surface *img;
148     struct menu_s *submenu;
149 };
150 typedef struct menuitem_s menuitem_t[1];
151 typedef struct menuitem_s *menuitem_ptr;
152 
153 struct menu_s {
154     struct widget_s widget;
155     //TODO: replace array with list
156     menuitem_ptr item_list[64];
157     int item_count;
158 };
159 typedef struct menu_s menu_t[1];
160 typedef struct menu_s *menu_ptr;
161 
162 struct menubar_s {
163     struct widget_s widget;
164     //TODO: replace array with list
165     menuitem_ptr item_list[64];
166     int item_count;
167 };
168 typedef struct menubar_s menubar_t[1];
169 typedef struct menubar_s *menubar_ptr;
170 
171 struct window_s {
172     struct widget_s widget;
173     //TODO: replace array with list
174     struct widget_s *widget_list[64];
175     int widget_count;
176 
177 };
178 typedef struct window_s window_t[1];
179 typedef struct window_s *window_ptr;
180 
181 struct hsw_s {
182     label_t level;
183     label_t time;
184     label_t name;
185 };
186 typedef struct hsw_s *hsw_ptr;
187 typedef struct hsw_s hsw_t[1];
188 
189 hsw_t hsw[level_max];
190 
191 arena_t arena;
192 window_t root;
193 label_t l_moves;
194 label_t l_time;
195 menubar_t menu;
196 menuitem_ptr openedmenu;
197 window_ptr modalwindow;
198 window_t about_window;
199 window_t hs_window;
200 window_t enter_name_window;
201 label_t l_about1;
202 label_t l_about2;
203 button_t b_about1;
204 widget_t statusbar;
205 
206 label_t l_hs1;
207 button_t b_hs1;
208 
209 label_t l_en1;
210 textbox_t tb_en1;
211 button_t b_en1;
212 
font_render(char * s,int c)213 SDL_Surface *font_render(char *s, int c)
214 {
215     return TTF_RenderText_Solid(font, s, rgbtable[c]);
216 }
217 
statusbar_update(widget_ptr w)218 void statusbar_update(widget_ptr w)
219 {
220     widget_lowered_background(w);
221 }
222 
menu_init(menu_ptr m)223 void menu_init(menu_ptr m)
224 {
225     widget_init((widget_ptr) m);
226     m->item_count = 0;
227 }
228 
menu_update(menu_ptr m)229 void menu_update(menu_ptr m)
230 {
231     int i;
232     menuitem_ptr it;
233     SDL_Rect rect;
234 
235     widget_fill((widget_ptr) m, c_background);
236     for (i=0; i<m->item_count; i++) {
237 	it = m->item_list[i];
238 	if (in_widget((widget_ptr) it, lastmousex, lastmousey)) {
239 	    rect.x = 2;
240 	    rect.y = vsize * i;
241 	    rect.w = ((widget_ptr) it)->w - 4;
242 	    rect.h = vsize;
243 	    widget_fillrect((widget_ptr) m, &rect, c_menubg);
244 	}
245 	rect.x = 8;
246 	rect.y = vsize * i + 4;
247 	widget_blit((widget_ptr) m, it->img, NULL, &rect);
248     }
249 }
250 
menu_add_item(menu_ptr m,menuitem_ptr it)251 void menu_add_item(menu_ptr m, menuitem_ptr it)
252 {
253     m->item_list[m->item_count] = it;
254     m->item_count++;
255 }
256 
menuitem_init(menuitem_ptr m)257 void menuitem_init(menuitem_ptr m)
258 {
259     widget_init((widget_ptr) m);
260     m->text = NULL;
261     m->img = NULL;
262     m->submenu = NULL;
263 }
264 
menuitem_new()265 menuitem_ptr menuitem_new()
266 {
267     menuitem_ptr it;
268 
269     it = (menuitem_ptr) malloc(sizeof(menuitem_t));
270     menuitem_init(it);
271 
272     return it;
273 }
274 
menuitem_put_text(menuitem_ptr m,char * s)275 void menuitem_put_text(menuitem_ptr m, char *s)
276 {
277     SDL_Surface *tmp;
278 
279     m->text = s;
280     if (m->img) SDL_FreeSurface(m->img);
281     tmp = font_render(s, c_text);
282     m->img = SDL_DisplayFormat(tmp);
283     SDL_FreeSurface(tmp);
284 }
285 
open_submenu(widget_ptr p,void * data)286 void open_submenu(widget_ptr p,	void *data)
287 {
288     int i;
289     int w = 0;
290     menuitem_ptr it;
291 
292     openedmenu = (menuitem_ptr) p;
293     menu_ptr m = openedmenu->submenu;
294 
295     for (i=0; i<m->item_count; i++) {
296 	it = m->item_list[i];
297 	if (w < it->img->w) w = it->img->w;
298     }
299     w += 12;
300 
301     m->widget.x = m->widget.parent->x;
302     m->widget.y = m->widget.parent->y + vsize;
303 
304     m->widget.w = w;
305     m->widget.h = vsize * m->item_count + 1;
306 
307     for (i=0; i<m->item_count; i++) {
308 	it = m->item_list[i];
309 	it->widget.x = 0 + m->widget.x;
310 	it->widget.y = vsize * i + 4 + m->widget.y;
311 	it->widget.w = w;
312 	it->widget.h = vsize;
313     }
314 }
315 
menuitem_set_submenu(menuitem_ptr it,menu_ptr m)316 void menuitem_set_submenu(menuitem_ptr it, menu_ptr m)
317 {
318     it->submenu = m;
319     //it->widget.signal_handler[signal_activate] = open_submenu;
320     widget_put_handler((widget_ptr) it, open_submenu, signal_activate);
321     m->widget.parent = (widget_ptr) it;
322 }
323 
menubar_update(widget_ptr wid)324 void menubar_update(widget_ptr wid)
325 {
326     SDL_Rect dst;
327     menubar_ptr m = (menubar_ptr) wid;
328     menuitem_ptr it;
329     int i;
330 
331     widget_raised_background(wid);
332 
333     for (i=0; i<m->item_count; i++) {
334 	it = m->item_list[i];
335 	if (it == openedmenu) {
336 	    dst.x = it->widget.x + 2;
337 	    dst.y = it->widget.y + 2;
338 	    dst.w = it->widget.w - 4;
339 	    dst.h = vsize - 2;
340 	    widget_fillrect(wid, &dst, c_menubg);
341 	}
342 	if (it->img) {
343 	    dst.x = it->widget.x + 5;
344 	    dst.y = it->widget.y + 2;
345 	    widget_blit(m, it->img, NULL, &dst);
346 	}
347     }
348 }
349 
menubar_handle_click(widget_ptr p,int button,int x,int y)350 void menubar_handle_click(widget_ptr p, int button, int x, int y)
351 {
352     int i;
353     menubar_ptr m = (menubar_ptr) p;
354     menuitem_ptr it;
355 
356     for (i=0; i<m->item_count; i++) {
357 	it = m->item_list[i];
358 	if (in_widget((widget_ptr) it, x, y)) {
359 	    widget_raise_signal((widget_ptr) it, signal_activate);
360 	    return;
361 	}
362     }
363 }
364 
menubar_init(menubar_ptr m)365 void menubar_init(menubar_ptr m)
366 {
367     widget_init((widget_ptr) m);
368     m->widget.update = menubar_update;
369     m->item_count = 0;
370     m->widget.handle_click = menubar_handle_click;
371 }
372 
menubar_add_item(menubar_ptr m,menuitem_ptr it)373 void menubar_add_item(menubar_ptr m, menuitem_ptr it)
374 {
375     m->item_list[m->item_count] = it;
376     m->item_count++;
377     it->widget.parent = (widget_ptr) m;
378 }
379 
menubar_auto_layout(menubar_ptr m)380 void menubar_auto_layout(menubar_ptr m)
381 {
382     int i, x, y;
383     menuitem_ptr it;
384 
385     x = 0;
386     y = 0;
387     for (i=0; i<m->item_count; i++) {
388 	it = m->item_list[i];
389 	if (it->img) {
390 	    it->widget.x = x;
391 	    it->widget.y = y;
392 	    it->widget.w = it->img->w + 10;
393 	    it->widget.h = it->img->h;
394 	    x += it->img->w + 10;
395 	}
396     }
397 }
398 
label_update(widget_ptr p)399 void label_update(widget_ptr p)
400 {
401     SDL_Rect dst;
402     label_ptr l = (label_ptr) p;
403 
404     if (l->img) {
405 	dst.x = 0;
406 	dst.y = 4;
407 	widget_blit(l, l->img, NULL, &dst);
408     }
409 }
410 
label_init(label_ptr l)411 void label_init(label_ptr l)
412 {
413     widget_init((widget_ptr) l);
414     l->text = NULL;
415     l->img = NULL;
416     l->widget.update = label_update;
417 }
418 
label_put_text(label_ptr l,char * s)419 void label_put_text(label_ptr l, char *s)
420 {
421     SDL_Surface *tmp;
422 
423     if (l->img) SDL_FreeSurface(l->img);
424     if (l->text) free(l->text);
425     l->text = clonestr(s);
426     tmp = font_render(s, c_text);
427     l->img = SDL_DisplayFormat(tmp);
428     SDL_FreeSurface(tmp);
429 }
430 
textbox_update_img(textbox_ptr tb)431 void textbox_update_img(textbox_ptr tb)
432 {
433     SDL_Surface *tmp;
434 
435     if (tb->img) SDL_FreeSurface(tb->img);
436     tmp = font_render(tb->text, c_text);
437     if (tmp) {
438 	tb->img = SDL_DisplayFormat(tmp);
439 	SDL_FreeSurface(tmp);
440     } else {
441 	tb->img = NULL;
442     }
443 }
444 
textbox_left(textbox_ptr tb)445 void textbox_left(textbox_ptr tb)
446 {
447     if (tb->i > 0) tb->i--;
448 }
449 
textbox_right(textbox_ptr tb)450 void textbox_right(textbox_ptr tb)
451 {
452     if (tb->i < strlen(tb->text)) tb->i++;
453 }
454 
textbox_delete(textbox_ptr tb)455 void textbox_delete(textbox_ptr tb)
456 {
457     if (tb->i > 0) {
458 	tb->i--;
459 	tb->text[tb->i] = 0;
460 	textbox_update_img(tb);
461     }
462 }
463 
textbox_backspace(textbox_ptr tb)464 void textbox_backspace(textbox_ptr tb)
465 {
466     char *s = &tb->text[tb->i];
467     if (tb->i) {
468 	memmove(s - 1, s, strlen(s) + 1);
469 	tb->i--;
470 	textbox_update_img(tb);
471     }
472 }
473 
textbox_insert(textbox_ptr tb,char c)474 void textbox_insert(textbox_ptr tb, char c)
475 {
476     char *s = &tb->text[tb->i];
477     memmove(s + 1, s, strlen(s) + 1);
478     tb->text[tb->i] = c;
479     tb->i++;
480     textbox_update_img(tb);
481 }
482 
textbox_update(widget_ptr p)483 void textbox_update(widget_ptr p)
484 {
485     SDL_Rect dst;
486     textbox_ptr tb = (textbox_ptr) p;
487 
488     dst.x = 0;
489     dst.y = 0;
490     dst.w = p->w;
491     dst.h = p->h;
492     widget_fillrect(p, &dst, c_text);
493     dst.x++;
494     dst.y++;
495     dst.w-=2;
496     dst.h-=2;
497     widget_fillrect(p, &dst, c_canvas);
498 
499     if (tb->img) {
500 	dst.x = 1;
501 	dst.y = 3;
502 	widget_blit(tb, tb->img, NULL, &dst);
503     }
504 
505     {
506 	char s[1024];
507 	int w, h;
508 
509 	strncpy(s, tb->text, tb->i);
510 	s[tb->i] = 0;
511 	TTF_SizeText(font, s, &w, &h);
512 
513 	dst.x = w;
514 	dst.y = 2;
515 	dst.w = 1;
516 	dst.h = vsize - 2;
517 	widget_fillrect(p, &dst, c_text);
518     }
519 }
520 
textbox_init(textbox_ptr l)521 void textbox_init(textbox_ptr l)
522 {
523     widget_init((widget_ptr) l);
524     l->img = NULL;
525     l->widget.update = textbox_update;
526 }
527 
textbox_put_text(textbox_ptr tb,char * s)528 void textbox_put_text(textbox_ptr tb, char *s)
529 {
530     strcpy(tb->text, s);
531     tb->i = strlen(s);
532     textbox_update_img(tb);
533 }
534 
535 static widget_ptr button_selection;
536 
button_handle_click(widget_ptr p,int button,int x,int y)537 void button_handle_click(widget_ptr p, int button, int x, int y)
538 {
539     state = state_button;
540     button_selection = p;
541 }
542 
button_update(widget_ptr p)543 void button_update(widget_ptr p)
544 {
545     SDL_Rect dst;
546     label_ptr l = (label_ptr) p;
547 
548     if (state == state_button && button_selection == p
549 	    && in_widget(p, lastmousex, lastmousey)) {
550 	widget_lowered_background(p);
551     } else {
552 	widget_raised_background(p);
553     }
554     if (l->img) {
555 	dst.x = 5;
556 	dst.y = 2;
557 	widget_blit(l, l->img, NULL, &dst);
558     }
559 }
560 
button_init(button_ptr b)561 void button_init(button_ptr b)
562 {
563     widget_init((widget_ptr) b);
564     b->text = NULL;
565     b->img = NULL;
566     b->widget.update = button_update;
567     b->widget.handle_click = button_handle_click;
568 }
569 
button_put_text(button_ptr b,char * s)570 void button_put_text(button_ptr b, char *s)
571 {
572     SDL_Surface *tmp;
573 
574     if (b->img) SDL_FreeSurface(b->img);
575     if (b->text) free(b->text);
576     b->text = clonestr(s);
577     tmp = font_render(s, c_text);
578     b->img = SDL_DisplayFormat(tmp);
579     SDL_FreeSurface(tmp);
580 }
581 
set_video(int w,int h)582 void set_video(int w, int h)
583 {
584     int flags;
585     flags = SDL_DOUBLEBUF;
586 
587     screen = SDL_SetVideoMode(w, h, 0, flags);
588     // flags = SDL_FULLSCREEN;
589     // screen = SDL_SetVideoMode(0, 0, 0, flags);
590     init_ctable(screen->format);
591 
592     if (!screen) {
593 	fprintf(stderr, "Can't set video mode: %s\n", SDL_GetError());
594 	exit(1);
595     }
596 
597     //SDL_ShowCursor(SDL_DISABLE);
598 }
599 
set_interrupted(int i)600 void set_interrupted(int i)
601 {
602     interrupted = 1;
603 }
604 
init()605 void init()
606 {
607     int status;
608 
609     signal(SIGINT, set_interrupted);
610     signal(SIGTERM, set_interrupted);
611 
612     //if (SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) {
613     if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) {
614 	fprintf(stderr, "Can't init SDL: %s\n", SDL_GetError());
615 	exit(1);
616     }
617     atexit(SDL_Quit);
618     status = TTF_Init();
619     if (status) {
620 	fprintf(stderr, "Can't init SDL_ttf\n");
621 	exit(-1);
622     }
623     atexit(TTF_Quit);
624 
625     SDL_WM_SetCaption("NetWalk", "NetWalk");
626 
627     SDL_EnableKeyRepeat(150, 50);
628 }
629 
630 SDL_Surface *unmarked_tileimg[64];
631 SDL_Surface *marked_tileimg[64];
632 int level = level_medium;
633 int tick;
634 int tick_old;
635 int pipey, pipex;
636 int pipew, pipeh;
637 int pipet = 4;
638 int move_count;
639 int ms_count;
640 int second_count;
641 
draw_tile(widget_ptr wid,int i,int j)642 void draw_tile(widget_ptr wid, int i, int j)
643 {
644     SDL_Rect rect;
645     int index;
646 
647     rect.x = padding + border + i * (cellw + border);
648     rect.y = padding + border + j * (cellh + border);
649 
650     int const marked = flags[i][j] & 0x1;
651     index = board[i][j] - 1;
652     widget_blit(wid,
653 	(marked?marked_tileimg:unmarked_tileimg)[index],
654 	NULL,
655 	&rect);
656 }
657 
658 typedef struct {
659     int x, y;
660     int dir;
661     int tick;
662 } pulse_s;
663 
664 pulse_s pulse_list[pulse_max];
665 int pulse_count;
666 
new_pulse(int x,int y,int d)667 void new_pulse(int x, int y, int d)
668 {
669     int i, j;
670 
671     if (pulse_count >= pulse_max) return;
672 
673     //stop incoming server pulses
674     if (x == sourcex && (y == sourceybottom || y ==sourceytop) && d != -1) {
675 	return;
676     }
677 
678     i = board[x][y];
679     for (j=0; j<4; j++) {
680 	if ((j != d) && (i & (1 << j))) {
681 	    pulse_list[pulse_count].x = x;
682 	    pulse_list[pulse_count].y = y;
683 	    pulse_list[pulse_count].dir = j;
684 	    pulse_list[pulse_count].tick = tick;
685 	    pulse_count++;
686 	    if (pulse_count >= pulse_max) return;
687 	}
688     }
689 }
690 
server_pulse()691 void server_pulse()
692 {
693     new_pulse(sourcex, sourceybottom, -1);
694     new_pulse(sourcex, sourceytop, -1);
695 }
696 
animate_pulse(widget_ptr wid)697 void animate_pulse(widget_ptr wid)
698 {
699     int i, dt, d;
700     int x, y;
701     SDL_Rect rect;
702     int speed = 500;
703 
704     if (!pulse_count) {
705 	server_pulse();
706     }
707 
708     rect.w = pipet + 2;
709     rect.h = pipet + 2;
710     i = 0;
711     while (i<pulse_count) {
712 	x = pulse_list[i].x;
713 	y = pulse_list[i].y;
714 	d = pulse_list[i].dir;
715 	dt = tick - pulse_list[i].tick;
716 	if (dt > speed) {
717 	    pulse_count--;
718 	    memmove(&pulse_list[i], &pulse_list[i+1], sizeof(pulse_s) * (pulse_count - i));
719 
720 	    add_dir(&x, &y, x, y, d);
721 	    new_pulse(x, y, (d + 2) % 4);
722 	} else {
723 	    //wrap cases:
724 	    if (dir[d].x == -1 && 2 * dt > speed && !x) {
725 		x += boardw;
726 	    }
727 	    if (dir[d].x == 1 && 2 * dt > speed && x == boardw - 1) {
728 		x -= boardw;
729 	    }
730 	    if (dir[d].y == -1 && 2 * dt > speed && !y) {
731 		y += boardh;
732 	    }
733 	    if (dir[d].y == 1 && 2 * dt > speed && y == boardh - 1) {
734 		y -= boardh;
735 	    }
736 
737 	    rect.x = x * (cellw + border) + pipex - 1;
738 	    rect.x += dir[d].x * (cellw + border) * dt / speed;
739 	    rect.x += border + padding;
740 
741 	    rect.y = y * (cellh + border) + border + padding;
742 	    rect.y += dir[d].y * (cellh + border) * dt / speed;
743 	    rect.y += pipey - 1;
744 	    widget_fillrect(wid, &rect, c_pulse);
745 	    i++;
746 	}
747     }
748 }
749 
750 /** \brief Determine which cell of the arena the mouse is currently
751  * pointing at, if any.
752  * \param wid The arena widget.
753  * \param mousex The mouse X position.
754  * \param mousey The mouse Y position.
755  * \param[out] col_p Returns the current cell's column in the arena.
756  * \param[out] row_p Returns the current cell's row in the arena.
757  * \retval 0 The mouse is over the arena and the cell index is returned
758  * through \a row_p and \a col_p.
759  * \retval -1 The mouse is not over the area.
760  */
761 
get_cell_from_mouse_position(widget_ptr const wid,int const mousex,int const mousey,unsigned int * const col_p,unsigned int * const row_p)762 int get_cell_from_mouse_position(widget_ptr const wid,
763 	int const mousex,int const mousey,
764 	unsigned int * const col_p,unsigned int * const row_p)
765 {
766 	if((mousex < wid->x) || (mousey < wid->y))
767 		return -1;
768 
769 	unsigned int const col =
770 		(mousex - padding - border - wid->x) / (cellw + border);
771 	unsigned int const row =
772 		(mousey - padding - border - wid->y) / (cellh + border);
773 
774 	if((col >= boardw) || (row >= boardh))
775 		return -1;
776 
777 	*col_p = col;
778 	*row_p = row;
779 	return 0;
780 }
781 
782 /** \brief Fill a cell (including border) in the arena with a color.
783  * \param wid The arena widget.
784  * \param col The column of the cell to be filled.
785  * \param row The row of the cell to be filled.
786  * \param coloridx The index of the color to use when filling.
787  */
788 
fill_arena_cell(widget_ptr const wid,unsigned int const col,unsigned int const row,int const coloridx)789 void fill_arena_cell(widget_ptr const wid,
790 	unsigned int const col,unsigned int const row,int const coloridx)
791 {
792 	SDL_Rect rect = {
793 		.x = (cellw + border) * col + padding,
794 		.y = (cellh + border) * row + padding,
795 		.w = cellw + 2 * border,
796 		.h = cellh + 2 * border
797 	};
798 	widget_fillrect(wid, &rect, coloridx);
799 }
800 
arena_update(widget_ptr wid)801 void arena_update(widget_ptr wid)
802 {
803     int i, j;
804     SDL_Rect rect;
805     int bc;
806     int c;
807 
808     //draw grid
809     rect.x = padding;
810     rect.y = padding;
811     rect.w = cellw * boardw + (boardw + 1) * border;
812     rect.h = border;
813 
814     if (game_won) bc = c_borderwon;
815     else bc = c_border;
816 
817     for (i=0; i<=boardh; i++) {
818 	widget_fillrect(wid, &rect, bc);
819 	rect.y += cellh + border;
820     }
821 
822     rect.y = padding;
823     rect.w = border;
824     rect.h = cellh * boardh + (boardh + 1) * border;
825     for (i=0; i<=boardw; i++) {
826 	widget_fillrect(wid, &rect, bc);
827 	rect.x += cellw + border;
828     }
829 
830     //highlight cursor
831     unsigned int col;
832     unsigned int row;
833     if(get_cell_from_mouse_position(wid,lastmousex,lastmousey,&col,&row) == 0)
834     {
835 	/* highlight the cell the mouse is pointing at */
836 	fill_arena_cell(wid,col,row,c_highlight);
837 
838 	if(wrap_flag)
839 	{
840 		/*
841 		 * If the highlighted cell is an edge cell, also
842 		 * highlight the corresponding cells on opposite
843 		 * edges.  This will make it easier to work with large
844 		 * wrapped grids.
845 		 */
846 		if(col == 0)
847 			fill_arena_cell(wid,boardw - 1,row,c_edgematch);
848 		else
849 		if(col == boardw - 1)
850 			fill_arena_cell(wid,0,row,c_edgematch);
851 
852 		if(row == 0)
853 			fill_arena_cell(wid,col,boardh - 1,c_edgematch);
854 		else
855 		if(row == boardh - 1)
856 			fill_arena_cell(wid,col,0,c_edgematch);
857 	}
858     }
859 
860     //draw in tiles
861     for (i=0; i<boardw; i++) {
862 	for (j=0; j<boardh; j++) {
863 	    if (board[i][j]) {
864 		draw_tile(wid, i, j);
865 	    }
866 	}
867     }
868     //draw server
869     if (game_won) c = c_serverwon;
870     else c = c_server;
871 
872     rect.x = padding + border + (cellw + border) * sourcex;
873     rect.y = padding + border + (cellh + border) * sourceytop;
874 
875     rect.x += 5;
876     rect.y += 5;
877     rect.w = cellw - 10;
878     rect.h = cellh - 5;
879     widget_fillrect(wid, &rect, c);
880 
881     rect.y = padding + border + (cellh + border) * sourceybottom;
882     widget_fillrect(wid, &rect, c);
883 
884     //victory animation
885     if (game_won) {
886 	animate_pulse(wid);
887     }
888 }
889 
read_field(FILE * fp)890 char* read_field(FILE *fp)
891 {
892     char *r;
893     int i;
894     char c;
895 
896     r = (char *) malloc(1024);
897     i = 0;
898 
899     for(;;) {
900 	c = getc(fp);
901 
902 	if (feof(fp)) {
903 	    free(r);
904 	    return NULL;
905 	}
906 
907 	switch(c) {
908 	    case ',':
909 		r[i] = 0;
910 		return r;
911 	    case '\n':
912 		r[i] = 0;
913 		return r;
914 	    default:
915 		r[i] = c;
916 		i++;
917 		break;
918 	}
919     }
920 }
921 
update_hsw()922 void update_hsw()
923 {
924     int i;
925     for (i=0; i<level_max; i++) {
926 	if (hstable[i]->name) {
927 	    label_put_text(hsw[i]->name, hstable[i]->name);
928 	} else {
929 	    label_put_text(hsw[i]->name, "None");
930 	}
931 	if (hstable[i]->time != -1) {
932 	    char s[80];
933 
934 	    sprintf(s, "%d", hstable[i]->time);
935 	    label_put_text(hsw[i]->time, s);
936 	} else {
937 	    label_put_text(hsw[i]->name, "None");
938 	}
939     }
940 }
941 
read_hstable()942 void read_hstable()
943 {
944     FILE *fp;
945     int i;
946 
947     for (i=0; i<level_max; i++) {
948 	hstable[i]->name = NULL;
949 	hstable[i]->time = -1;
950     }
951 
952     fp = fopen(config->hsfile, "r");
953     if (!fp) return;
954 
955     for(i=0; i<level_max; i++) {
956 	char *s;
957 	s = read_field(fp);
958 	if (!s) goto done;
959 
960 	hstable[i]->name = s;
961 
962 	s = read_field(fp);
963 	if (!s) goto done;
964 
965 	hstable[i]->time = atoi(s);
966 	free(s);
967     }
968 
969 done:
970     fclose(fp);
971 }
972 
write_hstable()973 void write_hstable()
974 {
975     FILE *fp;
976     int i;
977 
978     fp = fopen(config->hsfile, "w");
979     if (!fp) return;
980 
981     for(i=0; i<level_max; i++) {
982 	fprintf(fp, "%s,%d\n", hstable[i]->name, hstable[i]->time);
983     }
984 
985     fclose(fp);
986 }
987 
988 int enkludge;
989 
enter_name_open()990 void enter_name_open()
991 {
992     modalwindow = enter_name_window;
993     enkludge = 1;
994 }
995 
enter_name_close()996 void enter_name_close()
997 {
998     modalwindow = NULL;
999     enkludge = 0;
1000 
1001     player_name = tb_en1->text;
1002 
1003     if (hstable[level]->name) {
1004 	free(hstable[level]->name);
1005     }
1006     hstable[level]->name = clonestr(player_name);
1007     hstable[level]->time = second_count;
1008     update_hsw();
1009     write_hstable();
1010 }
1011 
check_hs()1012 void check_hs()
1013 {
1014     if (hstable[level]->time == -1 || second_count < hstable[level]->time) {
1015 	enter_name_open();
1016     }
1017 }
1018 
init_tileimg(SDL_Surface * tileimg[64],int bgcolor)1019 void init_tileimg(SDL_Surface * tileimg[64],int bgcolor)
1020 {
1021     int i, j;
1022     SDL_PixelFormat *fmt = screen->format;
1023     SDL_Rect rect;
1024     int c;
1025 
1026     pipex = cellw / 2 - pipet / 2;
1027     pipey = cellw / 2 - pipet / 2;
1028     pipew = cellw / 2 + pipet / 2;
1029     pipeh = cellh / 2 + pipet / 2;
1030 
1031     SDL_Rect entirecell = (SDL_Rect){
1032 	.x = 0,
1033 	.y = 0,
1034 	.w = cellw,
1035 	.h = cellh
1036     };
1037 
1038     for (i=0; i<64; i++) {
1039 	tileimg[i] = SDL_CreateRGBSurface(0, cellw, cellh, fmt->BitsPerPixel,
1040 	    fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
1041 
1042 	SDL_FillRect(tileimg[i], &entirecell, ctable[bgcolor]);
1043 
1044 	for (j=0; j<4; j++) {
1045 	    if ((i + 1) & (1 << j)) {
1046 		switch (j) {
1047 		    case 0:
1048 			rect.x = pipex;
1049 			rect.y = 0;
1050 			rect.w = pipet;
1051 			rect.h = pipeh;
1052 			break;
1053 		    case 1:
1054 			rect.x = pipex;
1055 			rect.y = pipey;
1056 			rect.w = pipew;
1057 			rect.h = pipet;
1058 			break;
1059 		    case 2:
1060 			rect.x = pipex;
1061 			rect.y = pipey;
1062 			rect.w = pipet;
1063 			rect.h = pipeh;
1064 			break;
1065 		    case 3:
1066 			rect.x = 0;
1067 			rect.y = pipey;
1068 			rect.w = pipew;
1069 			rect.h = pipet;
1070 			break;
1071 		}
1072 		if (i >= 16) {
1073 		    c = ctable[c_on];
1074 		} else c = ctable[c_off];
1075 		SDL_FillRect(tileimg[i], &rect, c);
1076 	    }
1077 	}
1078     }
1079 
1080     for (i=1; i<32; i*=2) {
1081 	rect.x = cellw / 2 - 2 * pipet;
1082 	rect.y = cellh / 2 - 2 * pipet;
1083 	rect.w = pipet * 4;
1084 	rect.h = pipet * 4;
1085 	rect.x++;
1086 	rect.w-=2;
1087 	rect.y++;
1088 	rect.h-=2;
1089 	/*
1090 	SDL_FillRect(tileimg[i-1], &rect, ctable[c_off]);
1091 	SDL_FillRect(tileimg[i-1+16], &rect, ctable[c_on]);
1092 	rect.x++;
1093 	rect.w-=2;
1094 	rect.y++;
1095 	rect.h-=2;
1096 	*/
1097 	SDL_FillRect(tileimg[i-1], &rect, ctable[c_down]);
1098 	SDL_FillRect(tileimg[i-1+16], &rect, ctable[c_up]);
1099     }
1100 }
1101 
reset_move_count()1102 void reset_move_count()
1103 {
1104     move_count = 0;
1105     label_put_text(l_moves, "moves: 0");
1106 }
1107 
increment_move_count()1108 void increment_move_count()
1109 {
1110     char s[80];
1111     move_count++;
1112     sprintf(s, "moves: %d", move_count);
1113     label_put_text(l_moves, s);
1114 }
1115 
reset_time()1116 void reset_time()
1117 {
1118     label_put_text(l_time, "time: 0");
1119 }
1120 
update_time()1121 void update_time()
1122 {
1123     static char s[80];
1124 
1125     ms_count += tick - tick_old;
1126     while (ms_count >= 1000) {
1127 	ms_count -= 1000;
1128 	second_count++;
1129 	sprintf(s, "time: %d", second_count);
1130 	label_put_text(l_time, s);
1131     }
1132 }
1133 
resize()1134 void resize()
1135 //position everything based on board size
1136 {
1137     int w, h;
1138 
1139     sourcex = boardw / 2 - 1;
1140     sourceytop =  boardh / 2;
1141     sourceybottom = sourceytop + 1;
1142 
1143     w = cellw * boardw + (boardw + 1) * border + 2 * padding;
1144     h = cellh * boardh + (boardh + 1) * border + 2 * padding;
1145     widget_put_geometry(arena, 0, vsize, w, h);
1146     set_video(w, h + 2 * vsize);
1147     widget_put_geometry(root, 0, 0, w, h + 2 * vsize);
1148     widget_put_geometry(menu, 0, 0, w, vsize);
1149     menubar_auto_layout(menu);
1150     widget_put_geometry(statusbar, 0, h + vsize, w, vsize);
1151 
1152     widget_put_geometry(l_moves, 8, h + vsize, 64, vsize);
1153     widget_put_geometry(l_time, w - 48, h + vsize, 64, vsize);
1154 
1155     widget_put_geometry((widget_ptr) about_window,
1156 	    w/2 - 50, h/2 - 50, 100, 100);
1157     widget_put_geometry((widget_ptr) l_about1, 10, 10, 60, vsize);
1158     widget_put_geometry((widget_ptr) l_about2, 10, 30, 60, vsize);
1159     widget_put_geometry((widget_ptr) b_about1, 30, 80, 30, vsize);
1160 
1161     /* vertical sizes and positions for the high score window */
1162     int const hslabely = 5;
1163     int const hslabelheight = vsize;
1164     int const hslisty = hslabely + hslabelheight + 8;
1165     int const hslisteachheight = vsize;
1166     int const hslistheight = hslisteachheight * level_max;
1167     int const hsoky = hslisty + hslistheight + 8;
1168     int const hsokheight = vsize;
1169     int const hswindowheight = hsoky + hsokheight + 5;
1170 
1171     widget_put_geometry((widget_ptr) hs_window,
1172 	    w/2 - 75, h/2 - 60, 170, hswindowheight);
1173     widget_put_geometry((widget_ptr) l_hs1, 10, hslabely, 60, hslabelheight);
1174     widget_put_geometry((widget_ptr) b_hs1, 100, hsoky, 30, hsokheight);
1175 
1176     {
1177 	int i;
1178 	for (i=0; i<level_max; i++) {
1179 	    int y = hslisty + (hslisteachheight * i);
1180 	    widget_put_geometry((widget_ptr) hsw[i]->level,
1181 		10, y, 20, hslisteachheight);
1182 	    widget_put_geometry((widget_ptr) hsw[i]->time,
1183 		60, y, 20, hslisteachheight);
1184 	    widget_put_geometry((widget_ptr) hsw[i]->name,
1185 		90, y, 20, hslisteachheight);
1186 	}
1187     }
1188 
1189     widget_put_geometry((widget_ptr) enter_name_window,
1190 	    10, h/2 - 30, w - 20, 67);
1191     widget_put_geometry((widget_ptr) l_en1, 10, 0, 60, vsize);
1192     widget_put_geometry((widget_ptr) tb_en1, 5, vsize + 5, w - 30, vsize);
1193     widget_put_geometry((widget_ptr) b_en1, w - 60, 45, 30, vsize);
1194 
1195     ((widget_ptr) root)->computexy((widget_ptr) root);
1196     ((widget_ptr) about_window)->computexy((widget_ptr) about_window);
1197     ((widget_ptr) hs_window)->computexy((widget_ptr) hs_window);
1198     ((widget_ptr) enter_name_window)->computexy((widget_ptr) enter_name_window);
1199 }
1200 
new_game()1201 void new_game()
1202 {
1203     char s[BUFSIZ];
1204     strcpy(s,"NetWalk - ");
1205     strcat(s,level_name[level]);
1206     SDL_WM_SetCaption(s,s);
1207 
1208     switch(level) {
1209 	case level_easy:
1210 	    boardw = 5; boardh = 5;
1211 	    wrap_flag = 0;
1212 	    no_fourway = 0;
1213 	    break;
1214 	case level_medium:
1215 	    boardw = 10; boardh = 9;
1216 	    wrap_flag = 0;
1217 	    no_fourway = 0;
1218 	    break;
1219 	case level_hard:
1220 	    boardw = 10; boardh = 9;
1221 	    wrap_flag = 1;
1222 	    no_fourway = 1;
1223 	    break;
1224 	case level_veryhard:
1225 	    boardw = 20; boardh = 18;
1226 	    wrap_flag = 1;
1227 	    no_fourway = 1;
1228 	    break;
1229 	case level_giant:
1230 	    boardw = 50; boardh = 50;
1231 	    wrap_flag = 0;
1232 	    no_fourway = 1;
1233 	    break;
1234 	case level_absurd:
1235 	    boardw = 50; boardh = 50;
1236 	    wrap_flag = 1;
1237 	    no_fourway = 1;
1238 	    break;
1239 	default:
1240 	    break;
1241     }
1242     resize();
1243     srand(time(NULL));
1244     generate_maze();
1245     clear_flags();
1246     scramble();
1247     check_live();
1248     reset_move_count();
1249     reset_time();
1250     ms_count = 0;
1251     tick = SDL_GetTicks();
1252     second_count = 0;
1253 }
1254 
handle_mousebuttonup(SDL_Event * event)1255 void handle_mousebuttonup(SDL_Event *event)
1256 {
1257     int x = event->button.x;
1258     int y = event->button.y;
1259     if (openedmenu) {
1260 	int i;
1261 	menuitem_ptr it;
1262 	menu_ptr m;
1263 
1264 	m = openedmenu->submenu;
1265 	for (i=0; i<m->item_count; i++) {
1266 	    it = m->item_list[i];
1267 	    if (in_widget((widget_ptr) it, x, y)) {
1268 		widget_raise_signal((widget_ptr) it, signal_activate);
1269 		break;
1270 	    }
1271 	}
1272 	openedmenu = NULL;
1273 
1274 	return;
1275     } else if (state == state_button) {
1276 	state = state_game;
1277 	if (in_widget(button_selection, x, y)) {
1278 	    widget_raise_signal(button_selection, signal_activate);
1279 	    return;
1280 	}
1281     }
1282 }
1283 
handle_click(int button,int x,int y)1284 void handle_click(int button, int x, int y)
1285 {
1286     widget_ptr wid;
1287 
1288     if (modalwindow) {
1289 	wid = (widget_ptr) modalwindow;
1290 	if (in_widget(wid, x, y) && (wid->handle_click)) {
1291 	    wid->handle_click(wid, button, x, y);
1292 	}
1293 	return;
1294     }
1295 
1296     wid = (widget_ptr) root;
1297     wid->handle_click(wid, button, x, y);
1298 }
1299 
arena_handle_click(widget_ptr p,int button,int x,int y)1300 void arena_handle_click(widget_ptr p, int button, int x, int y)
1301 {
1302     int i, j;
1303     int d;
1304 
1305     if (state != state_game) return;
1306 
1307     i = (x - padding - border) / (cellw + border);
1308     j = (y - padding - border) / (cellh + border);
1309     if (i >= boardw || j >= boardh) return;
1310 
1311     if (game_won) {
1312 	if (i == sourcex && (j == sourceytop || j == sourceybottom)) {
1313 	    new_pulse(sourcex, sourceybottom, -1);
1314 	    new_pulse(sourcex, sourceytop, -1);
1315 	} else {
1316 	    new_pulse(i, j, -1);
1317 	}
1318 	return;
1319     }
1320 
1321     //temporarily merge server squares
1322     board[sourcex][sourceybottom] |= board[sourcex][sourceytop] & 1;
1323     if (i == sourcex && j == sourceytop) {
1324 	j = sourceybottom;
1325     }
1326     d = board[i][j] & 15;
1327     switch(button) {
1328 	case SDL_BUTTON_LEFT:
1329 	    d = rotatecw(d, 3);
1330 	    increment_move_count();
1331 	    break;
1332 	case SDL_BUTTON_RIGHT:
1333 	    d = rotatecw(d, 1);
1334 	    increment_move_count();
1335 	    break;
1336     }
1337     board[i][j] &= ~15;
1338     board[i][j] += d;
1339 
1340     board[sourcex][sourceytop] &= ~1;
1341     board[sourcex][sourceytop] |= board[sourcex][sourceybottom] & 1;
1342     board[sourcex][sourceybottom] &= ~1;
1343 
1344     if(button == SDL_BUTTON_MIDDLE)
1345 	flags[i][j] ^= 0x1;
1346 
1347     check_live();
1348     if (game_won) {
1349 	pulse_count = 0;
1350 
1351 	check_hs();
1352     }
1353 }
1354 
quit()1355 void quit()
1356 {
1357     state = state_quit;
1358 }
1359 
quit_menu(widget_ptr w,void * data)1360 void quit_menu(widget_ptr w, void *data)
1361 {
1362     quit();
1363 }
1364 
new_game_menu(widget_ptr w,void * data)1365 void new_game_menu(widget_ptr w, void *data)
1366 {
1367     new_game();
1368 }
1369 
about_open(widget_ptr w,void * data)1370 void about_open(widget_ptr w, void *data)
1371 {
1372     modalwindow = about_window;
1373 }
1374 
about_close(widget_ptr w,void * data)1375 void about_close(widget_ptr w, void *data)
1376 {
1377     modalwindow = NULL;
1378 }
1379 
hs_open(widget_ptr w,void * data)1380 void hs_open(widget_ptr w, void *data)
1381 {
1382     modalwindow = hs_window;
1383 }
1384 
hs_close(widget_ptr w,void * data)1385 void hs_close(widget_ptr w, void *data)
1386 {
1387     modalwindow = NULL;
1388 }
1389 
set_level(widget_ptr w,void * data)1390 void set_level(widget_ptr w, void *data)
1391 {
1392     level = (int) data;
1393     new_game();
1394 }
1395 
handle_key(int key,int mod)1396 void handle_key(int key, int mod)
1397 {
1398     if (openedmenu) {
1399 	switch(key) {
1400 	    case SDLK_ESCAPE:
1401 		openedmenu = NULL;
1402 	}
1403 	return;
1404     }
1405 
1406     if (enkludge) {
1407 	switch(key) {
1408 	    case SDLK_LEFT:
1409 		textbox_left(tb_en1);
1410 		break;
1411 	    case SDLK_RIGHT:
1412 		textbox_right(tb_en1);
1413 		break;
1414 	    case SDLK_DELETE:
1415 		textbox_delete(tb_en1);
1416 		break;
1417 	    case SDLK_BACKSPACE:
1418 		textbox_backspace(tb_en1);
1419 		break;
1420 	    default:
1421 		if (key < 256 && key >= 32) {
1422 		    if (mod & KMOD_SHIFT) {
1423 			textbox_insert(tb_en1, shifttable[key]);
1424 		    } else {
1425 			textbox_insert(tb_en1, key);
1426 		    }
1427 		}
1428 		break;
1429 	}
1430 	return;
1431     }
1432 
1433     switch(key) {
1434 	case SDLK_d:
1435 	    enter_name_open();
1436 	    break;
1437 	case SDLK_ESCAPE:
1438 	case SDLK_q:
1439 	    quit();
1440 	    break;
1441 	case SDLK_F2:
1442 	    new_game();
1443 	    break;
1444     }
1445 }
1446 
update_screen()1447 void update_screen()
1448 {
1449     SDL_FillRect(screen, NULL, 0);
1450     widget_update((widget_ptr) root);
1451     if (openedmenu) {
1452 	int i;
1453 	menuitem_ptr it;
1454 
1455 	for (i=0; i<menu->item_count; i++) {
1456 	    it = menu->item_list[i];
1457 	    if (in_widget((widget_ptr) it, lastmousex, lastmousey)) {
1458 		open_submenu((widget_ptr) it, NULL);
1459 	    }
1460 	}
1461 	menu_update(openedmenu->submenu);
1462     }
1463     if (modalwindow) {
1464 	widget_update((widget_ptr) modalwindow);
1465     }
1466     SDL_Flip(screen);
1467 }
1468 
window_update(widget_ptr p)1469 void window_update(widget_ptr p)
1470 {
1471     window_ptr w = (window_ptr) p;
1472     widget_ptr wid;
1473     int i;
1474     SDL_Rect dst;
1475 
1476     if (p != (widget_ptr) root) {
1477 	dst.x = -1;
1478 	dst.y = -1;
1479 	dst.w = p->w + 2;
1480 	dst.h = p->h + 2;
1481 	widget_fillrect(p, &dst, c_windowborder);
1482 	widget_fill(p, c_background);
1483     }
1484 
1485     for (i=0; i<w->widget_count; i++) {
1486 	wid = w->widget_list[i];
1487 	widget_update(wid);
1488     }
1489 }
1490 
window_handle_click(widget_ptr p,int button,int x,int y)1491 void window_handle_click(widget_ptr p, int button, int x, int y)
1492 {
1493     widget_ptr wid;
1494     window_ptr window = (window_ptr) p;
1495     int i;
1496 
1497     for (i=0; i<window->widget_count; i++) {
1498 	wid = window->widget_list[i];
1499 	if (in_widget(wid, x, y) && (wid->handle_click)) {
1500 	    wid->handle_click(wid, button, x - wid->x, y - wid->y);
1501 	    return;
1502 	}
1503     }
1504 }
1505 
window_add_widget(window_ptr r,void * p)1506 void window_add_widget(window_ptr r, void *p)
1507 {
1508     widget_ptr wid = (widget_ptr) p;
1509     r->widget_list[r->widget_count] = wid;
1510     r->widget_count++;
1511     wid->parent = (widget_ptr) r;
1512     wid->x += r->widget.x;
1513     wid->y += r->widget.y;
1514 }
1515 
window_computexy(widget_ptr wid)1516 void window_computexy(widget_ptr wid)
1517 {
1518     int i;
1519     window_ptr w = (window_ptr) wid;
1520 
1521     widget_computexy(wid);
1522     for (i=0; i<w->widget_count; i++) {
1523 	w->widget_list[i]->computexy(w->widget_list[i]);
1524     }
1525 }
1526 
window_init(window_ptr w)1527 void window_init(window_ptr w)
1528 {
1529     widget_init((widget_ptr) w);
1530     w->widget_count = 0;
1531     w->widget.update = window_update;
1532     w->widget.handle_click = window_handle_click;
1533     w->widget.computexy = window_computexy;
1534 }
1535 
add_shiftstring(char * s1,char * s2)1536 static void add_shiftstring(char *s1, char *s2)
1537 {
1538     int i;
1539 
1540     for (i=0; i<strlen(s1); i++) {
1541 	shifttable[(int) s1[i]] = s2[i];
1542     }
1543 }
1544 
main(int argc,char * argv[])1545 int main(int argc, char *argv[])
1546 {
1547     SDL_Event event;
1548 
1549     //setup names
1550     level_name[level_easy] = "Newbie";
1551     level_name[level_medium] = "Normal";
1552     level_name[level_hard] = "Nerd";
1553     level_name[level_veryhard] = "Nutcase";
1554     level_name[level_giant] = "Nonsense";
1555     level_name[level_absurd] = "No Sleep";
1556 
1557     //setup shifttable
1558     {
1559 	int i;
1560 
1561 	for (i=0; i<256; i++) shifttable[i] = i;
1562 
1563 	for (i='a'; i<='z'; i++) {
1564 	    shifttable[i] = i - 32;
1565 	}
1566 
1567 	add_shiftstring("1234567890-=", "!@#$%^&*()_+");
1568 	add_shiftstring("[]\\;',./`", "{}|:\"<>?~");
1569     }
1570 
1571     config_load(config);
1572     read_hstable();
1573     init();
1574 
1575     init_rgbtable();
1576 
1577     font = TTF_OpenFont(config->fontname, config->fontsize);
1578     if (!font) {
1579 	fprintf(stderr, "error loading font %s\n", config->fontname);
1580 	exit(1);
1581     }
1582 
1583     window_init(root);
1584 
1585     //need to set video mode here to initialize colour table
1586     set_video(100, 100);
1587 
1588     //setup enter name box
1589     {
1590 	window_init(enter_name_window);
1591 
1592 	label_init(l_en1);
1593 	label_put_text(l_en1, "Enter name:");
1594 	window_add_widget(enter_name_window, l_en1);
1595 
1596 	textbox_init(tb_en1);
1597 	textbox_put_text(tb_en1, "Anonymous");
1598 	window_add_widget(enter_name_window, tb_en1);
1599 
1600 	button_init(b_en1);
1601 	button_put_text(b_en1, "Ok");
1602 	window_add_widget(enter_name_window, b_en1);
1603 	widget_put_handler((widget_ptr) b_en1, enter_name_close, signal_activate);
1604     }
1605 
1606     //setup the "arena": where the action is
1607     {
1608 	widget_init((widget_ptr) arena);
1609 	arena->widget.update = arena_update;
1610 	arena->widget.handle_click = arena_handle_click;
1611 	window_add_widget(root, arena);
1612     }
1613 
1614     //status bar: mainly for showing the time
1615     {
1616 	widget_init((widget_ptr) statusbar);
1617 	statusbar->update = statusbar_update;
1618 	window_add_widget(root, statusbar);
1619 
1620 	//setup moves and time
1621 	label_init(l_moves);
1622 	if (config->showmoves) {
1623 	    window_add_widget(root, l_moves);
1624 	}
1625 
1626 	label_init(l_time);
1627 	window_add_widget(root, l_time);
1628     }
1629 
1630     //setup the menus
1631     {
1632 	menuitem_t it1, it2;
1633 	menuitem_ptr it;
1634 	menu_t m1, m2;
1635 
1636 	int i;
1637 
1638 	menubar_init(menu);
1639 	window_add_widget(root, menu);
1640 	menuitem_init(it1);
1641 	menuitem_put_text(it1, "Game");
1642 	menubar_add_item(menu, it1);
1643 	menuitem_init(it2);
1644 	menuitem_put_text(it2, "Help");
1645 	menubar_add_item(menu, it2);
1646 
1647 	menu_init(m1);
1648 
1649 	it = menuitem_new();
1650 	menuitem_put_text(it, "New game");
1651 	widget_put_handler((widget_ptr) it, new_game_menu, signal_activate);
1652 	menu_add_item(m1, it);
1653 
1654 	for (i=0; i<level_max; i++) {
1655 	    it = menuitem_new();
1656 	    menuitem_put_text(it, level_name[i]);
1657 	    widget_put_handler_data((widget_ptr) it,
1658 		    set_level, (void *) i, signal_activate);
1659 	    menu_add_item(m1, it);
1660 	}
1661 	it = menuitem_new();
1662 	menuitem_put_text(it, "High Scores");
1663 	widget_put_handler((widget_ptr) it, hs_open, signal_activate);
1664 	menu_add_item(m1, it);
1665 	it = menuitem_new();
1666 	menuitem_put_text(it, "Quit");
1667 	widget_put_handler((widget_ptr) it, quit_menu, signal_activate);
1668 	menu_add_item(m1, it);
1669 
1670 	menuitem_set_submenu(it1, m1);
1671 
1672 	menu_init(m2);
1673 
1674 	it = menuitem_new();
1675 	menuitem_put_text(it, "About");
1676 	widget_put_handler((widget_ptr) it, about_open, signal_activate);
1677 	menu_add_item(m2, it);
1678 
1679 	menuitem_set_submenu(it2, m2);
1680     }
1681 
1682     //setup about box
1683     {
1684 	window_init(about_window);
1685 
1686 	label_init(l_about1);
1687 	label_put_text(l_about1, "NetWalk " VERSION_STRING);
1688 	window_add_widget(about_window, l_about1);
1689 
1690 	label_init(l_about2);
1691 	label_put_text(l_about2, "Ben Lynn");
1692 	window_add_widget(about_window, l_about2);
1693 
1694 	button_init(b_about1);
1695 	button_put_text(b_about1, "Ok");
1696 	window_add_widget(about_window, b_about1);
1697 	widget_put_handler((widget_ptr) b_about1, about_close, signal_activate);
1698     }
1699 
1700     //setup hiscores box
1701     {
1702 	int i;
1703 	window_init(hs_window);
1704 
1705 	label_init(l_hs1);
1706 	label_put_text(l_hs1, "High Scores");
1707 	window_add_widget(hs_window, l_hs1);
1708 
1709 	button_init(b_hs1);
1710 	button_put_text(b_hs1, "Ok");
1711 	window_add_widget(hs_window, b_hs1);
1712 	widget_put_handler((widget_ptr) b_hs1, hs_close, signal_activate);
1713 
1714 	for (i=0; i<level_max; i++) {
1715 	    label_init(hsw[i]->level);
1716 	    label_put_text(hsw[i]->level, level_name[i]);
1717 	    window_add_widget(hs_window, hsw[i]->level);
1718 	    label_init(hsw[i]->name);
1719 	    window_add_widget(hs_window, hsw[i]->name);
1720 	    label_init(hsw[i]->time);
1721 	    window_add_widget(hs_window, hsw[i]->time);
1722 	}
1723     }
1724 
1725     resize();
1726 
1727     update_hsw();
1728 
1729     init_tileimg(unmarked_tileimg,c_unmarkedbg);
1730     init_tileimg(marked_tileimg,c_markedbg);
1731     new_game();
1732 
1733     while (state != state_quit && !interrupted) {
1734 	tick_old = tick;
1735 	tick = SDL_GetTicks();
1736 	if (!game_won) {
1737 	    update_time();
1738 	}
1739 	SDL_GetMouseState(&lastmousex, &lastmousey);
1740 	while (SDL_PollEvent(&event)) {
1741 	    switch (event.type) {
1742 		case SDL_KEYDOWN:
1743 		    handle_key(event.key.keysym.sym, SDL_GetModState());
1744 		    break;
1745 		case SDL_MOUSEBUTTONDOWN:
1746 		    handle_click(event.button.button, event.button.x, event.button.y);
1747 		    break;
1748 		case SDL_MOUSEBUTTONUP:
1749 		    handle_mousebuttonup(&event);
1750 		    break;
1751 		case SDL_QUIT:
1752 		    quit();
1753 		    break;
1754 	    }
1755 	}
1756 	update_screen();
1757 	SDL_Delay(20);
1758     }
1759     return 0;
1760 }
1761