Tue May 9 13:19:01 PDT 2006
[netwalk.git] / main.c
bloba90b5b4c68cab81541a46cb2835cad8bf5ee06b9
1 /* NetWalk main program
2 * Ben Lynn
3 */
4 /*
5 Copyright (C) 2004 Benjamin Lynn (blynn@cs.stanford.edu)
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.
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.
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.
21 #include <assert.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <math.h>
25 #include <time.h>
27 #include <sys/types.h> //for opendir et al.
28 #include <dirent.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
33 #include <stdlib.h>
34 #include <signal.h>
36 #include <SDL.h>
37 #include <SDL_ttf.h>
39 #include "version.h"
41 #include "widget.h"
42 #include "colour.h"
43 #include "game.h"
45 #include "config.h"
46 #include "util.h"
48 static int interrupted = 0;
50 enum {
51 vsize = 18
54 enum {
55 pulse_max = 1024
58 enum {
59 state_game,
60 state_button,
61 state_quit
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
75 enum {
76 cellw = 24,
77 cellh = 24,
78 border = 1,
79 padding = 10
82 char shifttable[256];
84 struct hsentry_s {
85 char *name;
86 int time;
88 typedef struct hsentry_s *hsentry_ptr;
89 typedef struct hsentry_s hsentry_t[1];
91 hsentry_t hstable[level_max];
92 char *level_name[level_max];
94 //TODO use this somehow
95 struct gameparm_s {
96 int boardw, boardh;
97 int wrap;
99 typedef struct gameparm_s *gameparm_ptr;
100 typedef struct gameparm_s gameparm_t[1];
102 gameparm_t gp;
104 static config_t config;
106 SDL_Surface *screen;
107 TTF_Font *font;
108 int lastmousex, lastmousey;
109 char *player_name;
110 int game_won;
111 int state;
113 struct button_s {
114 struct widget_s widget;
115 char *text;
116 SDL_Surface *img;
118 typedef struct button_s button_t[1];
119 typedef struct button_s *button_ptr;
121 struct label_s {
122 struct widget_s widget;
123 char *text;
124 SDL_Surface *img;
126 typedef struct label_s label_t[1];
127 typedef struct label_s *label_ptr;
129 struct textbox_s {
130 struct widget_s widget;
131 char text[1024];
132 int i;
133 SDL_Surface *img;
135 typedef struct textbox_s textbox_t[1];
136 typedef struct textbox_s *textbox_ptr;
138 struct arena_s {
139 struct widget_s widget;
141 typedef struct arena_s arena_t[1];
142 typedef struct arena_s *arena_ptr;
144 struct menuitem_s {
145 struct widget_s widget;
146 char *text;
147 SDL_Surface *img;
148 struct menu_s *submenu;
150 typedef struct menuitem_s menuitem_t[1];
151 typedef struct menuitem_s *menuitem_ptr;
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;
159 typedef struct menu_s menu_t[1];
160 typedef struct menu_s *menu_ptr;
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;
168 typedef struct menubar_s menubar_t[1];
169 typedef struct menubar_s *menubar_ptr;
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;
178 typedef struct window_s window_t[1];
179 typedef struct window_s *window_ptr;
181 struct hsw_s {
182 label_t level;
183 label_t time;
184 label_t name;
186 typedef struct hsw_s *hsw_ptr;
187 typedef struct hsw_s hsw_t[1];
189 hsw_t hsw[level_max];
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;
206 label_t l_hs1;
207 button_t b_hs1;
209 label_t l_en1;
210 textbox_t tb_en1;
211 button_t b_en1;
213 SDL_Surface *font_render(char *s, int c)
215 return TTF_RenderText_Solid(font, s, rgbtable[c]);
218 void statusbar_update(widget_ptr w)
220 widget_lowered_background(w);
223 void menu_init(menu_ptr m)
225 widget_init((widget_ptr) m);
226 m->item_count = 0;
229 void menu_update(menu_ptr m)
231 int i;
232 menuitem_ptr it;
233 SDL_Rect rect;
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);
245 rect.x = 8;
246 rect.y = vsize * i + 4;
247 widget_blit((widget_ptr) m, it->img, NULL, &rect);
251 void menu_add_item(menu_ptr m, menuitem_ptr it)
253 m->item_list[m->item_count] = it;
254 m->item_count++;
257 void menuitem_init(menuitem_ptr m)
259 widget_init((widget_ptr) m);
260 m->text = NULL;
261 m->img = NULL;
262 m->submenu = NULL;
265 menuitem_ptr menuitem_new()
267 menuitem_ptr it;
269 it = (menuitem_ptr) malloc(sizeof(menuitem_t));
270 menuitem_init(it);
272 return it;
275 void menuitem_put_text(menuitem_ptr m, char *s)
277 SDL_Surface *tmp;
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);
286 void open_submenu(widget_ptr p, void *data)
288 int i;
289 int w = 0;
290 menuitem_ptr it;
292 openedmenu = (menuitem_ptr) p;
293 menu_ptr m = openedmenu->submenu;
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;
299 w += 12;
301 m->widget.x = m->widget.parent->x;
302 m->widget.y = m->widget.parent->y + vsize;
304 m->widget.w = w;
305 m->widget.h = vsize * m->item_count + 1;
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;
316 void menuitem_set_submenu(menuitem_ptr it, menu_ptr m)
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;
324 void menubar_update(widget_ptr wid)
326 SDL_Rect dst;
327 menubar_ptr m = (menubar_ptr) wid;
328 menuitem_ptr it;
329 int i;
331 widget_raised_background(wid);
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);
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);
350 void menubar_handle_click(widget_ptr p, int button, int x, int y)
352 int i;
353 menubar_ptr m = (menubar_ptr) p;
354 menuitem_ptr it;
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;
365 void menubar_init(menubar_ptr m)
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;
373 void menubar_add_item(menubar_ptr m, menuitem_ptr it)
375 m->item_list[m->item_count] = it;
376 m->item_count++;
377 it->widget.parent = (widget_ptr) m;
380 void menubar_auto_layout(menubar_ptr m)
382 int i, x, y;
383 menuitem_ptr it;
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;
399 void label_update(widget_ptr p)
401 SDL_Rect dst;
402 label_ptr l = (label_ptr) p;
404 if (l->img) {
405 dst.x = 0;
406 dst.y = 4;
407 widget_blit(l, l->img, NULL, &dst);
411 void label_init(label_ptr l)
413 widget_init((widget_ptr) l);
414 l->text = NULL;
415 l->img = NULL;
416 l->widget.update = label_update;
419 void label_put_text(label_ptr l, char *s)
421 SDL_Surface *tmp;
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);
431 void textbox_update_img(textbox_ptr tb)
433 SDL_Surface *tmp;
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;
445 void textbox_left(textbox_ptr tb)
447 if (tb->i > 0) tb->i--;
450 void textbox_right(textbox_ptr tb)
452 if (tb->i < strlen(tb->text)) tb->i++;
455 void textbox_delete(textbox_ptr tb)
457 if (tb->i > 0) {
458 tb->i--;
459 tb->text[tb->i] = 0;
460 textbox_update_img(tb);
464 void textbox_backspace(textbox_ptr tb)
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);
474 void textbox_insert(textbox_ptr tb, char c)
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);
483 void textbox_update(widget_ptr p)
485 SDL_Rect dst;
486 textbox_ptr tb = (textbox_ptr) p;
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);
499 if (tb->img) {
500 dst.x = 1;
501 dst.y = 3;
502 widget_blit(tb, tb->img, NULL, &dst);
506 char s[1024];
507 int w, h;
509 strncpy(s, tb->text, tb->i);
510 s[tb->i] = 0;
511 TTF_SizeText(font, s, &w, &h);
513 dst.x = w;
514 dst.y = 2;
515 dst.w = 1;
516 dst.h = vsize - 2;
517 widget_fillrect(p, &dst, c_text);
521 void textbox_init(textbox_ptr l)
523 widget_init((widget_ptr) l);
524 l->img = NULL;
525 l->widget.update = textbox_update;
528 void textbox_put_text(textbox_ptr tb, char *s)
530 strcpy(tb->text, s);
531 tb->i = strlen(s);
532 textbox_update_img(tb);
535 static widget_ptr button_selection;
537 void button_handle_click(widget_ptr p, int button, int x, int y)
539 state = state_button;
540 button_selection = p;
543 void button_update(widget_ptr p)
545 SDL_Rect dst;
546 label_ptr l = (label_ptr) p;
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);
554 if (l->img) {
555 dst.x = 5;
556 dst.y = 2;
557 widget_blit(l, l->img, NULL, &dst);
561 void button_init(button_ptr b)
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;
570 void button_put_text(button_ptr b, char *s)
572 SDL_Surface *tmp;
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);
582 void set_video(int w, int h)
584 int flags;
585 flags = SDL_DOUBLEBUF;
587 screen = SDL_SetVideoMode(w, h, 0, flags);
588 init_ctable(screen->format);
590 if (!screen) {
591 fprintf(stderr, "Can't set video mode: %s\n", SDL_GetError());
592 exit(1);
595 //SDL_ShowCursor(SDL_DISABLE);
598 void set_interrupted(int i)
600 interrupted = 1;
603 void init()
605 int status;
607 signal(SIGINT, set_interrupted);
608 signal(SIGTERM, set_interrupted);
610 //if (SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) {
611 if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) {
612 fprintf(stderr, "Can't init SDL: %s\n", SDL_GetError());
613 exit(1);
615 atexit(SDL_Quit);
616 status = TTF_Init();
617 if (status) {
618 fprintf(stderr, "Can't init SDL_ttf\n");
619 exit(-1);
621 atexit(TTF_Quit);
623 SDL_WM_SetCaption("NetWalk", "NetWalk");
625 SDL_EnableKeyRepeat(150, 50);
628 SDL_Surface *unmarked_tileimg[64];
629 SDL_Surface *marked_tileimg[64];
630 int level = level_medium;
631 int tick;
632 int tick_old;
633 int pipey, pipex;
634 int pipew, pipeh;
635 int pipet = 4;
636 int move_count;
637 int ms_count;
638 int second_count;
640 void draw_tile(widget_ptr wid, int i, int j)
642 SDL_Rect rect;
643 int index;
645 rect.x = padding + border + i * (cellw + border);
646 rect.y = padding + border + j * (cellh + border);
648 int const marked = flags[i][j] & 0x1;
649 index = board[i][j] - 1;
650 widget_blit(wid,
651 (marked?marked_tileimg:unmarked_tileimg)[index],
652 NULL,
653 &rect);
656 typedef struct {
657 int x, y;
658 int dir;
659 int tick;
660 } pulse_s;
662 pulse_s pulse_list[pulse_max];
663 int pulse_count;
665 void new_pulse(int x, int y, int d)
667 int i, j;
669 if (pulse_count >= pulse_max) return;
671 //stop incoming server pulses
672 if (x == sourcex && (y == sourceybottom || y ==sourceytop) && d != -1) {
673 return;
676 i = board[x][y];
677 for (j=0; j<4; j++) {
678 if ((j != d) && (i & (1 << j))) {
679 pulse_list[pulse_count].x = x;
680 pulse_list[pulse_count].y = y;
681 pulse_list[pulse_count].dir = j;
682 pulse_list[pulse_count].tick = tick;
683 pulse_count++;
684 if (pulse_count >= pulse_max) return;
689 void server_pulse()
691 new_pulse(sourcex, sourceybottom, -1);
692 new_pulse(sourcex, sourceytop, -1);
695 void animate_pulse(widget_ptr wid)
697 int i, dt, d;
698 int x, y;
699 SDL_Rect rect;
700 int speed = 500;
702 if (!pulse_count) {
703 server_pulse();
706 rect.w = pipet + 2;
707 rect.h = pipet + 2;
708 i = 0;
709 while (i<pulse_count) {
710 x = pulse_list[i].x;
711 y = pulse_list[i].y;
712 d = pulse_list[i].dir;
713 dt = tick - pulse_list[i].tick;
714 if (dt > speed) {
715 pulse_count--;
716 memmove(&pulse_list[i], &pulse_list[i+1], sizeof(pulse_s) * (pulse_count - i));
718 add_dir(&x, &y, x, y, d);
719 new_pulse(x, y, (d + 2) % 4);
720 } else {
721 //wrap cases:
722 if (dir[d].x == -1 && 2 * dt > speed && !x) {
723 x += boardw;
725 if (dir[d].x == 1 && 2 * dt > speed && x == boardw - 1) {
726 x -= boardw;
728 if (dir[d].y == -1 && 2 * dt > speed && !y) {
729 y += boardh;
731 if (dir[d].y == 1 && 2 * dt > speed && y == boardh - 1) {
732 y -= boardh;
735 rect.x = x * (cellw + border) + pipex - 1;
736 rect.x += dir[d].x * (cellw + border) * dt / speed;
737 rect.x += border + padding;
739 rect.y = y * (cellh + border) + border + padding;
740 rect.y += dir[d].y * (cellh + border) * dt / speed;
741 rect.y += pipey - 1;
742 widget_fillrect(wid, &rect, c_pulse);
743 i++;
748 /** \brief Determine which cell of the arena the mouse is currently
749 * pointing at, if any.
750 * \param wid The arena widget.
751 * \param mousex The mouse X position.
752 * \param mousey The mouse Y position.
753 * \param[out] col_p Returns the current cell's column in the arena.
754 * \param[out] row_p Returns the current cell's row in the arena.
755 * \retval 0 The mouse is over the arena and the cell index is returned
756 * through \a row_p and \a col_p.
757 * \retval -1 The mouse is not over the area.
760 int get_cell_from_mouse_position(widget_ptr const wid,
761 int const mousex,int const mousey,
762 unsigned int * const col_p,unsigned int * const row_p)
764 if((mousex < wid->x) || (mousey < wid->y))
765 return -1;
767 unsigned int const col =
768 (mousex - padding - border - wid->x) / (cellw + border);
769 unsigned int const row =
770 (mousey - padding - border - wid->y) / (cellh + border);
772 if((col >= boardw) || (row >= boardh))
773 return -1;
775 *col_p = col;
776 *row_p = row;
777 return 0;
780 /** \brief Fill a cell (including border) in the arena with a color.
781 * \param wid The arena widget.
782 * \param col The column of the cell to be filled.
783 * \param row The row of the cell to be filled.
784 * \param coloridx The index of the color to use when filling.
787 void fill_arena_cell(widget_ptr const wid,
788 unsigned int const col,unsigned int const row,int const coloridx)
790 SDL_Rect rect = {
791 .x = (cellw + border) * col + padding,
792 .y = (cellh + border) * row + padding,
793 .w = cellw + 2 * border,
794 .h = cellh + 2 * border
796 widget_fillrect(wid, &rect, coloridx);
799 void arena_update(widget_ptr wid)
801 int i, j;
802 SDL_Rect rect;
803 int bc;
804 int c;
806 //draw grid
807 rect.x = padding;
808 rect.y = padding;
809 rect.w = cellw * boardw + (boardw + 1) * border;
810 rect.h = border;
812 if (game_won) bc = c_borderwon;
813 else bc = c_border;
815 for (i=0; i<=boardh; i++) {
816 widget_fillrect(wid, &rect, bc);
817 rect.y += cellh + border;
820 rect.y = padding;
821 rect.w = border;
822 rect.h = cellh * boardh + (boardh + 1) * border;
823 for (i=0; i<=boardw; i++) {
824 widget_fillrect(wid, &rect, bc);
825 rect.x += cellw + border;
828 //highlight cursor
829 unsigned int col;
830 unsigned int row;
831 if(get_cell_from_mouse_position(wid,lastmousex,lastmousey,&col,&row) == 0)
833 /* highlight the cell the mouse is pointing at */
834 fill_arena_cell(wid,col,row,c_highlight);
836 if(wrap_flag)
839 * If the highlighted cell is an edge cell, also
840 * highlight the corresponding cells on opposite
841 * edges. This will make it easier to work with large
842 * wrapped grids.
844 if(col == 0)
845 fill_arena_cell(wid,boardw - 1,row,c_edgematch);
846 else
847 if(col == boardw - 1)
848 fill_arena_cell(wid,0,row,c_edgematch);
850 if(row == 0)
851 fill_arena_cell(wid,col,boardh - 1,c_edgematch);
852 else
853 if(row == boardh - 1)
854 fill_arena_cell(wid,col,0,c_edgematch);
858 //draw in tiles
859 for (i=0; i<boardw; i++) {
860 for (j=0; j<boardh; j++) {
861 if (board[i][j]) {
862 draw_tile(wid, i, j);
866 //draw server
867 if (game_won) c = c_serverwon;
868 else c = c_server;
870 rect.x = padding + border + (cellw + border) * sourcex;
871 rect.y = padding + border + (cellh + border) * sourceytop;
873 rect.x += 5;
874 rect.y += 5;
875 rect.w = cellw - 10;
876 rect.h = cellh - 5;
877 widget_fillrect(wid, &rect, c);
879 rect.y = padding + border + (cellh + border) * sourceybottom;
880 widget_fillrect(wid, &rect, c);
882 //victory animation
883 if (game_won) {
884 animate_pulse(wid);
888 char* read_field(FILE *fp)
890 char *r;
891 int i;
892 char c;
894 r = (char *) malloc(1024);
895 i = 0;
897 for(;;) {
898 c = getc(fp);
900 if (feof(fp)) {
901 free(r);
902 return NULL;
905 switch(c) {
906 case ',':
907 r[i] = 0;
908 return r;
909 case '\n':
910 r[i] = 0;
911 return r;
912 default:
913 r[i] = c;
914 i++;
915 break;
920 void update_hsw()
922 int i;
923 for (i=0; i<level_max; i++) {
924 if (hstable[i]->name) {
925 label_put_text(hsw[i]->name, hstable[i]->name);
926 } else {
927 label_put_text(hsw[i]->name, "None");
929 if (hstable[i]->time != -1) {
930 char s[80];
932 sprintf(s, "%d", hstable[i]->time);
933 label_put_text(hsw[i]->time, s);
934 } else {
935 label_put_text(hsw[i]->name, "None");
940 void read_hstable()
942 FILE *fp;
943 int i;
945 for (i=0; i<level_max; i++) {
946 hstable[i]->name = NULL;
947 hstable[i]->time = -1;
950 fp = fopen(config->hsfile, "r");
951 if (!fp) return;
953 for(i=0; i<level_max; i++) {
954 char *s;
955 s = read_field(fp);
956 if (!s) goto done;
958 hstable[i]->name = s;
960 s = read_field(fp);
961 if (!s) goto done;
963 hstable[i]->time = atoi(s);
964 free(s);
967 done:
968 fclose(fp);
971 void write_hstable()
973 FILE *fp;
974 int i;
976 fp = fopen(config->hsfile, "w");
977 if (!fp) return;
979 for(i=0; i<level_max; i++) {
980 fprintf(fp, "%s,%d\n", hstable[i]->name, hstable[i]->time);
983 fclose(fp);
986 int enkludge;
988 void enter_name_open()
990 modalwindow = enter_name_window;
991 enkludge = 1;
994 void enter_name_close()
996 modalwindow = NULL;
997 enkludge = 0;
999 player_name = tb_en1->text;
1001 if (hstable[level]->name) {
1002 free(hstable[level]->name);
1004 hstable[level]->name = clonestr(player_name);
1005 hstable[level]->time = second_count;
1006 update_hsw();
1007 write_hstable();
1010 void check_hs()
1012 if (hstable[level]->time == -1 || second_count < hstable[level]->time) {
1013 enter_name_open();
1017 void init_tileimg(SDL_Surface * tileimg[64],int bgcolor)
1019 int i, j;
1020 SDL_PixelFormat *fmt = screen->format;
1021 SDL_Rect rect;
1022 int c;
1024 pipex = cellw / 2 - pipet / 2;
1025 pipey = cellw / 2 - pipet / 2;
1026 pipew = cellw / 2 + pipet / 2;
1027 pipeh = cellh / 2 + pipet / 2;
1029 SDL_Rect entirecell = (SDL_Rect){
1030 .x = 0,
1031 .y = 0,
1032 .w = cellw,
1033 .h = cellh
1036 for (i=0; i<64; i++) {
1037 tileimg[i] = SDL_CreateRGBSurface(0, cellw, cellh, fmt->BitsPerPixel,
1038 fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
1040 SDL_FillRect(tileimg[i], &entirecell, ctable[bgcolor]);
1042 for (j=0; j<4; j++) {
1043 if ((i + 1) & (1 << j)) {
1044 switch (j) {
1045 case 0:
1046 rect.x = pipex;
1047 rect.y = 0;
1048 rect.w = pipet;
1049 rect.h = pipeh;
1050 break;
1051 case 1:
1052 rect.x = pipex;
1053 rect.y = pipey;
1054 rect.w = pipew;
1055 rect.h = pipet;
1056 break;
1057 case 2:
1058 rect.x = pipex;
1059 rect.y = pipey;
1060 rect.w = pipet;
1061 rect.h = pipeh;
1062 break;
1063 case 3:
1064 rect.x = 0;
1065 rect.y = pipey;
1066 rect.w = pipew;
1067 rect.h = pipet;
1068 break;
1070 if (i >= 16) {
1071 c = ctable[c_on];
1072 } else c = ctable[c_off];
1073 SDL_FillRect(tileimg[i], &rect, c);
1078 for (i=1; i<32; i*=2) {
1079 rect.x = cellw / 2 - 2 * pipet;
1080 rect.y = cellh / 2 - 2 * pipet;
1081 rect.w = pipet * 4;
1082 rect.h = pipet * 4;
1083 rect.x++;
1084 rect.w-=2;
1085 rect.y++;
1086 rect.h-=2;
1088 SDL_FillRect(tileimg[i-1], &rect, ctable[c_off]);
1089 SDL_FillRect(tileimg[i-1+16], &rect, ctable[c_on]);
1090 rect.x++;
1091 rect.w-=2;
1092 rect.y++;
1093 rect.h-=2;
1095 SDL_FillRect(tileimg[i-1], &rect, ctable[c_down]);
1096 SDL_FillRect(tileimg[i-1+16], &rect, ctable[c_up]);
1100 void reset_move_count()
1102 move_count = 0;
1103 label_put_text(l_moves, "moves: 0");
1106 void increment_move_count()
1108 char s[80];
1109 move_count++;
1110 sprintf(s, "moves: %d", move_count);
1111 label_put_text(l_moves, s);
1114 void reset_time()
1116 label_put_text(l_time, "time: 0");
1119 void update_time()
1121 static char s[80];
1123 ms_count += tick - tick_old;
1124 while (ms_count >= 1000) {
1125 ms_count -= 1000;
1126 second_count++;
1127 sprintf(s, "time: %d", second_count);
1128 label_put_text(l_time, s);
1132 void resize()
1133 //position everything based on board size
1135 int w, h;
1137 sourcex = boardw / 2 - 1;
1138 sourceytop = boardh / 2;
1139 sourceybottom = sourceytop + 1;
1141 w = cellw * boardw + (boardw + 1) * border + 2 * padding;
1142 h = cellh * boardh + (boardh + 1) * border + 2 * padding;
1143 widget_put_geometry(arena, 0, vsize, w, h);
1144 set_video(w, h + 2 * vsize);
1145 widget_put_geometry(root, 0, 0, w, h + 2 * vsize);
1146 widget_put_geometry(menu, 0, 0, w, vsize);
1147 menubar_auto_layout(menu);
1148 widget_put_geometry(statusbar, 0, h + vsize, w, vsize);
1150 widget_put_geometry(l_moves, 8, h + vsize, 64, vsize);
1151 widget_put_geometry(l_time, w - 48, h + vsize, 64, vsize);
1153 widget_put_geometry((widget_ptr) about_window,
1154 w/2 - 50, h/2 - 50, 100, 100);
1155 widget_put_geometry((widget_ptr) l_about1, 10, 10, 60, vsize);
1156 widget_put_geometry((widget_ptr) l_about2, 10, 30, 60, vsize);
1157 widget_put_geometry((widget_ptr) b_about1, 30, 80, 30, vsize);
1159 /* vertical sizes and positions for the high score window */
1160 int const hslabely = 5;
1161 int const hslabelheight = vsize;
1162 int const hslisty = hslabely + hslabelheight + 8;
1163 int const hslisteachheight = vsize;
1164 int const hslistheight = hslisteachheight * level_max;
1165 int const hsoky = hslisty + hslistheight + 8;
1166 int const hsokheight = vsize;
1167 int const hswindowheight = hsoky + hsokheight + 5;
1169 widget_put_geometry((widget_ptr) hs_window,
1170 w/2 - 75, h/2 - 60, 170, hswindowheight);
1171 widget_put_geometry((widget_ptr) l_hs1, 10, hslabely, 60, hslabelheight);
1172 widget_put_geometry((widget_ptr) b_hs1, 100, hsoky, 30, hsokheight);
1175 int i;
1176 for (i=0; i<level_max; i++) {
1177 int y = hslisty + (hslisteachheight * i);
1178 widget_put_geometry((widget_ptr) hsw[i]->level,
1179 10, y, 20, hslisteachheight);
1180 widget_put_geometry((widget_ptr) hsw[i]->time,
1181 60, y, 20, hslisteachheight);
1182 widget_put_geometry((widget_ptr) hsw[i]->name,
1183 90, y, 20, hslisteachheight);
1187 widget_put_geometry((widget_ptr) enter_name_window,
1188 10, h/2 - 30, w - 20, 67);
1189 widget_put_geometry((widget_ptr) l_en1, 10, 0, 60, vsize);
1190 widget_put_geometry((widget_ptr) tb_en1, 5, vsize + 5, w - 30, vsize);
1191 widget_put_geometry((widget_ptr) b_en1, w - 60, 45, 30, vsize);
1193 ((widget_ptr) root)->computexy((widget_ptr) root);
1194 ((widget_ptr) about_window)->computexy((widget_ptr) about_window);
1195 ((widget_ptr) hs_window)->computexy((widget_ptr) hs_window);
1196 ((widget_ptr) enter_name_window)->computexy((widget_ptr) enter_name_window);
1199 void new_game()
1201 char s[BUFSIZ];
1202 strcpy(s,"NetWalk - ");
1203 strcat(s,level_name[level]);
1204 SDL_WM_SetCaption(s,s);
1206 switch(level) {
1207 case level_easy:
1208 boardw = 5; boardh = 5;
1209 wrap_flag = 0;
1210 no_fourway = 0;
1211 break;
1212 case level_medium:
1213 boardw = 10; boardh = 9;
1214 wrap_flag = 0;
1215 no_fourway = 0;
1216 break;
1217 case level_hard:
1218 boardw = 10; boardh = 9;
1219 wrap_flag = 1;
1220 no_fourway = 1;
1221 break;
1222 case level_veryhard:
1223 boardw = 20; boardh = 18;
1224 wrap_flag = 1;
1225 no_fourway = 1;
1226 break;
1227 case level_giant:
1228 boardw = 50; boardh = 50;
1229 wrap_flag = 0;
1230 no_fourway = 1;
1231 break;
1232 case level_absurd:
1233 boardw = 50; boardh = 50;
1234 wrap_flag = 1;
1235 no_fourway = 1;
1236 break;
1237 default:
1238 break;
1240 resize();
1241 srand(time(NULL));
1242 generate_maze();
1243 clear_flags();
1244 scramble();
1245 check_live();
1246 reset_move_count();
1247 reset_time();
1248 ms_count = 0;
1249 tick = SDL_GetTicks();
1250 second_count = 0;
1253 void handle_mousebuttonup(SDL_Event *event)
1255 int x = event->button.x;
1256 int y = event->button.y;
1257 if (openedmenu) {
1258 int i;
1259 menuitem_ptr it;
1260 menu_ptr m;
1262 m = openedmenu->submenu;
1263 for (i=0; i<m->item_count; i++) {
1264 it = m->item_list[i];
1265 if (in_widget((widget_ptr) it, x, y)) {
1266 widget_raise_signal((widget_ptr) it, signal_activate);
1267 break;
1270 openedmenu = NULL;
1272 return;
1273 } else if (state == state_button) {
1274 state = state_game;
1275 if (in_widget(button_selection, x, y)) {
1276 widget_raise_signal(button_selection, signal_activate);
1277 return;
1282 void handle_click(int button, int x, int y)
1284 widget_ptr wid;
1286 if (modalwindow) {
1287 wid = (widget_ptr) modalwindow;
1288 if (in_widget(wid, x, y) && (wid->handle_click)) {
1289 wid->handle_click(wid, button, x, y);
1291 return;
1294 wid = (widget_ptr) root;
1295 wid->handle_click(wid, button, x, y);
1298 void arena_handle_click(widget_ptr p, int button, int x, int y)
1300 int i, j;
1301 int d;
1303 if (state != state_game) return;
1305 i = (x - padding - border) / (cellw + border);
1306 j = (y - padding - border) / (cellh + border);
1307 if (i >= boardw || j >= boardh) return;
1309 if (game_won) {
1310 if (i == sourcex && (j == sourceytop || j == sourceybottom)) {
1311 new_pulse(sourcex, sourceybottom, -1);
1312 new_pulse(sourcex, sourceytop, -1);
1313 } else {
1314 new_pulse(i, j, -1);
1316 return;
1319 //temporarily merge server squares
1320 board[sourcex][sourceybottom] |= board[sourcex][sourceytop] & 1;
1321 if (i == sourcex && j == sourceytop) {
1322 j = sourceybottom;
1324 d = board[i][j] & 15;
1325 switch(button) {
1326 case SDL_BUTTON_LEFT:
1327 d = rotatecw(d, 3);
1328 increment_move_count();
1329 break;
1330 case SDL_BUTTON_RIGHT:
1331 d = rotatecw(d, 1);
1332 increment_move_count();
1333 break;
1335 board[i][j] &= ~15;
1336 board[i][j] += d;
1338 board[sourcex][sourceytop] &= ~1;
1339 board[sourcex][sourceytop] |= board[sourcex][sourceybottom] & 1;
1340 board[sourcex][sourceybottom] &= ~1;
1342 if(button == SDL_BUTTON_MIDDLE)
1343 flags[i][j] ^= 0x1;
1345 check_live();
1346 if (game_won) {
1347 pulse_count = 0;
1349 check_hs();
1353 void quit()
1355 state = state_quit;
1358 void quit_menu(widget_ptr w, void *data)
1360 quit();
1363 void new_game_menu(widget_ptr w, void *data)
1365 new_game();
1368 void about_open(widget_ptr w, void *data)
1370 modalwindow = about_window;
1373 void about_close(widget_ptr w, void *data)
1375 modalwindow = NULL;
1378 void hs_open(widget_ptr w, void *data)
1380 modalwindow = hs_window;
1383 void hs_close(widget_ptr w, void *data)
1385 modalwindow = NULL;
1388 void set_level(widget_ptr w, void *data)
1390 level = (int) data;
1391 new_game();
1394 void handle_key(int key, int mod)
1396 if (openedmenu) {
1397 switch(key) {
1398 case SDLK_ESCAPE:
1399 openedmenu = NULL;
1401 return;
1404 if (enkludge) {
1405 switch(key) {
1406 case SDLK_LEFT:
1407 textbox_left(tb_en1);
1408 break;
1409 case SDLK_RIGHT:
1410 textbox_right(tb_en1);
1411 break;
1412 case SDLK_DELETE:
1413 textbox_delete(tb_en1);
1414 break;
1415 case SDLK_BACKSPACE:
1416 textbox_backspace(tb_en1);
1417 break;
1418 default:
1419 if (key < 256 && key >= 32) {
1420 if (mod & KMOD_SHIFT) {
1421 textbox_insert(tb_en1, shifttable[key]);
1422 } else {
1423 textbox_insert(tb_en1, key);
1426 break;
1428 return;
1431 switch(key) {
1432 case SDLK_d:
1433 enter_name_open();
1434 break;
1435 case SDLK_ESCAPE:
1436 case SDLK_q:
1437 quit();
1438 break;
1439 case SDLK_F2:
1440 new_game();
1441 break;
1445 void update_screen()
1447 SDL_FillRect(screen, NULL, 0);
1448 widget_update((widget_ptr) root);
1449 if (openedmenu) {
1450 int i;
1451 menuitem_ptr it;
1453 for (i=0; i<menu->item_count; i++) {
1454 it = menu->item_list[i];
1455 if (in_widget((widget_ptr) it, lastmousex, lastmousey)) {
1456 open_submenu((widget_ptr) it, NULL);
1459 menu_update(openedmenu->submenu);
1461 if (modalwindow) {
1462 widget_update((widget_ptr) modalwindow);
1464 SDL_Flip(screen);
1467 void window_update(widget_ptr p)
1469 window_ptr w = (window_ptr) p;
1470 widget_ptr wid;
1471 int i;
1472 SDL_Rect dst;
1474 if (p != (widget_ptr) root) {
1475 dst.x = -1;
1476 dst.y = -1;
1477 dst.w = p->w + 2;
1478 dst.h = p->h + 2;
1479 widget_fillrect(p, &dst, c_windowborder);
1480 widget_fill(p, c_background);
1483 for (i=0; i<w->widget_count; i++) {
1484 wid = w->widget_list[i];
1485 widget_update(wid);
1489 void window_handle_click(widget_ptr p, int button, int x, int y)
1491 widget_ptr wid;
1492 window_ptr window = (window_ptr) p;
1493 int i;
1495 for (i=0; i<window->widget_count; i++) {
1496 wid = window->widget_list[i];
1497 if (in_widget(wid, x, y) && (wid->handle_click)) {
1498 wid->handle_click(wid, button, x - wid->x, y - wid->y);
1499 return;
1504 void window_add_widget(window_ptr r, void *p)
1506 widget_ptr wid = (widget_ptr) p;
1507 r->widget_list[r->widget_count] = wid;
1508 r->widget_count++;
1509 wid->parent = (widget_ptr) r;
1510 wid->x += r->widget.x;
1511 wid->y += r->widget.y;
1514 void window_computexy(widget_ptr wid)
1516 int i;
1517 window_ptr w = (window_ptr) wid;
1519 widget_computexy(wid);
1520 for (i=0; i<w->widget_count; i++) {
1521 w->widget_list[i]->computexy(w->widget_list[i]);
1525 void window_init(window_ptr w)
1527 widget_init((widget_ptr) w);
1528 w->widget_count = 0;
1529 w->widget.update = window_update;
1530 w->widget.handle_click = window_handle_click;
1531 w->widget.computexy = window_computexy;
1534 static void add_shiftstring(char *s1, char *s2)
1536 int i;
1538 for (i=0; i<strlen(s1); i++) {
1539 shifttable[(int) s1[i]] = s2[i];
1543 int main(int argc, char *argv[])
1545 SDL_Event event;
1547 //setup names
1548 level_name[level_easy] = "Newbie";
1549 level_name[level_medium] = "Normal";
1550 level_name[level_hard] = "Nerd";
1551 level_name[level_veryhard] = "Nutcase";
1552 level_name[level_giant] = "Nonsense";
1553 level_name[level_absurd] = "No Sleep";
1555 //setup shifttable
1557 int i;
1559 for (i=0; i<256; i++) shifttable[i] = i;
1561 for (i='a'; i<='z'; i++) {
1562 shifttable[i] = i - 32;
1565 add_shiftstring("1234567890-=", "!@#$%^&*()_+");
1566 add_shiftstring("[]\\;',./`", "{}|:\"<>?~");
1569 config_load(config);
1570 read_hstable();
1571 init();
1573 init_rgbtable();
1575 font = TTF_OpenFont(config->fontname, config->fontsize);
1576 if (!font) {
1577 fprintf(stderr, "error loading font %s\n", config->fontname);
1578 exit(1);
1581 window_init(root);
1583 //need to set video mode here to initialize colour table
1584 set_video(100, 100);
1586 //setup enter name box
1588 window_init(enter_name_window);
1590 label_init(l_en1);
1591 label_put_text(l_en1, "Enter name:");
1592 window_add_widget(enter_name_window, l_en1);
1594 textbox_init(tb_en1);
1595 textbox_put_text(tb_en1, "Anonymous");
1596 window_add_widget(enter_name_window, tb_en1);
1598 button_init(b_en1);
1599 button_put_text(b_en1, "Ok");
1600 window_add_widget(enter_name_window, b_en1);
1601 widget_put_handler((widget_ptr) b_en1, enter_name_close, signal_activate);
1604 //setup the "arena": where the action is
1606 widget_init((widget_ptr) arena);
1607 arena->widget.update = arena_update;
1608 arena->widget.handle_click = arena_handle_click;
1609 window_add_widget(root, arena);
1612 //status bar: mainly for showing the time
1614 widget_init((widget_ptr) statusbar);
1615 statusbar->update = statusbar_update;
1616 window_add_widget(root, statusbar);
1618 //setup moves and time
1619 label_init(l_moves);
1620 if (config->showmoves) {
1621 window_add_widget(root, l_moves);
1624 label_init(l_time);
1625 window_add_widget(root, l_time);
1628 //setup the menus
1630 menuitem_t it1, it2;
1631 menuitem_ptr it;
1632 menu_t m1, m2;
1634 int i;
1636 menubar_init(menu);
1637 window_add_widget(root, menu);
1638 menuitem_init(it1);
1639 menuitem_put_text(it1, "Game");
1640 menubar_add_item(menu, it1);
1641 menuitem_init(it2);
1642 menuitem_put_text(it2, "Help");
1643 menubar_add_item(menu, it2);
1645 menu_init(m1);
1647 it = menuitem_new();
1648 menuitem_put_text(it, "New game");
1649 widget_put_handler((widget_ptr) it, new_game_menu, signal_activate);
1650 menu_add_item(m1, it);
1652 for (i=0; i<level_max; i++) {
1653 it = menuitem_new();
1654 menuitem_put_text(it, level_name[i]);
1655 widget_put_handler_data((widget_ptr) it,
1656 set_level, (void *) i, signal_activate);
1657 menu_add_item(m1, it);
1659 it = menuitem_new();
1660 menuitem_put_text(it, "High Scores");
1661 widget_put_handler((widget_ptr) it, hs_open, signal_activate);
1662 menu_add_item(m1, it);
1663 it = menuitem_new();
1664 menuitem_put_text(it, "Quit");
1665 widget_put_handler((widget_ptr) it, quit_menu, signal_activate);
1666 menu_add_item(m1, it);
1668 menuitem_set_submenu(it1, m1);
1670 menu_init(m2);
1672 it = menuitem_new();
1673 menuitem_put_text(it, "About");
1674 widget_put_handler((widget_ptr) it, about_open, signal_activate);
1675 menu_add_item(m2, it);
1677 menuitem_set_submenu(it2, m2);
1680 //setup about box
1682 window_init(about_window);
1684 label_init(l_about1);
1685 label_put_text(l_about1, "NetWalk " VERSION_STRING);
1686 window_add_widget(about_window, l_about1);
1688 label_init(l_about2);
1689 label_put_text(l_about2, "Ben Lynn");
1690 window_add_widget(about_window, l_about2);
1692 button_init(b_about1);
1693 button_put_text(b_about1, "Ok");
1694 window_add_widget(about_window, b_about1);
1695 widget_put_handler((widget_ptr) b_about1, about_close, signal_activate);
1698 //setup hiscores box
1700 int i;
1701 window_init(hs_window);
1703 label_init(l_hs1);
1704 label_put_text(l_hs1, "High Scores");
1705 window_add_widget(hs_window, l_hs1);
1707 button_init(b_hs1);
1708 button_put_text(b_hs1, "Ok");
1709 window_add_widget(hs_window, b_hs1);
1710 widget_put_handler((widget_ptr) b_hs1, hs_close, signal_activate);
1712 for (i=0; i<level_max; i++) {
1713 label_init(hsw[i]->level);
1714 label_put_text(hsw[i]->level, level_name[i]);
1715 window_add_widget(hs_window, hsw[i]->level);
1716 label_init(hsw[i]->name);
1717 window_add_widget(hs_window, hsw[i]->name);
1718 label_init(hsw[i]->time);
1719 window_add_widget(hs_window, hsw[i]->time);
1723 resize();
1725 update_hsw();
1727 init_tileimg(unmarked_tileimg,c_unmarkedbg);
1728 init_tileimg(marked_tileimg,c_markedbg);
1729 new_game();
1731 while (state != state_quit && !interrupted) {
1732 tick_old = tick;
1733 tick = SDL_GetTicks();
1734 if (!game_won) {
1735 update_time();
1737 SDL_GetMouseState(&lastmousex, &lastmousey);
1738 while (SDL_PollEvent(&event)) {
1739 switch (event.type) {
1740 case SDL_KEYDOWN:
1741 handle_key(event.key.keysym.sym, SDL_GetModState());
1742 break;
1743 case SDL_MOUSEBUTTONDOWN:
1744 handle_click(event.button.button, event.button.x, event.button.y);
1745 break;
1746 case SDL_MOUSEBUTTONUP:
1747 handle_mousebuttonup(&event);
1748 break;
1749 case SDL_QUIT:
1750 quit();
1751 break;
1754 update_screen();
1755 SDL_Delay(20);
1757 return 0;