Tue Jun 22 23:13:04 PDT 2004
[netwalk.git] / main.c
blob0606b7e09ac1870d704ec8a32febc918d27a25c4
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 char *config_file = "config";
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_max,
69 level_custom
72 enum {
73 cellw = 24,
74 cellh = 24,
75 border = 1,
76 padding = 10
79 char shifttable[256];
80 int show_moves_flag = 0;
82 struct config_s {
83 char *fontname;
84 char *hsfile;
85 int fontsize;
87 typedef struct config_s *config_ptr;
88 typedef struct config_s config_t[1];
90 struct hsentry_s {
91 char *name;
92 int time;
94 typedef struct hsentry_s *hsentry_ptr;
95 typedef struct hsentry_s hsentry_t[1];
97 hsentry_t hstable[level_max];
98 char *level_name[level_max];
100 //TODO use this somehow
101 struct gameparm_s {
102 int boardw, boardh;
103 int wrap;
105 typedef struct gameparm_s *gameparm_ptr;
106 typedef struct gameparm_s gameparm_t[1];
108 gameparm_t gp;
110 SDL_Surface *screen;
111 TTF_Font *font;
112 int lastmousex, lastmousey;
113 config_t config;
114 char *player_name;
115 int game_won;
116 int state;
118 struct button_s {
119 struct widget_s widget;
120 char *text;
121 SDL_Surface *img;
123 typedef struct button_s button_t[1];
124 typedef struct button_s *button_ptr;
126 struct label_s {
127 struct widget_s widget;
128 char *text;
129 SDL_Surface *img;
131 typedef struct label_s label_t[1];
132 typedef struct label_s *label_ptr;
134 struct textbox_s {
135 struct widget_s widget;
136 char text[1024];
137 int i;
138 SDL_Surface *img;
140 typedef struct textbox_s textbox_t[1];
141 typedef struct textbox_s *textbox_ptr;
143 struct arena_s {
144 struct widget_s widget;
146 typedef struct arena_s arena_t[1];
147 typedef struct arena_s *arena_ptr;
149 struct menuitem_s {
150 struct widget_s widget;
151 char *text;
152 SDL_Surface *img;
153 struct menu_s *submenu;
155 typedef struct menuitem_s menuitem_t[1];
156 typedef struct menuitem_s *menuitem_ptr;
158 struct menu_s {
159 struct widget_s widget;
160 //TODO: replace array with list
161 menuitem_ptr item_list[64];
162 int item_count;
164 typedef struct menu_s menu_t[1];
165 typedef struct menu_s *menu_ptr;
167 struct menubar_s {
168 struct widget_s widget;
169 //TODO: replace array with list
170 menuitem_ptr item_list[64];
171 int item_count;
173 typedef struct menubar_s menubar_t[1];
174 typedef struct menubar_s *menubar_ptr;
176 struct window_s {
177 struct widget_s widget;
178 //TODO: replace array with list
179 struct widget_s *widget_list[64];
180 int widget_count;
183 typedef struct window_s window_t[1];
184 typedef struct window_s *window_ptr;
186 struct hsw_s {
187 label_t level;
188 label_t time;
189 label_t name;
191 typedef struct hsw_s *hsw_ptr;
192 typedef struct hsw_s hsw_t[1];
194 hsw_t hsw[level_max];
196 arena_t arena;
197 window_t root;
198 label_t l_moves;
199 label_t l_time;
200 menubar_t menu;
201 menuitem_ptr openedmenu;
202 window_ptr modalwindow;
203 window_t about_window;
204 window_t hs_window;
205 window_t enter_name_window;
206 label_t l_about1;
207 label_t l_about2;
208 button_t b_about1;
209 widget_t statusbar;
211 label_t l_hs1;
212 button_t b_hs1;
214 label_t l_en1;
215 textbox_t tb_en1;
216 button_t b_en1;
218 char *clonestr(char *s)
220 char *res = malloc(sizeof(char) * strlen(s) + 1);
221 strcpy(res, s);
222 return res;
225 SDL_Surface *font_render(char *s, int c)
227 return TTF_RenderText_Solid(font, s, rgbtable[c]);
230 void statusbar_update(widget_ptr w)
232 widget_lowered_background(w);
235 void menu_init(menu_ptr m)
237 widget_init((widget_ptr) m);
238 m->item_count = 0;
241 void menu_update(menu_ptr m)
243 int i;
244 menuitem_ptr it;
245 SDL_Rect rect;
247 widget_fill((widget_ptr) m, c_background);
248 for (i=0; i<m->item_count; i++) {
249 it = m->item_list[i];
250 if (in_widget((widget_ptr) it, lastmousex, lastmousey)) {
251 rect.x = 2;
252 rect.y = vsize * i;
253 rect.w = ((widget_ptr) it)->w - 4;
254 rect.h = vsize;
255 widget_fillrect((widget_ptr) m, &rect, c_menubg);
257 rect.x = 8;
258 rect.y = vsize * i + 4;
259 widget_blit((widget_ptr) m, it->img, NULL, &rect);
263 void menu_add_item(menu_ptr m, menuitem_ptr it)
265 m->item_list[m->item_count] = it;
266 m->item_count++;
269 void menuitem_init(menuitem_ptr m)
271 widget_init((widget_ptr) m);
272 m->text = NULL;
273 m->img = NULL;
274 m->submenu = NULL;
277 menuitem_ptr menuitem_new()
279 menuitem_ptr it;
281 it = (menuitem_ptr) malloc(sizeof(menuitem_t));
282 menuitem_init(it);
284 return it;
287 void menuitem_put_text(menuitem_ptr m, char *s)
289 SDL_Surface *tmp;
291 m->text = s;
292 if (m->img) SDL_FreeSurface(m->img);
293 tmp = font_render(s, c_text);
294 m->img = SDL_DisplayFormat(tmp);
295 SDL_FreeSurface(tmp);
298 void open_submenu(widget_ptr p, void *data)
300 int i;
301 int w = 0;
302 menuitem_ptr it;
304 openedmenu = (menuitem_ptr) p;
305 menu_ptr m = openedmenu->submenu;
307 for (i=0; i<m->item_count; i++) {
308 it = m->item_list[i];
309 if (w < it->img->w) w = it->img->w;
311 w += 12;
313 m->widget.x = m->widget.parent->x;
314 m->widget.y = m->widget.parent->y + vsize;
316 m->widget.w = w;
317 m->widget.h = vsize * m->item_count + 1;
319 for (i=0; i<m->item_count; i++) {
320 it = m->item_list[i];
321 it->widget.x = 0 + m->widget.x;
322 it->widget.y = vsize * i + 4 + m->widget.y;
323 it->widget.w = w;
324 it->widget.h = vsize;
328 void menuitem_set_submenu(menuitem_ptr it, menu_ptr m)
330 it->submenu = m;
331 //it->widget.signal_handler[signal_activate] = open_submenu;
332 widget_put_handler((widget_ptr) it, open_submenu, signal_activate);
333 m->widget.parent = (widget_ptr) it;
336 void menubar_update(widget_ptr wid)
338 SDL_Rect dst;
339 menubar_ptr m = (menubar_ptr) wid;
340 menuitem_ptr it;
341 int i;
343 widget_raised_background(wid);
345 for (i=0; i<m->item_count; i++) {
346 it = m->item_list[i];
347 if (it == openedmenu) {
348 dst.x = it->widget.x + 2;
349 dst.y = it->widget.y + 2;
350 dst.w = it->widget.w - 4;
351 dst.h = vsize - 2;
352 widget_fillrect(wid, &dst, c_menubg);
354 if (it->img) {
355 dst.x = it->widget.x + 5;
356 dst.y = it->widget.y + 2;
357 widget_blit(m, it->img, NULL, &dst);
362 void menubar_handle_click(widget_ptr p, int button, int x, int y)
364 int i;
365 menubar_ptr m = (menubar_ptr) p;
366 menuitem_ptr it;
368 for (i=0; i<m->item_count; i++) {
369 it = m->item_list[i];
370 if (in_widget((widget_ptr) it, x, y)) {
371 widget_raise_signal((widget_ptr) it, signal_activate);
372 return;
377 void menubar_init(menubar_ptr m)
379 widget_init((widget_ptr) m);
380 m->widget.update = menubar_update;
381 m->item_count = 0;
382 m->widget.handle_click = menubar_handle_click;
385 void menubar_add_item(menubar_ptr m, menuitem_ptr it)
387 m->item_list[m->item_count] = it;
388 m->item_count++;
389 it->widget.parent = (widget_ptr) m;
392 void menubar_auto_layout(menubar_ptr m)
394 int i, x, y;
395 menuitem_ptr it;
397 x = 0;
398 y = 0;
399 for (i=0; i<m->item_count; i++) {
400 it = m->item_list[i];
401 if (it->img) {
402 it->widget.x = x;
403 it->widget.y = y;
404 it->widget.w = it->img->w + 10;
405 it->widget.h = it->img->h;
406 x += it->img->w + 10;
411 void label_update(widget_ptr p)
413 SDL_Rect dst;
414 label_ptr l = (label_ptr) p;
416 if (l->img) {
417 dst.x = 0;
418 dst.y = 4;
419 widget_blit(l, l->img, NULL, &dst);
423 void label_init(label_ptr l)
425 widget_init((widget_ptr) l);
426 l->text = NULL;
427 l->img = NULL;
428 l->widget.update = label_update;
431 void label_put_text(label_ptr l, char *s)
433 SDL_Surface *tmp;
435 if (l->img) SDL_FreeSurface(l->img);
436 if (l->text) free(l->text);
437 l->text = clonestr(s);
438 tmp = font_render(s, c_text);
439 l->img = SDL_DisplayFormat(tmp);
440 SDL_FreeSurface(tmp);
443 void textbox_update_img(textbox_ptr tb)
445 SDL_Surface *tmp;
447 if (tb->img) SDL_FreeSurface(tb->img);
448 tmp = font_render(tb->text, c_text);
449 if (tmp) {
450 tb->img = SDL_DisplayFormat(tmp);
451 SDL_FreeSurface(tmp);
452 } else {
453 tb->img = NULL;
457 void textbox_left(textbox_ptr tb)
459 if (tb->i > 0) tb->i--;
462 void textbox_right(textbox_ptr tb)
464 if (tb->i < strlen(tb->text)) tb->i++;
467 void textbox_delete(textbox_ptr tb)
469 if (tb->i > 0) {
470 tb->i--;
471 tb->text[tb->i] = 0;
472 textbox_update_img(tb);
476 void textbox_backspace(textbox_ptr tb)
478 char *s = &tb->text[tb->i];
479 if (tb->i) {
480 memmove(s - 1, s, strlen(s) + 1);
481 tb->i--;
482 textbox_update_img(tb);
486 void textbox_insert(textbox_ptr tb, char c)
488 char *s = &tb->text[tb->i];
489 memmove(s + 1, s, strlen(s) + 1);
490 tb->text[tb->i] = c;
491 tb->i++;
492 textbox_update_img(tb);
495 void textbox_update(widget_ptr p)
497 SDL_Rect dst;
498 textbox_ptr tb = (textbox_ptr) p;
500 dst.x = 0;
501 dst.y = 0;
502 dst.w = p->w;
503 dst.h = p->h;
504 widget_fillrect(p, &dst, c_text);
505 dst.x++;
506 dst.y++;
507 dst.w-=2;
508 dst.h-=2;
509 widget_fillrect(p, &dst, c_canvas);
511 if (tb->img) {
512 dst.x = 1;
513 dst.y = 3;
514 widget_blit(tb, tb->img, NULL, &dst);
518 char s[1024];
519 int w, h;
521 strncpy(s, tb->text, tb->i);
522 s[tb->i] = 0;
523 TTF_SizeText(font, s, &w, &h);
525 dst.x = w;
526 dst.y = 2;
527 dst.w = 1;
528 dst.h = vsize - 2;
529 widget_fillrect(p, &dst, c_text);
533 void textbox_init(textbox_ptr l)
535 widget_init((widget_ptr) l);
536 l->img = NULL;
537 l->widget.update = textbox_update;
540 void textbox_put_text(textbox_ptr tb, char *s)
542 strcpy(tb->text, s);
543 tb->i = strlen(s);
544 textbox_update_img(tb);
547 static widget_ptr button_selection;
549 void button_handle_click(widget_ptr p, int button, int x, int y)
551 state = state_button;
552 button_selection = p;
555 void button_update(widget_ptr p)
557 SDL_Rect dst;
558 label_ptr l = (label_ptr) p;
560 if (state == state_button && button_selection == p
561 && in_widget(p, lastmousex, lastmousey)) {
562 widget_lowered_background(p);
563 } else {
564 widget_raised_background(p);
566 if (l->img) {
567 dst.x = 5;
568 dst.y = 2;
569 widget_blit(l, l->img, NULL, &dst);
573 void button_init(button_ptr b)
575 widget_init((widget_ptr) b);
576 b->text = NULL;
577 b->img = NULL;
578 b->widget.update = button_update;
579 b->widget.handle_click = button_handle_click;
582 void button_put_text(button_ptr b, char *s)
584 SDL_Surface *tmp;
586 if (b->img) SDL_FreeSurface(b->img);
587 if (b->text) free(b->text);
588 b->text = clonestr(s);
589 tmp = font_render(s, c_text);
590 b->img = SDL_DisplayFormat(tmp);
591 SDL_FreeSurface(tmp);
594 void set_video(int w, int h)
596 int flags;
597 flags = SDL_DOUBLEBUF;
599 screen = SDL_SetVideoMode(w, h, 0, flags);
600 init_ctable(screen->format);
602 if (!screen) {
603 fprintf(stderr, "Can't set video mode: %s\n", SDL_GetError());
604 exit(1);
607 //SDL_ShowCursor(SDL_DISABLE);
610 void set_interrupted(int i)
612 interrupted = 1;
615 void init()
617 int status;
619 signal(SIGINT, set_interrupted);
620 signal(SIGTERM, set_interrupted);
622 //if (SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) {
623 if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) {
624 fprintf(stderr, "Can't init SDL: %s\n", SDL_GetError());
625 exit(1);
627 atexit(SDL_Quit);
628 status = TTF_Init();
629 if (status) {
630 fprintf(stderr, "Can't init SDL_ttf\n");
631 exit(-1);
633 atexit(TTF_Quit);
635 SDL_WM_SetCaption("NetWalk", "NetWalk");
638 SDL_Surface *tileimg[64];
639 int level = level_medium;
640 int tick;
641 int tick_old;
642 int pipey, pipex;
643 int pipew, pipeh;
644 int pipet = 4;
645 int move_count;
646 int ms_count;
647 int second_count;
649 void draw_tile(widget_ptr wid, int i, int j)
651 SDL_Rect rect;
652 int index;
654 rect.x = padding + border + i * (cellw + border);
655 rect.y = padding + border + j * (cellh + border);
657 index = board[i][j] - 1;
658 widget_blit(wid, tileimg[index], NULL, &rect);
661 typedef struct {
662 int x, y;
663 int dir;
664 int tick;
665 } pulse_s;
667 pulse_s pulse_list[pulse_max];
668 int pulse_count;
670 void new_pulse(int x, int y, int d)
672 int i, j;
674 if (pulse_count >= pulse_max) return;
676 //stop incoming server pulses
677 if (x == sourcex && (y == sourceybottom || y ==sourceytop) && d != -1) {
678 return;
681 i = board[x][y];
682 for (j=0; j<4; j++) {
683 if ((j != d) && (i & (1 << j))) {
684 pulse_list[pulse_count].x = x;
685 pulse_list[pulse_count].y = y;
686 pulse_list[pulse_count].dir = j;
687 pulse_list[pulse_count].tick = tick;
688 pulse_count++;
689 if (pulse_count >= pulse_max) return;
694 void server_pulse()
696 new_pulse(sourcex, sourceybottom, -1);
697 new_pulse(sourcex, sourceytop, -1);
700 void animate_pulse(widget_ptr wid)
702 int i, dt, d;
703 int x, y;
704 SDL_Rect rect;
705 int speed = 500;
707 if (!pulse_count) {
708 server_pulse();
711 rect.w = pipet + 2;
712 rect.h = pipet + 2;
713 i = 0;
714 while (i<pulse_count) {
715 x = pulse_list[i].x;
716 y = pulse_list[i].y;
717 d = pulse_list[i].dir;
718 dt = tick - pulse_list[i].tick;
719 if (dt > speed) {
720 pulse_count--;
721 memmove(&pulse_list[i], &pulse_list[i+1], sizeof(pulse_s) * (pulse_count - i));
723 add_dir(&x, &y, x, y, d);
724 new_pulse(x, y, (d + 2) % 4);
725 } else {
726 //wrap cases:
727 if (dir[d].x == -1 && 2 * dt > speed && !x) {
728 x += boardw;
730 if (dir[d].x == 1 && 2 * dt > speed && x == boardw - 1) {
731 x -= boardw;
733 if (dir[d].y == -1 && 2 * dt > speed && !y) {
734 y += boardh;
736 if (dir[d].y == 1 && 2 * dt > speed && y == boardh - 1) {
737 y -= boardh;
740 rect.x = x * (cellw + border) + pipex - 1;
741 rect.x += dir[d].x * (cellw + border) * dt / speed;
742 rect.x += border + padding;
744 rect.y = y * (cellh + border) + border + padding;
745 rect.y += dir[d].y * (cellh + border) * dt / speed;
746 rect.y += pipey - 1;
747 widget_fillrect(wid, &rect, c_pulse);
748 i++;
753 void arena_update(widget_ptr wid)
755 int i, j;
756 SDL_Rect rect;
757 int bc;
758 int c;
760 //draw grid
761 rect.x = padding;
762 rect.y = padding;
763 rect.w = cellw * boardw + (boardw + 1) * border;
764 rect.h = border;
766 if (game_won) bc = c_borderwon;
767 else bc = c_border;
769 for (i=0; i<=boardh; i++) {
770 widget_fillrect(wid, &rect, bc);
771 rect.y += cellh + border;
774 rect.y = padding;
775 rect.w = border;
776 rect.h = cellh * boardh + (boardh + 1) * border;
777 for (i=0; i<=boardw; i++) {
778 widget_fillrect(wid, &rect, bc);
779 rect.x += cellw + border;
782 //highlight cursor
783 if (lastmousex > wid->x && lastmousey > wid->y) {
784 i = (lastmousex - padding - border - wid->x) / (cellw + border);
785 j = (lastmousey - padding - border - wid->y) / (cellh + border);
786 if (i < boardw && j < boardh) {
787 rect.x = (cellw + border) * i + padding;
788 rect.y = (cellh + border) * j + padding;
789 rect.w = cellw + 2 * border;
790 rect.h = cellh + 2 * border;
791 widget_fillrect(wid, &rect, c_highlight);
795 //draw in tiles
796 for (i=0; i<boardw; i++) {
797 for (j=0; j<boardh; j++) {
798 if (board[i][j]) {
799 draw_tile(wid, i, j);
803 //draw server
804 if (game_won) c = c_serverwon;
805 else c = c_server;
807 rect.x = padding + border + (cellw + border) * sourcex;
808 rect.y = padding + border + (cellh + border) * sourceytop;
810 rect.x += 5;
811 rect.y += 5;
812 rect.w = cellw - 10;
813 rect.h = cellh - 5;
814 widget_fillrect(wid, &rect, c);
816 rect.y = padding + border + (cellh + border) * sourceybottom;
817 widget_fillrect(wid, &rect, c);
819 //victory animation
820 if (game_won) {
821 animate_pulse(wid);
825 char* read_field(FILE *fp)
827 char *r;
828 int i;
829 char c;
831 r = (char *) malloc(1024);
832 i = 0;
834 for(;;) {
835 c = getc(fp);
837 if (feof(fp)) {
838 free(r);
839 return NULL;
842 switch(c) {
843 case ',':
844 r[i] = 0;
845 return r;
846 case '\n':
847 r[i] = 0;
848 return r;
849 default:
850 r[i] = c;
851 i++;
852 break;
857 void update_hsw()
859 int i;
860 for (i=0; i<level_max; i++) {
861 if (hstable[i]->name) {
862 label_put_text(hsw[i]->name, hstable[i]->name);
863 } else {
864 label_put_text(hsw[i]->name, "None");
866 if (hstable[i]->time != -1) {
867 char s[80];
869 sprintf(s, "%d", hstable[i]->time);
870 label_put_text(hsw[i]->time, s);
871 } else {
872 label_put_text(hsw[i]->name, "None");
877 void read_hstable()
879 FILE *fp;
880 int i;
882 for (i=0; i<level_max; i++) {
883 hstable[i]->name = NULL;
884 hstable[i]->time = -1;
887 fp = fopen(config->hsfile, "r");
888 if (!fp) return;
890 for(i=0; i<level_max; i++) {
891 char *s;
892 s = read_field(fp);
893 if (!s) goto done;
895 hstable[i]->name = s;
897 s = read_field(fp);
898 if (!s) goto done;
900 hstable[i]->time = atoi(s);
901 free(s);
904 done:
905 fclose(fp);
908 void write_hstable()
910 FILE *fp;
911 int i;
913 fp = fopen(config->hsfile, "w");
914 if (!fp) return;
916 for(i=0; i<level_max; i++) {
917 fprintf(fp, "%s,%d\n", hstable[i]->name, hstable[i]->time);
920 fclose(fp);
923 int enkludge;
925 void enter_name_open()
927 modalwindow = enter_name_window;
928 enkludge = 1;
931 void enter_name_close()
933 modalwindow = NULL;
934 enkludge = 0;
936 player_name = tb_en1->text;
938 if (hstable[level]->name) {
939 free(hstable[level]->name);
941 hstable[level]->name = clonestr(player_name);
942 hstable[level]->time = second_count;
943 update_hsw();
944 write_hstable();
947 void check_hs()
949 if (hstable[level]->time == -1 || second_count < hstable[level]->time) {
950 enter_name_open();
954 void init_tileimg()
956 int i, j;
957 SDL_PixelFormat *fmt = screen->format;
958 SDL_Rect rect;
959 int c;
961 pipex = cellw / 2 - pipet / 2;
962 pipey = cellw / 2 - pipet / 2;
963 pipew = cellw / 2 + pipet / 2;
964 pipeh = cellh / 2 + pipet / 2;
966 for (i=0; i<64; i++) {
967 tileimg[i] = SDL_CreateRGBSurface(0, cellw, cellh, fmt->BitsPerPixel,
968 fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
969 for (j=0; j<4; j++) {
970 if ((i + 1) & (1 << j)) {
971 switch (j) {
972 case 0:
973 rect.x = pipex;
974 rect.y = 0;
975 rect.w = pipet;
976 rect.h = pipeh;
977 break;
978 case 1:
979 rect.x = pipex;
980 rect.y = pipey;
981 rect.w = pipew;
982 rect.h = pipet;
983 break;
984 case 2:
985 rect.x = pipex;
986 rect.y = pipey;
987 rect.w = pipet;
988 rect.h = pipeh;
989 break;
990 case 3:
991 rect.x = 0;
992 rect.y = pipey;
993 rect.w = pipew;
994 rect.h = pipet;
995 break;
997 if (i >= 16) {
998 c = ctable[c_on];
999 } else c = ctable[c_off];
1000 SDL_FillRect(tileimg[i], &rect, c);
1005 for (i=1; i<32; i*=2) {
1006 rect.x = cellw / 2 - 2 * pipet;
1007 rect.y = cellh / 2 - 2 * pipet;
1008 rect.w = pipet * 4;
1009 rect.h = pipet * 4;
1010 rect.x++;
1011 rect.w-=2;
1012 rect.y++;
1013 rect.h-=2;
1015 SDL_FillRect(tileimg[i-1], &rect, ctable[c_off]);
1016 SDL_FillRect(tileimg[i-1+16], &rect, ctable[c_on]);
1017 rect.x++;
1018 rect.w-=2;
1019 rect.y++;
1020 rect.h-=2;
1022 SDL_FillRect(tileimg[i-1], &rect, ctable[c_down]);
1023 SDL_FillRect(tileimg[i-1+16], &rect, ctable[c_up]);
1027 void reset_move_count()
1029 move_count = 0;
1030 label_put_text(l_moves, "moves: 0");
1033 void increment_move_count()
1035 char s[80];
1036 move_count++;
1037 sprintf(s, "moves: %d", move_count);
1038 label_put_text(l_moves, s);
1041 void reset_time()
1043 label_put_text(l_time, "time: 0");
1046 void update_time()
1048 static char s[80];
1050 ms_count += tick - tick_old;
1051 while (ms_count >= 1000) {
1052 ms_count -= 1000;
1053 second_count++;
1054 sprintf(s, "time: %d", second_count);
1055 label_put_text(l_time, s);
1059 void resize()
1060 //position everything based on board size
1062 int w, h;
1064 sourcex = boardw / 2 - 1;
1065 sourceytop = boardh / 2;
1066 sourceybottom = sourceytop + 1;
1068 w = cellw * boardw + (boardw + 1) * border + 2 * padding;
1069 h = cellh * boardh + (boardh + 1) * border + 2 * padding;
1070 widget_put_geometry(arena, 0, vsize, w, h);
1071 set_video(w, h + 2 * vsize);
1072 widget_put_geometry(root, 0, 0, w, h + 2 * vsize);
1073 widget_put_geometry(menu, 0, 0, w, vsize);
1074 menubar_auto_layout(menu);
1075 widget_put_geometry(statusbar, 0, h + vsize, w, vsize);
1077 widget_put_geometry(l_moves, 8, h + vsize, 64, vsize);
1078 widget_put_geometry(l_time, w - 48, h + vsize, 64, vsize);
1080 widget_put_geometry((widget_ptr) about_window,
1081 w/2 - 50, h/2 - 50, 100, 100);
1082 widget_put_geometry((widget_ptr) l_about1, 10, 10, 60, vsize);
1083 widget_put_geometry((widget_ptr) l_about2, 10, 30, 60, vsize);
1084 widget_put_geometry((widget_ptr) b_about1, 30, 80, 30, vsize);
1086 widget_put_geometry((widget_ptr) hs_window,
1087 w/2 - 75, h/2 - 60, 150, 120);
1088 widget_put_geometry((widget_ptr) l_hs1, 10, 5, 60, vsize);
1089 widget_put_geometry((widget_ptr) b_hs1, 100, 100, 30, vsize);
1092 int i;
1093 for (i=0; i<level_max; i++) {
1094 int y = vsize * i + 8 + vsize;
1095 widget_put_geometry((widget_ptr) hsw[i]->level, 10, y, 20, vsize);
1096 widget_put_geometry((widget_ptr) hsw[i]->time, 60, y, 20, vsize);
1097 widget_put_geometry((widget_ptr) hsw[i]->name, 90, y, 20, vsize);
1101 widget_put_geometry((widget_ptr) enter_name_window,
1102 10, h/2 - 30, w - 20, 67);
1103 widget_put_geometry((widget_ptr) l_en1, 10, 0, 60, vsize);
1104 widget_put_geometry((widget_ptr) tb_en1, 5, vsize + 5, w - 30, vsize);
1105 widget_put_geometry((widget_ptr) b_en1, w - 60, 45, 30, vsize);
1107 ((widget_ptr) root)->computexy((widget_ptr) root);
1108 ((widget_ptr) about_window)->computexy((widget_ptr) about_window);
1109 ((widget_ptr) hs_window)->computexy((widget_ptr) hs_window);
1110 ((widget_ptr) enter_name_window)->computexy((widget_ptr) enter_name_window);
1113 void new_game()
1115 switch(level) {
1116 case level_easy:
1117 boardw = 5; boardh = 5;
1118 wrap_flag = 0;
1119 no_fourway = 0;
1120 break;
1121 case level_medium:
1122 boardw = 10; boardh = 9;
1123 wrap_flag = 0;
1124 no_fourway = 0;
1125 break;
1126 case level_hard:
1127 boardw = 10; boardh = 9;
1128 wrap_flag = 1;
1129 no_fourway = 1;
1130 break;
1131 case level_veryhard:
1132 boardw = 20; boardh = 18;
1133 wrap_flag = 1;
1134 no_fourway = 1;
1135 break;
1136 default:
1137 break;
1139 resize();
1140 srand(time(NULL));
1141 generate_maze();
1142 scramble();
1143 check_live();
1144 reset_move_count();
1145 reset_time();
1146 ms_count = 0;
1147 tick = SDL_GetTicks();
1148 second_count = 0;
1151 void handle_mousebuttonup(SDL_Event *event)
1153 int x = event->button.x;
1154 int y = event->button.y;
1155 if (openedmenu) {
1156 int i;
1157 menuitem_ptr it;
1158 menu_ptr m;
1160 m = openedmenu->submenu;
1161 for (i=0; i<m->item_count; i++) {
1162 it = m->item_list[i];
1163 if (in_widget((widget_ptr) it, x, y)) {
1164 widget_raise_signal((widget_ptr) it, signal_activate);
1165 break;
1168 openedmenu = NULL;
1170 return;
1171 } else if (state == state_button) {
1172 state = state_game;
1173 if (in_widget(button_selection, x, y)) {
1174 widget_raise_signal(button_selection, signal_activate);
1175 return;
1180 void handle_click(int button, int x, int y)
1182 widget_ptr wid;
1184 if (modalwindow) {
1185 wid = (widget_ptr) modalwindow;
1186 if (in_widget(wid, x, y) && (wid->handle_click)) {
1187 wid->handle_click(wid, button, x, y);
1189 return;
1192 wid = (widget_ptr) root;
1193 wid->handle_click(wid, button, x, y);
1196 void arena_handle_click(widget_ptr p, int button, int x, int y)
1198 int i, j;
1199 int d;
1201 if (state != state_game) return;
1203 i = (x - padding - border) / (cellw + border);
1204 j = (y - padding - border) / (cellh + border);
1205 if (i >= boardw || j >= boardh) return;
1207 if (game_won) {
1208 if (i == sourcex && (j == sourceytop || j == sourceybottom)) {
1209 new_pulse(sourcex, sourceybottom, -1);
1210 new_pulse(sourcex, sourceytop, -1);
1211 } else {
1212 new_pulse(i, j, -1);
1214 return;
1217 //temporarily merge server squares
1218 board[sourcex][sourceybottom] |= board[sourcex][sourceytop] & 1;
1219 if (i == sourcex && j == sourceytop) {
1220 j = sourceybottom;
1222 d = board[i][j] & 15;
1223 switch(button) {
1224 case SDL_BUTTON_LEFT:
1225 d = rotatecw(d, 3);
1226 increment_move_count();
1227 break;
1228 case SDL_BUTTON_RIGHT:
1229 d = rotatecw(d, 1);
1230 increment_move_count();
1231 break;
1233 board[i][j] &= ~15;
1234 board[i][j] += d;
1236 board[sourcex][sourceytop] &= ~1;
1237 board[sourcex][sourceytop] |= board[sourcex][sourceybottom] & 1;
1238 board[sourcex][sourceybottom] &= ~1;
1240 check_live();
1241 if (game_won) {
1242 pulse_count = 0;
1244 check_hs();
1248 void quit()
1250 state = state_quit;
1253 void quit_menu(widget_ptr w, void *data)
1255 quit();
1258 void new_game_menu(widget_ptr w, void *data)
1260 new_game();
1263 void about_open(widget_ptr w, void *data)
1265 modalwindow = about_window;
1268 void about_close(widget_ptr w, void *data)
1270 modalwindow = NULL;
1273 void hs_open(widget_ptr w, void *data)
1275 modalwindow = hs_window;
1278 void hs_close(widget_ptr w, void *data)
1280 modalwindow = NULL;
1283 void set_level(widget_ptr w, void *data)
1285 level = (int) data;
1286 new_game();
1289 void handle_key(int key, int mod)
1291 if (openedmenu) {
1292 switch(key) {
1293 case SDLK_ESCAPE:
1294 openedmenu = NULL;
1296 return;
1299 if (enkludge) {
1300 switch(key) {
1301 case SDLK_LEFT:
1302 textbox_left(tb_en1);
1303 break;
1304 case SDLK_RIGHT:
1305 textbox_right(tb_en1);
1306 break;
1307 case SDLK_DELETE:
1308 textbox_delete(tb_en1);
1309 break;
1310 case SDLK_BACKSPACE:
1311 textbox_backspace(tb_en1);
1312 break;
1313 default:
1314 if (key < 256 && key >= 32) {
1315 if (mod & KMOD_SHIFT) {
1316 textbox_insert(tb_en1, shifttable[key]);
1317 } else {
1318 textbox_insert(tb_en1, key);
1321 break;
1323 return;
1326 switch(key) {
1327 case SDLK_d:
1328 enter_name_open();
1329 break;
1330 case SDLK_ESCAPE:
1331 case SDLK_q:
1332 quit();
1333 break;
1334 case SDLK_F2:
1335 new_game();
1336 break;
1340 void update_screen()
1342 SDL_FillRect(screen, NULL, 0);
1343 widget_update((widget_ptr) root);
1344 if (openedmenu) {
1345 int i;
1346 menuitem_ptr it;
1348 for (i=0; i<menu->item_count; i++) {
1349 it = menu->item_list[i];
1350 if (in_widget((widget_ptr) it, lastmousex, lastmousey)) {
1351 open_submenu((widget_ptr) it, NULL);
1354 menu_update(openedmenu->submenu);
1356 if (modalwindow) {
1357 widget_update((widget_ptr) modalwindow);
1359 SDL_Flip(screen);
1362 void window_update(widget_ptr p)
1364 window_ptr w = (window_ptr) p;
1365 widget_ptr wid;
1366 int i;
1367 SDL_Rect dst;
1369 if (p != (widget_ptr) root) {
1370 dst.x = -1;
1371 dst.y = -1;
1372 dst.w = p->w + 2;
1373 dst.h = p->h + 2;
1374 widget_fillrect(p, &dst, c_windowborder);
1375 widget_fill(p, c_background);
1378 for (i=0; i<w->widget_count; i++) {
1379 wid = w->widget_list[i];
1380 widget_update(wid);
1384 void window_handle_click(widget_ptr p, int button, int x, int y)
1386 widget_ptr wid;
1387 window_ptr window = (window_ptr) p;
1388 int i;
1390 for (i=0; i<window->widget_count; i++) {
1391 wid = window->widget_list[i];
1392 if (in_widget(wid, x, y) && (wid->handle_click)) {
1393 wid->handle_click(wid, button, x - wid->x, y - wid->y);
1394 return;
1399 void window_add_widget(window_ptr r, void *p)
1401 widget_ptr wid = (widget_ptr) p;
1402 r->widget_list[r->widget_count] = wid;
1403 r->widget_count++;
1404 wid->parent = (widget_ptr) r;
1405 wid->x += r->widget.x;
1406 wid->y += r->widget.y;
1409 void window_computexy(widget_ptr wid)
1411 int i;
1412 window_ptr w = (window_ptr) wid;
1414 widget_computexy(wid);
1415 for (i=0; i<w->widget_count; i++) {
1416 w->widget_list[i]->computexy(w->widget_list[i]);
1420 void window_init(window_ptr w)
1422 widget_init((widget_ptr) w);
1423 w->widget_count = 0;
1424 w->widget.update = window_update;
1425 w->widget.handle_click = window_handle_click;
1426 w->widget.computexy = window_computexy;
1429 void parse_option(char *s1, char *s2)
1431 if (!strcmp(s1, "showmoves")) {
1432 show_moves_flag = atoi(s2);
1434 if (!strcmp(s1, "fontsize")) {
1435 config->fontsize = atoi(s2);
1437 if (!strcmp(s1, "font")) {
1438 config->fontname = clonestr(s2);
1440 if (!strcmp(s1, "hiscores")) {
1441 config->hsfile = clonestr(s2);
1445 int is_whitespace(char c)
1447 if (strchr(" \t\r\n", c)) return -1;
1448 return 0;
1451 void skip_whitespace(FILE *fp)
1453 for (;;) {
1454 int c;
1455 c = getc(fp);
1456 if (feof(fp)) return;
1457 if (!is_whitespace(c)) {
1458 ungetc(c, fp);
1459 break;
1464 void read_word(char *s, FILE *fp)
1466 int i = 0;
1468 skip_whitespace(fp);
1469 if (feof(fp)) return;
1471 for (;;) {
1472 int c;
1473 c = getc(fp);
1474 if (feof(fp)) return;
1475 if (is_whitespace(c)) {
1476 ungetc(c, fp);
1477 break;
1479 s[i] = c;
1480 i++;
1481 if (i >= 128 - 1) break;
1483 s[i] = 0;
1486 void read_line(char *s, FILE *fp)
1488 int i = 0;
1490 for (;;) {
1491 int c;
1492 c = getc(fp);
1493 if (feof(fp)) return;
1494 if (c == '\r') {
1495 //safest thing to do?
1496 continue;
1498 if (c == '\n') {
1499 ungetc(c, fp);
1500 break;
1502 s[i] = c;
1503 i++;
1504 if (i >= 1024 - 1) break;
1506 s[i] = 0;
1509 void read_config()
1511 FILE *fp;
1513 fp = fopen(config_file, "r");
1514 if (!fp) {
1515 fprintf(stderr,"Can't open config file %s\n", config_file);
1516 exit(1);
1519 for(;;) {
1520 int i;
1521 char s1[1024], *s2;
1523 skip_whitespace(fp);
1524 if (feof(fp)) {
1525 break;
1527 read_line(s1, fp);
1528 if (feof(fp)) {
1529 break;
1532 i = 0;
1533 for(;;) {
1534 if (!s1[i]) {
1535 s2 = &s1[i];
1536 break;
1538 if (is_whitespace(s1[i])) {
1539 s1[i] = 0;
1540 i++;
1541 for(;;) {
1542 if (!s1[i] || !is_whitespace(s1[i])) {
1543 s2 = &s1[i];
1544 break;
1547 break;
1549 i++;
1552 parse_option(s1, s2);
1555 fclose(fp);
1558 void add_shiftstring(char *s1, char *s2)
1560 int i;
1562 for (i=0; i<strlen(s1); i++) {
1563 shifttable[(int) s1[i]] = s2[i];
1567 int main(int argc, char *argv[])
1569 SDL_Event event;
1571 //setup names
1572 level_name[level_easy] = "Newbie";
1573 level_name[level_medium] = "Normal";
1574 level_name[level_hard] = "Nerd";
1575 level_name[level_veryhard] = "Nutcase";
1577 //setup shifttable
1579 int i;
1581 for (i=0; i<256; i++) shifttable[i] = i;
1583 for (i='a'; i<='z'; i++) {
1584 shifttable[i] = i - 32;
1587 add_shiftstring("1234567890-=", "!@#$%^&*()_+");
1588 add_shiftstring("[]\\;',./`", "{}|:\"<>?~");
1591 read_config();
1592 read_hstable();
1593 init();
1595 init_rgbtable();
1597 font = TTF_OpenFont(config->fontname, config->fontsize);
1598 if (!font) {
1599 fprintf(stderr, "error loading font\n");
1600 exit(1);
1603 window_init(root);
1605 //need to set video mode here to initialize colour table
1606 set_video(100, 100);
1608 //setup enter name box
1610 window_init(enter_name_window);
1612 label_init(l_en1);
1613 label_put_text(l_en1, "Enter name:");
1614 window_add_widget(enter_name_window, l_en1);
1616 textbox_init(tb_en1);
1617 textbox_put_text(tb_en1, "Anonymous");
1618 window_add_widget(enter_name_window, tb_en1);
1620 button_init(b_en1);
1621 button_put_text(b_en1, "Ok");
1622 window_add_widget(enter_name_window, b_en1);
1623 widget_put_handler((widget_ptr) b_en1, enter_name_close, signal_activate);
1626 //setup the "arena": where the action is
1628 widget_init((widget_ptr) arena);
1629 arena->widget.update = arena_update;
1630 arena->widget.handle_click = arena_handle_click;
1631 window_add_widget(root, arena);
1634 //status bar: mainly for showing the time
1636 widget_init((widget_ptr) statusbar);
1637 statusbar->update = statusbar_update;
1638 window_add_widget(root, statusbar);
1640 //setup moves and time
1641 label_init(l_moves);
1642 if (show_moves_flag) {
1643 window_add_widget(root, l_moves);
1646 label_init(l_time);
1647 window_add_widget(root, l_time);
1650 //setup the menus
1652 menuitem_t it1, it2;
1653 menuitem_ptr it;
1654 menu_t m1, m2;
1656 int i;
1658 menubar_init(menu);
1659 window_add_widget(root, menu);
1660 menuitem_init(it1);
1661 menuitem_put_text(it1, "Game");
1662 menubar_add_item(menu, it1);
1663 menuitem_init(it2);
1664 menuitem_put_text(it2, "Help");
1665 menubar_add_item(menu, it2);
1667 menu_init(m1);
1669 it = menuitem_new();
1670 menuitem_put_text(it, "New game");
1671 widget_put_handler((widget_ptr) it, new_game_menu, signal_activate);
1672 menu_add_item(m1, it);
1674 for (i=0; i<level_max; i++) {
1675 it = menuitem_new();
1676 menuitem_put_text(it, level_name[i]);
1677 widget_put_handler_data((widget_ptr) it,
1678 set_level, (void *) i, signal_activate);
1679 menu_add_item(m1, it);
1681 it = menuitem_new();
1682 menuitem_put_text(it, "High Scores");
1683 widget_put_handler((widget_ptr) it, hs_open, signal_activate);
1684 menu_add_item(m1, it);
1685 it = menuitem_new();
1686 menuitem_put_text(it, "Quit");
1687 widget_put_handler((widget_ptr) it, quit_menu, signal_activate);
1688 menu_add_item(m1, it);
1690 menuitem_set_submenu(it1, m1);
1692 menu_init(m2);
1694 it = menuitem_new();
1695 menuitem_put_text(it, "About");
1696 widget_put_handler((widget_ptr) it, about_open, signal_activate);
1697 menu_add_item(m2, it);
1699 menuitem_set_submenu(it2, m2);
1702 //setup about box
1704 window_init(about_window);
1706 label_init(l_about1);
1707 label_put_text(l_about1, "NetWalk " VERSION_STRING);
1708 window_add_widget(about_window, l_about1);
1710 label_init(l_about2);
1711 label_put_text(l_about2, "Ben Lynn");
1712 window_add_widget(about_window, l_about2);
1714 button_init(b_about1);
1715 button_put_text(b_about1, "Ok");
1716 window_add_widget(about_window, b_about1);
1717 widget_put_handler((widget_ptr) b_about1, about_close, signal_activate);
1720 //setup hiscores box
1722 int i;
1723 window_init(hs_window);
1725 label_init(l_hs1);
1726 label_put_text(l_hs1, "High Scores");
1727 window_add_widget(hs_window, l_hs1);
1729 button_init(b_hs1);
1730 button_put_text(b_hs1, "Ok");
1731 window_add_widget(hs_window, b_hs1);
1732 widget_put_handler((widget_ptr) b_hs1, hs_close, signal_activate);
1734 for (i=0; i<level_max; i++) {
1735 label_init(hsw[i]->level);
1736 label_put_text(hsw[i]->level, level_name[i]);
1737 window_add_widget(hs_window, hsw[i]->level);
1738 label_init(hsw[i]->name);
1739 window_add_widget(hs_window, hsw[i]->name);
1740 label_init(hsw[i]->time);
1741 window_add_widget(hs_window, hsw[i]->time);
1745 resize();
1747 update_hsw();
1749 init_tileimg();
1750 new_game();
1752 while (state != state_quit && !interrupted) {
1753 tick_old = tick;
1754 tick = SDL_GetTicks();
1755 if (!game_won) {
1756 update_time();
1758 SDL_GetMouseState(&lastmousex, &lastmousey);
1759 while (SDL_PollEvent(&event)) {
1760 switch (event.type) {
1761 case SDL_KEYDOWN:
1762 handle_key(event.key.keysym.sym, SDL_GetModState());
1763 break;
1764 case SDL_MOUSEBUTTONDOWN:
1765 handle_click(event.button.button, event.button.x, event.button.y);
1766 break;
1767 case SDL_MOUSEBUTTONUP:
1768 handle_mousebuttonup(&event);
1769 break;
1770 case SDL_QUIT:
1771 quit();
1772 break;
1775 update_screen();
1776 SDL_Delay(20);
1778 return 0;