Fixed scoping of menu variables.
[netwalk.git] / main.c
blobfe09f7e5a027bb00e2e5c3c4592eb495d2f3d4a1
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 <stdio.h>
22 #include <string.h>
23 #include <math.h>
24 #include <time.h>
26 #include <sys/types.h> //for opendir et al.
27 #include <dirent.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
32 #include <stdlib.h>
33 #include <signal.h>
35 #include <SDL.h>
36 #include <SDL_ttf.h>
38 #include "version.h"
40 #include "widget.h"
41 #include "colour.h"
42 #include "game.h"
44 #include "config.h"
45 #include "util.h"
47 static int interrupted = 0;
49 enum {
50 vsize = 18
53 enum {
54 pulse_max = 1024
57 enum {
58 state_game,
59 state_button,
60 state_quit
63 enum {
64 level_easy = 0,
65 level_medium,
66 level_hard,
67 level_veryhard,
68 level_giant,
69 level_absurd,
70 level_max,
71 level_custom
74 enum {
75 cellw = 24,
76 cellh = 24,
77 border = 1,
78 padding = 10
81 char shifttable[256];
83 struct hsentry_s {
84 char *name;
85 int time;
87 typedef struct hsentry_s *hsentry_ptr;
88 typedef struct hsentry_s hsentry_t[1];
90 hsentry_t hstable[level_max];
91 char *level_name[level_max];
93 static config_t config;
95 SDL_Surface *screen;
96 TTF_Font *font;
97 int lastmousex, lastmousey;
98 char *player_name;
99 int game_won;
100 int state;
102 struct button_s {
103 struct widget_s widget;
104 char *text;
105 SDL_Surface *img;
107 typedef struct button_s button_t[1];
108 typedef struct button_s *button_ptr;
110 struct label_s {
111 struct widget_s widget;
112 char *text;
113 SDL_Surface *img;
115 typedef struct label_s label_t[1];
116 typedef struct label_s *label_ptr;
118 struct textbox_s {
119 struct widget_s widget;
120 char text[1024];
121 int i;
122 SDL_Surface *img;
124 typedef struct textbox_s textbox_t[1];
125 typedef struct textbox_s *textbox_ptr;
127 struct arena_s {
128 struct widget_s widget;
130 typedef struct arena_s arena_t[1];
131 typedef struct arena_s *arena_ptr;
133 struct menuitem_s {
134 struct widget_s widget;
135 char *text;
136 SDL_Surface *img;
137 struct menu_s *submenu;
139 typedef struct menuitem_s menuitem_t[1];
140 typedef struct menuitem_s *menuitem_ptr;
142 struct menu_s {
143 struct widget_s widget;
144 menuitem_ptr item_list[64];
145 int item_count;
147 typedef struct menu_s menu_t[1];
148 typedef struct menu_s *menu_ptr;
150 struct menubar_s {
151 struct widget_s widget;
152 menuitem_ptr item_list[64];
153 int item_count;
155 typedef struct menubar_s menubar_t[1];
156 typedef struct menubar_s *menubar_ptr;
158 struct window_s {
159 struct widget_s widget;
160 struct widget_s *widget_list[64];
161 int widget_count;
164 typedef struct window_s window_t[1];
165 typedef struct window_s *window_ptr;
167 struct hsw_s {
168 label_t level;
169 label_t time;
170 label_t name;
172 typedef struct hsw_s *hsw_ptr;
173 typedef struct hsw_s hsw_t[1];
175 hsw_t hsw[level_max];
177 arena_t arena;
178 window_t root;
179 label_t l_moves;
180 label_t l_time;
181 menubar_t menu;
182 menuitem_ptr openedmenu;
183 window_ptr modalwindow;
184 window_t about_window;
185 window_t hs_window;
186 window_t enter_name_window;
187 label_t l_about1;
188 label_t l_about2;
189 button_t b_about1;
190 widget_t statusbar;
192 label_t l_hs1;
193 button_t b_hs1;
195 label_t l_en1;
196 textbox_t tb_en1;
197 button_t b_en1;
199 SDL_Surface *font_render(char *s, int c)
201 return TTF_RenderText_Solid(font, s, rgbtable[c]);
204 void statusbar_update(widget_ptr w)
206 widget_lowered_background(w);
209 void menu_init(menu_ptr m)
211 widget_init((widget_ptr) m);
212 m->item_count = 0;
215 void menu_update(menu_ptr m)
217 int i;
218 menuitem_ptr it;
219 SDL_Rect rect;
221 widget_fill((widget_ptr) m, c_background);
222 for (i=0; i<m->item_count; i++) {
223 it = m->item_list[i];
224 if (in_widget((widget_ptr) it, lastmousex, lastmousey)) {
225 rect.x = 2;
226 rect.y = vsize * i;
227 rect.w = ((widget_ptr) it)->w - 4;
228 rect.h = vsize;
229 widget_fillrect((widget_ptr) m, &rect, c_menubg);
231 rect.x = 8;
232 rect.y = vsize * i + 4;
233 widget_blit((widget_ptr) m, it->img, NULL, &rect);
237 void menu_add_item(menu_ptr m, menuitem_ptr it)
239 m->item_list[m->item_count] = it;
240 m->item_count++;
243 void menuitem_init(menuitem_ptr m)
245 widget_init((widget_ptr) m);
246 m->text = NULL;
247 m->img = NULL;
248 m->submenu = NULL;
251 menuitem_ptr menuitem_new()
253 menuitem_ptr it = (menuitem_ptr) malloc(sizeof(menuitem_t));
254 menuitem_init(it);
255 return it;
258 void menuitem_put_text(menuitem_ptr m, char *s)
260 SDL_Surface *tmp;
262 m->text = s;
263 if (m->img) SDL_FreeSurface(m->img);
264 tmp = font_render(s, c_text);
265 m->img = SDL_DisplayFormat(tmp);
266 SDL_FreeSurface(tmp);
269 void open_submenu(widget_ptr p, void *data)
271 int i;
272 int w = 0;
273 menuitem_ptr it;
275 openedmenu = (menuitem_ptr) p;
276 menu_ptr m = openedmenu->submenu;
278 for (i=0; i<m->item_count; i++) {
279 it = m->item_list[i];
280 if (w < it->img->w) w = it->img->w;
282 w += 12;
284 m->widget.x = m->widget.parent->x;
285 m->widget.y = m->widget.parent->y + vsize;
287 m->widget.w = w;
288 m->widget.h = vsize * m->item_count + 1;
290 for (i=0; i<m->item_count; i++) {
291 it = m->item_list[i];
292 it->widget.x = 0 + m->widget.x;
293 it->widget.y = vsize * i + 4 + m->widget.y;
294 it->widget.w = w;
295 it->widget.h = vsize;
299 void menuitem_set_submenu(menuitem_ptr it, menu_ptr m)
301 it->submenu = m;
302 widget_put_handler((widget_ptr) it, open_submenu, signal_activate);
303 m->widget.parent = (widget_ptr) it;
306 void menubar_update(widget_ptr wid)
308 SDL_Rect dst;
309 menubar_ptr m = (menubar_ptr) wid;
310 menuitem_ptr it;
311 int i;
313 widget_raised_background(wid);
315 for (i=0; i<m->item_count; i++) {
316 it = m->item_list[i];
317 if (it == openedmenu) {
318 dst.x = it->widget.x + 2;
319 dst.y = it->widget.y + 2;
320 dst.w = it->widget.w - 4;
321 dst.h = vsize - 2;
322 widget_fillrect(wid, &dst, c_menubg);
324 if (it->img) {
325 dst.x = it->widget.x + 5;
326 dst.y = it->widget.y + 2;
327 widget_blit(m, it->img, NULL, &dst);
332 void menubar_handle_click(widget_ptr p, int button, int x, int y)
334 menubar_ptr m = (menubar_ptr) p;
335 menuitem_ptr it;
336 int i;
337 for (i=0; i<m->item_count; i++) {
338 it = m->item_list[i];
339 if (in_widget((widget_ptr) it, x, y)) {
340 widget_raise_signal((widget_ptr) it, signal_activate);
341 return;
346 void menubar_init(menubar_ptr m)
348 widget_init((widget_ptr) m);
349 m->widget.update = menubar_update;
350 m->item_count = 0;
351 m->widget.handle_click = menubar_handle_click;
354 void menubar_add_item(menubar_ptr m, menuitem_ptr it)
356 m->item_list[m->item_count] = it;
357 m->item_count++;
358 it->widget.parent = (widget_ptr) m;
361 void menubar_auto_layout(menubar_ptr m)
363 int i, x = 0, y = 0;
364 menuitem_ptr it;
366 for (i=0; i<m->item_count; i++) {
367 it = m->item_list[i];
368 if (it->img) {
369 it->widget.x = x;
370 it->widget.y = y;
371 it->widget.w = it->img->w + 10;
372 it->widget.h = it->img->h;
373 x += it->img->w + 10;
378 void label_update(widget_ptr p)
380 SDL_Rect dst;
381 label_ptr l = (label_ptr) p;
383 if (l->img) {
384 dst.x = 0;
385 dst.y = 4;
386 widget_blit(l, l->img, NULL, &dst);
390 void label_init(label_ptr l)
392 widget_init((widget_ptr) l);
393 l->text = NULL;
394 l->img = NULL;
395 l->widget.update = label_update;
398 void label_put_text(label_ptr l, char *s)
400 SDL_Surface *tmp;
402 if (l->img) SDL_FreeSurface(l->img);
403 if (l->text) free(l->text);
404 l->text = clonestr(s);
405 tmp = font_render(s, c_text);
406 l->img = SDL_DisplayFormat(tmp);
407 SDL_FreeSurface(tmp);
410 void textbox_update_img(textbox_ptr tb)
412 SDL_Surface *tmp;
414 if (tb->img) SDL_FreeSurface(tb->img);
415 tmp = font_render(tb->text, c_text);
416 if (tmp) {
417 tb->img = SDL_DisplayFormat(tmp);
418 SDL_FreeSurface(tmp);
419 } else {
420 tb->img = NULL;
424 void textbox_left(textbox_ptr tb)
426 if (tb->i > 0) tb->i--;
429 void textbox_right(textbox_ptr tb)
431 if (tb->i < strlen(tb->text)) tb->i++;
434 void textbox_delete(textbox_ptr tb)
436 if (tb->i > 0) {
437 tb->i--;
438 tb->text[tb->i] = 0;
439 textbox_update_img(tb);
443 void textbox_backspace(textbox_ptr tb)
445 char *s = &tb->text[tb->i];
446 if (tb->i) {
447 memmove(s - 1, s, strlen(s) + 1);
448 tb->i--;
449 textbox_update_img(tb);
453 void textbox_insert(textbox_ptr tb, char c)
455 char *s = &tb->text[tb->i];
456 memmove(s + 1, s, strlen(s) + 1);
457 tb->text[tb->i] = c;
458 tb->i++;
459 textbox_update_img(tb);
462 void textbox_update(widget_ptr p)
464 textbox_ptr tb = (textbox_ptr) p;
465 SDL_Rect dst;
467 dst.x = 0;
468 dst.y = 0;
469 dst.w = p->w;
470 dst.h = p->h;
471 widget_fillrect(p, &dst, c_text);
472 dst.x++;
473 dst.y++;
474 dst.w-=2;
475 dst.h-=2;
476 widget_fillrect(p, &dst, c_canvas);
478 if (tb->img) {
479 dst.x = 1;
480 dst.y = 3;
481 widget_blit(tb, tb->img, NULL, &dst);
485 char s[1024];
486 int w, h;
488 strncpy(s, tb->text, tb->i);
489 s[tb->i] = 0;
490 TTF_SizeText(font, s, &w, &h);
492 dst.x = w;
493 dst.y = 2;
494 dst.w = 1;
495 dst.h = vsize - 2;
496 widget_fillrect(p, &dst, c_text);
500 void textbox_init(textbox_ptr l)
502 widget_init((widget_ptr) l);
503 l->img = NULL;
504 l->widget.update = textbox_update;
507 void textbox_put_text(textbox_ptr tb, char *s)
509 strcpy(tb->text, s);
510 tb->i = strlen(s);
511 textbox_update_img(tb);
514 static widget_ptr button_selection;
516 void button_handle_click(widget_ptr p, int button, int x, int y)
518 state = state_button;
519 button_selection = p;
522 void button_update(widget_ptr p)
524 SDL_Rect dst;
525 label_ptr l = (label_ptr) p;
527 if (state == state_button && button_selection == p
528 && in_widget(p, lastmousex, lastmousey)) {
529 widget_lowered_background(p);
530 } else {
531 widget_raised_background(p);
533 if (l->img) {
534 dst.x = 5;
535 dst.y = 2;
536 widget_blit(l, l->img, NULL, &dst);
540 void button_init(button_ptr b)
542 widget_init((widget_ptr) b);
543 b->text = NULL;
544 b->img = NULL;
545 b->widget.update = button_update;
546 b->widget.handle_click = button_handle_click;
549 void button_put_text(button_ptr b, char *s)
551 SDL_Surface *tmp;
553 if (b->img) SDL_FreeSurface(b->img);
554 if (b->text) free(b->text);
555 b->text = clonestr(s);
556 tmp = font_render(s, c_text);
557 b->img = SDL_DisplayFormat(tmp);
558 SDL_FreeSurface(tmp);
561 void set_video(int w, int h)
563 int flags = SDL_DOUBLEBUF;
564 screen = SDL_SetVideoMode(w, h, 0, flags);
565 // flags = SDL_FULLSCREEN;
566 // screen = SDL_SetVideoMode(0, 0, 0, flags);
567 init_ctable(screen->format);
568 if (!screen) {
569 fprintf(stderr, "Can't set video mode: %s\n", SDL_GetError());
570 exit(1);
572 //SDL_ShowCursor(SDL_DISABLE);
575 void set_interrupted(int i)
577 interrupted = 1;
580 void init()
582 int status;
583 signal(SIGINT, set_interrupted);
584 signal(SIGTERM, set_interrupted);
585 if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) {
586 fprintf(stderr, "Can't init SDL: %s\n", SDL_GetError());
587 exit(1);
589 atexit(SDL_Quit);
590 status = TTF_Init();
591 if (status) {
592 fprintf(stderr, "Can't init SDL_ttf\n");
593 exit(-1);
595 atexit(TTF_Quit);
596 SDL_WM_SetCaption("NetWalk", "NetWalk");
597 SDL_EnableKeyRepeat(150, 50);
600 SDL_Surface *unmarked_tileimg[64];
601 SDL_Surface *marked_tileimg[64];
602 int level = level_medium;
603 int tick;
604 int tick_old;
605 int pipey, pipex;
606 int pipew, pipeh;
607 int pipet = 4;
608 int move_count;
609 int ms_count;
610 int second_count;
612 void draw_tile(widget_ptr wid, int i, int j)
614 SDL_Rect rect;
615 rect.x = padding + border + i * (cellw + border);
616 rect.y = padding + border + j * (cellh + border);
617 int const marked = flags[i][j] & 0x1;
618 int index = board[i][j] - 1;
619 widget_blit(wid,
620 (marked?marked_tileimg:unmarked_tileimg)[index],
621 NULL,
622 &rect);
625 typedef struct {
626 int x, y;
627 int dir;
628 int tick;
629 } pulse_s;
631 pulse_s pulse_list[pulse_max];
632 int pulse_count;
634 void new_pulse(int x, int y, int d)
636 int i, j;
638 if (pulse_count >= pulse_max) return;
640 //stop incoming server pulses
641 if (x == sourcex && (y == sourceybottom || y ==sourceytop) && d != -1) {
642 return;
645 i = board[x][y];
646 for (j=0; j<4; j++) {
647 if ((j != d) && (i & (1 << j))) {
648 pulse_list[pulse_count].x = x;
649 pulse_list[pulse_count].y = y;
650 pulse_list[pulse_count].dir = j;
651 pulse_list[pulse_count].tick = tick;
652 pulse_count++;
653 if (pulse_count >= pulse_max) return;
658 void server_pulse()
660 new_pulse(sourcex, sourceybottom, -1);
661 new_pulse(sourcex, sourceytop, -1);
664 void animate_pulse(widget_ptr wid)
666 int i, dt, d;
667 int x, y;
668 SDL_Rect rect;
669 int speed = 500;
671 if (!pulse_count) {
672 server_pulse();
675 rect.w = pipet + 2;
676 rect.h = pipet + 2;
677 i = 0;
678 while (i<pulse_count) {
679 x = pulse_list[i].x;
680 y = pulse_list[i].y;
681 d = pulse_list[i].dir;
682 dt = tick - pulse_list[i].tick;
683 if (dt > speed) {
684 pulse_count--;
685 memmove(&pulse_list[i], &pulse_list[i+1], sizeof(pulse_s) * (pulse_count - i));
687 add_dir(&x, &y, x, y, d);
688 new_pulse(x, y, (d + 2) % 4);
689 } else {
690 //wrap cases:
691 if (dir[d].x == -1 && 2 * dt > speed && !x) {
692 x += boardw;
694 if (dir[d].x == 1 && 2 * dt > speed && x == boardw - 1) {
695 x -= boardw;
697 if (dir[d].y == -1 && 2 * dt > speed && !y) {
698 y += boardh;
700 if (dir[d].y == 1 && 2 * dt > speed && y == boardh - 1) {
701 y -= boardh;
704 rect.x = x * (cellw + border) + pipex - 1;
705 rect.x += dir[d].x * (cellw + border) * dt / speed;
706 rect.x += border + padding;
708 rect.y = y * (cellh + border) + border + padding;
709 rect.y += dir[d].y * (cellh + border) * dt / speed;
710 rect.y += pipey - 1;
711 widget_fillrect(wid, &rect, c_pulse);
712 i++;
717 /** \brief Determine which cell of the arena the mouse is currently
718 * pointing at, if any.
719 * \param wid The arena widget.
720 * \param mousex The mouse X position.
721 * \param mousey The mouse Y position.
722 * \param[out] col_p Returns the current cell's column in the arena.
723 * \param[out] row_p Returns the current cell's row in the arena.
724 * \retval 0 The mouse is over the arena and the cell index is returned
725 * through \a row_p and \a col_p.
726 * \retval -1 The mouse is not over the area.
729 int get_cell_from_mouse_position(widget_ptr const wid,
730 int const mousex,int const mousey,
731 unsigned int * const col_p,unsigned int * const row_p)
733 if((mousex < wid->x) || (mousey < wid->y))
734 return -1;
736 unsigned int const col =
737 (mousex - padding - border - wid->x) / (cellw + border);
738 unsigned int const row =
739 (mousey - padding - border - wid->y) / (cellh + border);
741 if((col >= boardw) || (row >= boardh))
742 return -1;
744 *col_p = col;
745 *row_p = row;
746 return 0;
749 /** \brief Fill a cell (including border) in the arena with a color.
750 * \param wid The arena widget.
751 * \param col The column of the cell to be filled.
752 * \param row The row of the cell to be filled.
753 * \param coloridx The index of the color to use when filling.
756 void fill_arena_cell(widget_ptr const wid,
757 unsigned int const col,unsigned int const row,int const coloridx)
759 SDL_Rect rect = {
760 .x = (cellw + border) * col + padding,
761 .y = (cellh + border) * row + padding,
762 .w = cellw + 2 * border,
763 .h = cellh + 2 * border
765 widget_fillrect(wid, &rect, coloridx);
768 void arena_update(widget_ptr wid)
770 int i, j;
771 SDL_Rect rect;
772 int bc = game_won ? c_borderwon : c_border;
773 int c = game_won ? c_serverwon : c_server;
775 //draw grid
776 rect.x = padding;
777 rect.y = padding;
778 rect.w = cellw * boardw + (boardw + 1) * border;
779 rect.h = border;
781 for (i=0; i<=boardh; i++) {
782 widget_fillrect(wid, &rect, bc);
783 rect.y += cellh + border;
786 rect.y = padding;
787 rect.w = border;
788 rect.h = cellh * boardh + (boardh + 1) * border;
789 for (i=0; i<=boardw; i++) {
790 widget_fillrect(wid, &rect, bc);
791 rect.x += cellw + border;
794 //highlight cursor
795 unsigned int col;
796 unsigned int row;
797 if(get_cell_from_mouse_position(wid,lastmousex,lastmousey,&col,&row) == 0)
799 /* highlight the cell the mouse is pointing at */
800 fill_arena_cell(wid,col,row,c_highlight);
802 if(wrap_flag)
805 * If the highlighted cell is an edge cell, also
806 * highlight the corresponding cells on opposite
807 * edges. This will make it easier to work with large
808 * wrapped grids.
810 if(col == 0)
811 fill_arena_cell(wid,boardw - 1,row,c_edgematch);
812 else
813 if(col == boardw - 1)
814 fill_arena_cell(wid,0,row,c_edgematch);
816 if(row == 0)
817 fill_arena_cell(wid,col,boardh - 1,c_edgematch);
818 else
819 if(row == boardh - 1)
820 fill_arena_cell(wid,col,0,c_edgematch);
824 //draw in tiles
825 for (i=0; i<boardw; i++) {
826 for (j=0; j<boardh; j++) {
827 if (board[i][j]) {
828 draw_tile(wid, i, j);
832 //draw server
834 rect.x = padding + border + (cellw + border) * sourcex;
835 rect.y = padding + border + (cellh + border) * sourceytop;
837 rect.x += 5;
838 rect.y += 5;
839 rect.w = cellw - 10;
840 rect.h = cellh - 5;
841 widget_fillrect(wid, &rect, c);
843 rect.y = padding + border + (cellh + border) * sourceybottom;
844 widget_fillrect(wid, &rect, c);
846 //victory animation
847 if (game_won) {
848 animate_pulse(wid);
852 char* read_field(FILE *fp)
854 char *r = (char *) malloc(1024);
855 int i = 0;
857 for (;;) {
858 char c = getc(fp);
860 if (feof(fp)) {
861 free(r);
862 return NULL;
865 switch(c) {
866 case ',':
867 r[i] = 0;
868 return r;
869 case '\n':
870 r[i] = 0;
871 return r;
872 default:
873 r[i] = c;
874 i++;
875 break;
880 void update_hsw()
882 int i;
883 for (i=0; i<level_max; i++) {
884 if (hstable[i]->name) {
885 label_put_text(hsw[i]->name, hstable[i]->name);
886 } else {
887 label_put_text(hsw[i]->name, "None");
889 if (hstable[i]->time != -1) {
890 char s[80];
892 sprintf(s, "%d", hstable[i]->time);
893 label_put_text(hsw[i]->time, s);
894 } else {
895 label_put_text(hsw[i]->name, "None");
900 void read_hstable()
902 FILE *fp;
903 int i;
905 for (i=0; i<level_max; i++) {
906 hstable[i]->name = NULL;
907 hstable[i]->time = -1;
910 fp = fopen(config->hsfile, "r");
911 if (!fp) return;
913 for (i=0; i<level_max; i++) {
914 char *s;
915 s = read_field(fp);
916 if (!s) goto done;
918 hstable[i]->name = s;
920 s = read_field(fp);
921 if (!s) goto done;
923 hstable[i]->time = atoi(s);
924 free(s);
927 done:
928 fclose(fp);
931 void write_hstable()
933 FILE *fp = fopen(config->hsfile, "w");
934 if (!fp) return;
935 int i;
936 for (i=0; i<level_max; i++) {
937 fprintf(fp, "%s,%d\n", hstable[i]->name, hstable[i]->time);
939 fclose(fp);
942 int enkludge;
944 void enter_name_open()
946 modalwindow = enter_name_window;
947 enkludge = 1;
950 void enter_name_close()
952 modalwindow = NULL;
953 enkludge = 0;
955 player_name = tb_en1->text;
957 if (hstable[level]->name) {
958 free(hstable[level]->name);
960 hstable[level]->name = clonestr(player_name);
961 hstable[level]->time = second_count;
962 update_hsw();
963 write_hstable();
966 void check_hs()
968 if (hstable[level]->time == -1 || second_count < hstable[level]->time) {
969 enter_name_open();
973 void init_tileimg(SDL_Surface * tileimg[64],int bgcolor)
975 int i, j;
976 SDL_PixelFormat *fmt = screen->format;
977 SDL_Rect rect;
978 int c;
980 pipex = cellw / 2 - pipet / 2;
981 pipey = cellw / 2 - pipet / 2;
982 pipew = cellw / 2 + pipet / 2;
983 pipeh = cellh / 2 + pipet / 2;
985 SDL_Rect entirecell = (SDL_Rect){
986 .x = 0,
987 .y = 0,
988 .w = cellw,
989 .h = cellh
992 for (i=0; i<64; i++) {
993 tileimg[i] = SDL_CreateRGBSurface(0, cellw, cellh, fmt->BitsPerPixel,
994 fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
996 SDL_FillRect(tileimg[i], &entirecell, ctable[bgcolor]);
998 for (j=0; j<4; j++) {
999 if ((i + 1) & (1 << j)) {
1000 switch (j) {
1001 case 0:
1002 rect.x = pipex;
1003 rect.y = 0;
1004 rect.w = pipet;
1005 rect.h = pipeh;
1006 break;
1007 case 1:
1008 rect.x = pipex;
1009 rect.y = pipey;
1010 rect.w = pipew;
1011 rect.h = pipet;
1012 break;
1013 case 2:
1014 rect.x = pipex;
1015 rect.y = pipey;
1016 rect.w = pipet;
1017 rect.h = pipeh;
1018 break;
1019 case 3:
1020 rect.x = 0;
1021 rect.y = pipey;
1022 rect.w = pipew;
1023 rect.h = pipet;
1024 break;
1026 if (i >= 16) {
1027 c = ctable[c_on];
1028 } else c = ctable[c_off];
1029 SDL_FillRect(tileimg[i], &rect, c);
1034 for (i=1; i<32; i*=2) {
1035 rect.x = cellw / 2 - 2 * pipet;
1036 rect.y = cellh / 2 - 2 * pipet;
1037 rect.w = pipet * 4;
1038 rect.h = pipet * 4;
1039 rect.x++;
1040 rect.w-=2;
1041 rect.y++;
1042 rect.h-=2;
1043 SDL_FillRect(tileimg[i-1], &rect, ctable[c_down]);
1044 SDL_FillRect(tileimg[i-1+16], &rect, ctable[c_up]);
1048 void reset_move_count()
1050 move_count = 0;
1051 label_put_text(l_moves, "moves: 0");
1054 void increment_move_count()
1056 char s[80];
1057 move_count++;
1058 sprintf(s, "moves: %d", move_count);
1059 label_put_text(l_moves, s);
1062 void reset_time()
1064 label_put_text(l_time, "time: 0");
1067 void update_time()
1069 static char s[80];
1071 ms_count += tick - tick_old;
1072 while (ms_count >= 1000) {
1073 ms_count -= 1000;
1074 second_count++;
1075 sprintf(s, "time: %d", second_count);
1076 label_put_text(l_time, s);
1080 void resize()
1081 //position everything based on board size
1083 sourcex = boardw / 2 - 1;
1084 sourceytop = boardh / 2;
1085 sourceybottom = sourceytop + 1;
1086 int w = cellw * boardw + (boardw + 1) * border + 2 * padding;
1087 int h = cellh * boardh + (boardh + 1) * border + 2 * padding;
1088 widget_put_geometry(arena, 0, vsize, w, h);
1089 set_video(w, h + 2 * vsize);
1090 widget_put_geometry(root, 0, 0, w, h + 2 * vsize);
1091 widget_put_geometry(menu, 0, 0, w, vsize);
1092 menubar_auto_layout(menu);
1093 widget_put_geometry(statusbar, 0, h + vsize, w, vsize);
1095 widget_put_geometry(l_moves, 8, h + vsize, 64, vsize);
1096 widget_put_geometry(l_time, w - 48, h + vsize, 64, vsize);
1098 widget_put_geometry((widget_ptr) about_window,
1099 w/2 - 50, h/2 - 50, 100, 100);
1100 widget_put_geometry((widget_ptr) l_about1, 10, 10, 60, vsize);
1101 widget_put_geometry((widget_ptr) l_about2, 10, 30, 60, vsize);
1102 widget_put_geometry((widget_ptr) b_about1, 30, 80, 30, vsize);
1104 /* vertical sizes and positions for the high score window */
1105 int const hslabely = 5;
1106 int const hslabelheight = vsize;
1107 int const hslisty = hslabely + hslabelheight + 8;
1108 int const hslisteachheight = vsize;
1109 int const hslistheight = hslisteachheight * level_max;
1110 int const hsoky = hslisty + hslistheight + 8;
1111 int const hsokheight = vsize;
1112 int const hswindowheight = hsoky + hsokheight + 5;
1114 widget_put_geometry((widget_ptr) hs_window,
1115 w/2 - 75, h/2 - 60, 170, hswindowheight);
1116 widget_put_geometry((widget_ptr) l_hs1, 10, hslabely, 60, hslabelheight);
1117 widget_put_geometry((widget_ptr) b_hs1, 100, hsoky, 30, hsokheight);
1120 int i;
1121 for (i=0; i<level_max; i++) {
1122 int y = hslisty + (hslisteachheight * i);
1123 widget_put_geometry((widget_ptr) hsw[i]->level,
1124 10, y, 20, hslisteachheight);
1125 widget_put_geometry((widget_ptr) hsw[i]->time,
1126 60, y, 20, hslisteachheight);
1127 widget_put_geometry((widget_ptr) hsw[i]->name,
1128 90, y, 20, hslisteachheight);
1132 widget_put_geometry((widget_ptr) enter_name_window,
1133 10, h/2 - 30, w - 20, 67);
1134 widget_put_geometry((widget_ptr) l_en1, 10, 0, 60, vsize);
1135 widget_put_geometry((widget_ptr) tb_en1, 5, vsize + 5, w - 30, vsize);
1136 widget_put_geometry((widget_ptr) b_en1, w - 60, 45, 30, vsize);
1138 ((widget_ptr) root)->computexy((widget_ptr) root);
1139 ((widget_ptr) about_window)->computexy((widget_ptr) about_window);
1140 ((widget_ptr) hs_window)->computexy((widget_ptr) hs_window);
1141 ((widget_ptr) enter_name_window)->computexy((widget_ptr) enter_name_window);
1144 void new_game()
1146 char s[BUFSIZ];
1147 strcpy(s,"NetWalk - ");
1148 strcat(s,level_name[level]);
1149 SDL_WM_SetCaption(s,s);
1151 switch(level) {
1152 case level_easy:
1153 boardw = 5; boardh = 5;
1154 wrap_flag = 0;
1155 no_fourway = 0;
1156 break;
1157 case level_medium:
1158 boardw = 10; boardh = 9;
1159 wrap_flag = 0;
1160 no_fourway = 0;
1161 break;
1162 case level_hard:
1163 boardw = 10; boardh = 9;
1164 wrap_flag = 1;
1165 no_fourway = 1;
1166 break;
1167 case level_veryhard:
1168 boardw = 20; boardh = 18;
1169 wrap_flag = 1;
1170 no_fourway = 1;
1171 break;
1172 case level_giant:
1173 boardw = 50; boardh = 50;
1174 wrap_flag = 0;
1175 no_fourway = 1;
1176 break;
1177 case level_absurd:
1178 boardw = 50; boardh = 50;
1179 wrap_flag = 1;
1180 no_fourway = 1;
1181 break;
1182 default:
1183 break;
1185 resize();
1186 srand(time(NULL));
1187 generate_maze();
1188 clear_flags();
1189 scramble();
1190 check_live();
1191 reset_move_count();
1192 reset_time();
1193 ms_count = 0;
1194 tick = SDL_GetTicks();
1195 second_count = 0;
1198 void handle_mousebuttonup(SDL_Event *event)
1200 int x = event->button.x;
1201 int y = event->button.y;
1202 if (openedmenu) {
1203 int i;
1204 menuitem_ptr it;
1205 menu_ptr m;
1207 m = openedmenu->submenu;
1208 for (i=0; i<m->item_count; i++) {
1209 it = m->item_list[i];
1210 if (in_widget((widget_ptr) it, x, y)) {
1211 widget_raise_signal((widget_ptr) it, signal_activate);
1212 break;
1215 openedmenu = NULL;
1217 return;
1218 } else if (state == state_button) {
1219 state = state_game;
1220 if (in_widget(button_selection, x, y)) {
1221 widget_raise_signal(button_selection, signal_activate);
1222 return;
1227 void handle_click(int button, int x, int y)
1229 widget_ptr wid;
1231 if (modalwindow) {
1232 wid = (widget_ptr) modalwindow;
1233 if (in_widget(wid, x, y) && (wid->handle_click)) {
1234 wid->handle_click(wid, button, x, y);
1236 return;
1239 wid = (widget_ptr) root;
1240 wid->handle_click(wid, button, x, y);
1243 void arena_handle_click(widget_ptr p, int button, int x, int y)
1245 int i, j;
1246 int d;
1248 if (state != state_game) return;
1250 i = (x - padding - border) / (cellw + border);
1251 j = (y - padding - border) / (cellh + border);
1252 if (i >= boardw || j >= boardh) return;
1254 if (game_won) {
1255 if (i == sourcex && (j == sourceytop || j == sourceybottom)) {
1256 new_pulse(sourcex, sourceybottom, -1);
1257 new_pulse(sourcex, sourceytop, -1);
1258 } else {
1259 new_pulse(i, j, -1);
1261 return;
1264 //temporarily merge server squares
1265 board[sourcex][sourceybottom] |= board[sourcex][sourceytop] & 1;
1266 if (i == sourcex && j == sourceytop) {
1267 j = sourceybottom;
1269 d = board[i][j] & 15;
1270 switch(button) {
1271 case SDL_BUTTON_LEFT:
1272 d = rotatecw(d, 3);
1273 increment_move_count();
1274 break;
1275 case SDL_BUTTON_RIGHT:
1276 d = rotatecw(d, 1);
1277 increment_move_count();
1278 break;
1280 board[i][j] &= ~15;
1281 board[i][j] += d;
1283 board[sourcex][sourceytop] &= ~1;
1284 board[sourcex][sourceytop] |= board[sourcex][sourceybottom] & 1;
1285 board[sourcex][sourceybottom] &= ~1;
1287 if(button == SDL_BUTTON_MIDDLE)
1288 flags[i][j] ^= 0x1;
1290 check_live();
1291 if (game_won) {
1292 pulse_count = 0;
1294 check_hs();
1298 void quit()
1300 state = state_quit;
1303 void quit_menu(widget_ptr w, void *data)
1305 quit();
1308 void new_game_menu(widget_ptr w, void *data)
1310 new_game();
1313 void about_open(widget_ptr w, void *data)
1315 modalwindow = about_window;
1318 void about_close(widget_ptr w, void *data)
1320 modalwindow = NULL;
1323 void hs_open(widget_ptr w, void *data)
1325 modalwindow = hs_window;
1328 void hs_close(widget_ptr w, void *data)
1330 modalwindow = NULL;
1333 void set_level(widget_ptr w, void *data)
1335 level = (intptr_t) data;
1336 new_game();
1339 void handle_key(int key, int mod)
1341 if (openedmenu) {
1342 switch(key) {
1343 case SDLK_ESCAPE:
1344 openedmenu = NULL;
1346 return;
1349 if (enkludge) {
1350 switch(key) {
1351 case SDLK_LEFT:
1352 textbox_left(tb_en1);
1353 break;
1354 case SDLK_RIGHT:
1355 textbox_right(tb_en1);
1356 break;
1357 case SDLK_DELETE:
1358 textbox_delete(tb_en1);
1359 break;
1360 case SDLK_BACKSPACE:
1361 textbox_backspace(tb_en1);
1362 break;
1363 default:
1364 if (key < 256 && key >= 32) {
1365 if (mod & KMOD_SHIFT) {
1366 textbox_insert(tb_en1, shifttable[key]);
1367 } else {
1368 textbox_insert(tb_en1, key);
1371 break;
1373 return;
1376 switch(key) {
1377 case SDLK_d:
1378 enter_name_open();
1379 break;
1380 case SDLK_ESCAPE:
1381 case SDLK_q:
1382 quit();
1383 break;
1384 case SDLK_F2:
1385 new_game();
1386 break;
1390 void update_screen()
1392 SDL_FillRect(screen, NULL, 0);
1393 widget_update((widget_ptr) root);
1394 if (openedmenu) {
1395 int i;
1396 menuitem_ptr it;
1398 for (i=0; i<menu->item_count; i++) {
1399 it = menu->item_list[i];
1400 if (in_widget((widget_ptr) it, lastmousex, lastmousey)) {
1401 open_submenu((widget_ptr) it, NULL);
1404 menu_update(openedmenu->submenu);
1406 if (modalwindow) {
1407 widget_update((widget_ptr) modalwindow);
1409 SDL_Flip(screen);
1412 void window_update(widget_ptr p)
1414 window_ptr w = (window_ptr) p;
1415 widget_ptr wid;
1416 int i;
1417 SDL_Rect dst;
1419 if (p != (widget_ptr) root) {
1420 dst.x = -1;
1421 dst.y = -1;
1422 dst.w = p->w + 2;
1423 dst.h = p->h + 2;
1424 widget_fillrect(p, &dst, c_windowborder);
1425 widget_fill(p, c_background);
1428 for (i=0; i<w->widget_count; i++) {
1429 wid = w->widget_list[i];
1430 widget_update(wid);
1434 void window_handle_click(widget_ptr p, int button, int x, int y)
1436 widget_ptr wid;
1437 window_ptr window = (window_ptr) p;
1438 int i;
1440 for (i=0; i<window->widget_count; i++) {
1441 wid = window->widget_list[i];
1442 if (in_widget(wid, x, y) && (wid->handle_click)) {
1443 wid->handle_click(wid, button, x - wid->x, y - wid->y);
1444 return;
1449 void window_add_widget(window_ptr r, void *p)
1451 widget_ptr wid = (widget_ptr) p;
1452 r->widget_list[r->widget_count] = wid;
1453 r->widget_count++;
1454 wid->parent = (widget_ptr) r;
1455 wid->x += r->widget.x;
1456 wid->y += r->widget.y;
1459 void window_computexy(widget_ptr wid)
1461 int i;
1462 window_ptr w = (window_ptr) wid;
1464 widget_computexy(wid);
1465 for (i=0; i<w->widget_count; i++) {
1466 w->widget_list[i]->computexy(w->widget_list[i]);
1470 void window_init(window_ptr w)
1472 widget_init((widget_ptr) w);
1473 w->widget_count = 0;
1474 w->widget.update = window_update;
1475 w->widget.handle_click = window_handle_click;
1476 w->widget.computexy = window_computexy;
1479 static void add_shiftstring(char *s1, char *s2)
1481 int i;
1482 for (i=0; i<strlen(s1); i++) {
1483 shifttable[(int) s1[i]] = s2[i];
1487 int main(int argc, char *argv[])
1489 SDL_Event event;
1491 //setup names
1492 level_name[level_easy] = "Newbie";
1493 level_name[level_medium] = "Normal";
1494 level_name[level_hard] = "Nerd";
1495 level_name[level_veryhard] = "Nutcase";
1496 level_name[level_giant] = "Nonsense";
1497 level_name[level_absurd] = "No Sleep";
1499 //setup shifttable
1501 int i;
1503 for (i=0; i<256; i++) shifttable[i] = i;
1505 for (i='a'; i<='z'; i++) {
1506 shifttable[i] = i - 32;
1509 add_shiftstring("1234567890-=", "!@#$%^&*()_+");
1510 add_shiftstring("[]\\;',./`", "{}|:\"<>?~");
1513 config_load(config);
1514 read_hstable();
1515 init();
1517 init_rgbtable();
1519 font = TTF_OpenFont(config->fontname, config->fontsize);
1520 if (!font) {
1521 fprintf(stderr, "error loading font %s\n", config->fontname);
1522 exit(1);
1525 window_init(root);
1527 //need to set video mode here to initialize colour table
1528 set_video(100, 100);
1530 //setup enter name box
1532 window_init(enter_name_window);
1534 label_init(l_en1);
1535 label_put_text(l_en1, "Enter name:");
1536 window_add_widget(enter_name_window, l_en1);
1538 textbox_init(tb_en1);
1539 textbox_put_text(tb_en1, "Anonymous");
1540 window_add_widget(enter_name_window, tb_en1);
1542 button_init(b_en1);
1543 button_put_text(b_en1, "Ok");
1544 window_add_widget(enter_name_window, b_en1);
1545 widget_put_handler((widget_ptr) b_en1, enter_name_close, signal_activate);
1548 //setup the "arena": where the action is
1550 widget_init((widget_ptr) arena);
1551 arena->widget.update = arena_update;
1552 arena->widget.handle_click = arena_handle_click;
1553 window_add_widget(root, arena);
1556 //status bar: mainly for showing the time
1558 widget_init((widget_ptr) statusbar);
1559 statusbar->update = statusbar_update;
1560 window_add_widget(root, statusbar);
1562 //setup moves and time
1563 label_init(l_moves);
1564 if (config->showmoves) {
1565 window_add_widget(root, l_moves);
1568 label_init(l_time);
1569 window_add_widget(root, l_time);
1572 //setup the menus
1573 menuitem_t it1, it2;
1574 menu_t m1, m2;
1576 menuitem_ptr it;
1578 intptr_t i;
1580 menubar_init(menu);
1581 window_add_widget(root, menu);
1582 menuitem_init(it1);
1583 menuitem_put_text(it1, "Game");
1584 menubar_add_item(menu, it1);
1585 menuitem_init(it2);
1586 menuitem_put_text(it2, "Help");
1587 menubar_add_item(menu, it2);
1589 menu_init(m1);
1591 it = menuitem_new();
1592 menuitem_put_text(it, "New game");
1593 widget_put_handler((widget_ptr) it, new_game_menu, signal_activate);
1594 menu_add_item(m1, it);
1596 for (i=0; i<level_max; i++) {
1597 it = menuitem_new();
1598 menuitem_put_text(it, level_name[i]);
1599 widget_put_handler_data((widget_ptr) it,
1600 set_level, (void *) i, signal_activate);
1601 menu_add_item(m1, it);
1603 it = menuitem_new();
1604 menuitem_put_text(it, "High Scores");
1605 widget_put_handler((widget_ptr) it, hs_open, signal_activate);
1606 menu_add_item(m1, it);
1607 it = menuitem_new();
1608 menuitem_put_text(it, "Quit");
1609 widget_put_handler((widget_ptr) it, quit_menu, signal_activate);
1610 menu_add_item(m1, it);
1612 menuitem_set_submenu(it1, m1);
1614 menu_init(m2);
1616 it = menuitem_new();
1617 menuitem_put_text(it, "About");
1618 widget_put_handler((widget_ptr) it, about_open, signal_activate);
1619 menu_add_item(m2, it);
1621 menuitem_set_submenu(it2, m2);
1624 //setup about box
1626 window_init(about_window);
1628 label_init(l_about1);
1629 label_put_text(l_about1, "NetWalk " VERSION_STRING);
1630 window_add_widget(about_window, l_about1);
1632 label_init(l_about2);
1633 label_put_text(l_about2, "Ben Lynn");
1634 window_add_widget(about_window, l_about2);
1636 button_init(b_about1);
1637 button_put_text(b_about1, "Ok");
1638 window_add_widget(about_window, b_about1);
1639 widget_put_handler((widget_ptr) b_about1, about_close, signal_activate);
1642 //setup hiscores box
1644 int i;
1645 window_init(hs_window);
1647 label_init(l_hs1);
1648 label_put_text(l_hs1, "High Scores");
1649 window_add_widget(hs_window, l_hs1);
1651 button_init(b_hs1);
1652 button_put_text(b_hs1, "Ok");
1653 window_add_widget(hs_window, b_hs1);
1654 widget_put_handler((widget_ptr) b_hs1, hs_close, signal_activate);
1656 for (i=0; i<level_max; i++) {
1657 label_init(hsw[i]->level);
1658 label_put_text(hsw[i]->level, level_name[i]);
1659 window_add_widget(hs_window, hsw[i]->level);
1660 label_init(hsw[i]->name);
1661 window_add_widget(hs_window, hsw[i]->name);
1662 label_init(hsw[i]->time);
1663 window_add_widget(hs_window, hsw[i]->time);
1667 resize();
1669 update_hsw();
1671 init_tileimg(unmarked_tileimg,c_unmarkedbg);
1672 init_tileimg(marked_tileimg,c_markedbg);
1673 new_game();
1675 while (state != state_quit && !interrupted) {
1676 tick_old = tick;
1677 tick = SDL_GetTicks();
1678 if (!game_won) {
1679 update_time();
1681 SDL_GetMouseState(&lastmousex, &lastmousey);
1682 while (SDL_PollEvent(&event)) {
1683 switch (event.type) {
1684 case SDL_KEYDOWN:
1685 handle_key(event.key.keysym.sym, SDL_GetModState());
1686 break;
1687 case SDL_MOUSEBUTTONDOWN:
1688 handle_click(event.button.button, event.button.x, event.button.y);
1689 break;
1690 case SDL_MOUSEBUTTONUP:
1691 handle_mousebuttonup(&event);
1692 break;
1693 case SDL_QUIT:
1694 quit();
1695 break;
1698 update_screen();
1699 SDL_Delay(20);
1701 return 0;