Sun Jun 27 10:41:49 PDT 2004
[netwalk.git] / main.c
blob7eca62a9030a780c7bf4182ac5258123caa6873d
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_max,
70 level_custom
73 enum {
74 cellw = 24,
75 cellh = 24,
76 border = 1,
77 padding = 10
80 char shifttable[256];
82 struct hsentry_s {
83 char *name;
84 int time;
86 typedef struct hsentry_s *hsentry_ptr;
87 typedef struct hsentry_s hsentry_t[1];
89 hsentry_t hstable[level_max];
90 char *level_name[level_max];
92 //TODO use this somehow
93 struct gameparm_s {
94 int boardw, boardh;
95 int wrap;
97 typedef struct gameparm_s *gameparm_ptr;
98 typedef struct gameparm_s gameparm_t[1];
100 gameparm_t gp;
102 static config_t config;
104 SDL_Surface *screen;
105 TTF_Font *font;
106 int lastmousex, lastmousey;
107 char *player_name;
108 int game_won;
109 int state;
111 struct button_s {
112 struct widget_s widget;
113 char *text;
114 SDL_Surface *img;
116 typedef struct button_s button_t[1];
117 typedef struct button_s *button_ptr;
119 struct label_s {
120 struct widget_s widget;
121 char *text;
122 SDL_Surface *img;
124 typedef struct label_s label_t[1];
125 typedef struct label_s *label_ptr;
127 struct textbox_s {
128 struct widget_s widget;
129 char text[1024];
130 int i;
131 SDL_Surface *img;
133 typedef struct textbox_s textbox_t[1];
134 typedef struct textbox_s *textbox_ptr;
136 struct arena_s {
137 struct widget_s widget;
139 typedef struct arena_s arena_t[1];
140 typedef struct arena_s *arena_ptr;
142 struct menuitem_s {
143 struct widget_s widget;
144 char *text;
145 SDL_Surface *img;
146 struct menu_s *submenu;
148 typedef struct menuitem_s menuitem_t[1];
149 typedef struct menuitem_s *menuitem_ptr;
151 struct menu_s {
152 struct widget_s widget;
153 //TODO: replace array with list
154 menuitem_ptr item_list[64];
155 int item_count;
157 typedef struct menu_s menu_t[1];
158 typedef struct menu_s *menu_ptr;
160 struct menubar_s {
161 struct widget_s widget;
162 //TODO: replace array with list
163 menuitem_ptr item_list[64];
164 int item_count;
166 typedef struct menubar_s menubar_t[1];
167 typedef struct menubar_s *menubar_ptr;
169 struct window_s {
170 struct widget_s widget;
171 //TODO: replace array with list
172 struct widget_s *widget_list[64];
173 int widget_count;
176 typedef struct window_s window_t[1];
177 typedef struct window_s *window_ptr;
179 struct hsw_s {
180 label_t level;
181 label_t time;
182 label_t name;
184 typedef struct hsw_s *hsw_ptr;
185 typedef struct hsw_s hsw_t[1];
187 hsw_t hsw[level_max];
189 arena_t arena;
190 window_t root;
191 label_t l_moves;
192 label_t l_time;
193 menubar_t menu;
194 menuitem_ptr openedmenu;
195 window_ptr modalwindow;
196 window_t about_window;
197 window_t hs_window;
198 window_t enter_name_window;
199 label_t l_about1;
200 label_t l_about2;
201 button_t b_about1;
202 widget_t statusbar;
204 label_t l_hs1;
205 button_t b_hs1;
207 label_t l_en1;
208 textbox_t tb_en1;
209 button_t b_en1;
211 SDL_Surface *font_render(char *s, int c)
213 return TTF_RenderText_Solid(font, s, rgbtable[c]);
216 void statusbar_update(widget_ptr w)
218 widget_lowered_background(w);
221 void menu_init(menu_ptr m)
223 widget_init((widget_ptr) m);
224 m->item_count = 0;
227 void menu_update(menu_ptr m)
229 int i;
230 menuitem_ptr it;
231 SDL_Rect rect;
233 widget_fill((widget_ptr) m, c_background);
234 for (i=0; i<m->item_count; i++) {
235 it = m->item_list[i];
236 if (in_widget((widget_ptr) it, lastmousex, lastmousey)) {
237 rect.x = 2;
238 rect.y = vsize * i;
239 rect.w = ((widget_ptr) it)->w - 4;
240 rect.h = vsize;
241 widget_fillrect((widget_ptr) m, &rect, c_menubg);
243 rect.x = 8;
244 rect.y = vsize * i + 4;
245 widget_blit((widget_ptr) m, it->img, NULL, &rect);
249 void menu_add_item(menu_ptr m, menuitem_ptr it)
251 m->item_list[m->item_count] = it;
252 m->item_count++;
255 void menuitem_init(menuitem_ptr m)
257 widget_init((widget_ptr) m);
258 m->text = NULL;
259 m->img = NULL;
260 m->submenu = NULL;
263 menuitem_ptr menuitem_new()
265 menuitem_ptr it;
267 it = (menuitem_ptr) malloc(sizeof(menuitem_t));
268 menuitem_init(it);
270 return it;
273 void menuitem_put_text(menuitem_ptr m, char *s)
275 SDL_Surface *tmp;
277 m->text = s;
278 if (m->img) SDL_FreeSurface(m->img);
279 tmp = font_render(s, c_text);
280 m->img = SDL_DisplayFormat(tmp);
281 SDL_FreeSurface(tmp);
284 void open_submenu(widget_ptr p, void *data)
286 int i;
287 int w = 0;
288 menuitem_ptr it;
290 openedmenu = (menuitem_ptr) p;
291 menu_ptr m = openedmenu->submenu;
293 for (i=0; i<m->item_count; i++) {
294 it = m->item_list[i];
295 if (w < it->img->w) w = it->img->w;
297 w += 12;
299 m->widget.x = m->widget.parent->x;
300 m->widget.y = m->widget.parent->y + vsize;
302 m->widget.w = w;
303 m->widget.h = vsize * m->item_count + 1;
305 for (i=0; i<m->item_count; i++) {
306 it = m->item_list[i];
307 it->widget.x = 0 + m->widget.x;
308 it->widget.y = vsize * i + 4 + m->widget.y;
309 it->widget.w = w;
310 it->widget.h = vsize;
314 void menuitem_set_submenu(menuitem_ptr it, menu_ptr m)
316 it->submenu = m;
317 //it->widget.signal_handler[signal_activate] = open_submenu;
318 widget_put_handler((widget_ptr) it, open_submenu, signal_activate);
319 m->widget.parent = (widget_ptr) it;
322 void menubar_update(widget_ptr wid)
324 SDL_Rect dst;
325 menubar_ptr m = (menubar_ptr) wid;
326 menuitem_ptr it;
327 int i;
329 widget_raised_background(wid);
331 for (i=0; i<m->item_count; i++) {
332 it = m->item_list[i];
333 if (it == openedmenu) {
334 dst.x = it->widget.x + 2;
335 dst.y = it->widget.y + 2;
336 dst.w = it->widget.w - 4;
337 dst.h = vsize - 2;
338 widget_fillrect(wid, &dst, c_menubg);
340 if (it->img) {
341 dst.x = it->widget.x + 5;
342 dst.y = it->widget.y + 2;
343 widget_blit(m, it->img, NULL, &dst);
348 void menubar_handle_click(widget_ptr p, int button, int x, int y)
350 int i;
351 menubar_ptr m = (menubar_ptr) p;
352 menuitem_ptr it;
354 for (i=0; i<m->item_count; i++) {
355 it = m->item_list[i];
356 if (in_widget((widget_ptr) it, x, y)) {
357 widget_raise_signal((widget_ptr) it, signal_activate);
358 return;
363 void menubar_init(menubar_ptr m)
365 widget_init((widget_ptr) m);
366 m->widget.update = menubar_update;
367 m->item_count = 0;
368 m->widget.handle_click = menubar_handle_click;
371 void menubar_add_item(menubar_ptr m, menuitem_ptr it)
373 m->item_list[m->item_count] = it;
374 m->item_count++;
375 it->widget.parent = (widget_ptr) m;
378 void menubar_auto_layout(menubar_ptr m)
380 int i, x, y;
381 menuitem_ptr it;
383 x = 0;
384 y = 0;
385 for (i=0; i<m->item_count; i++) {
386 it = m->item_list[i];
387 if (it->img) {
388 it->widget.x = x;
389 it->widget.y = y;
390 it->widget.w = it->img->w + 10;
391 it->widget.h = it->img->h;
392 x += it->img->w + 10;
397 void label_update(widget_ptr p)
399 SDL_Rect dst;
400 label_ptr l = (label_ptr) p;
402 if (l->img) {
403 dst.x = 0;
404 dst.y = 4;
405 widget_blit(l, l->img, NULL, &dst);
409 void label_init(label_ptr l)
411 widget_init((widget_ptr) l);
412 l->text = NULL;
413 l->img = NULL;
414 l->widget.update = label_update;
417 void label_put_text(label_ptr l, char *s)
419 SDL_Surface *tmp;
421 if (l->img) SDL_FreeSurface(l->img);
422 if (l->text) free(l->text);
423 l->text = clonestr(s);
424 tmp = font_render(s, c_text);
425 l->img = SDL_DisplayFormat(tmp);
426 SDL_FreeSurface(tmp);
429 void textbox_update_img(textbox_ptr tb)
431 SDL_Surface *tmp;
433 if (tb->img) SDL_FreeSurface(tb->img);
434 tmp = font_render(tb->text, c_text);
435 if (tmp) {
436 tb->img = SDL_DisplayFormat(tmp);
437 SDL_FreeSurface(tmp);
438 } else {
439 tb->img = NULL;
443 void textbox_left(textbox_ptr tb)
445 if (tb->i > 0) tb->i--;
448 void textbox_right(textbox_ptr tb)
450 if (tb->i < strlen(tb->text)) tb->i++;
453 void textbox_delete(textbox_ptr tb)
455 if (tb->i > 0) {
456 tb->i--;
457 tb->text[tb->i] = 0;
458 textbox_update_img(tb);
462 void textbox_backspace(textbox_ptr tb)
464 char *s = &tb->text[tb->i];
465 if (tb->i) {
466 memmove(s - 1, s, strlen(s) + 1);
467 tb->i--;
468 textbox_update_img(tb);
472 void textbox_insert(textbox_ptr tb, char c)
474 char *s = &tb->text[tb->i];
475 memmove(s + 1, s, strlen(s) + 1);
476 tb->text[tb->i] = c;
477 tb->i++;
478 textbox_update_img(tb);
481 void textbox_update(widget_ptr p)
483 SDL_Rect dst;
484 textbox_ptr tb = (textbox_ptr) p;
486 dst.x = 0;
487 dst.y = 0;
488 dst.w = p->w;
489 dst.h = p->h;
490 widget_fillrect(p, &dst, c_text);
491 dst.x++;
492 dst.y++;
493 dst.w-=2;
494 dst.h-=2;
495 widget_fillrect(p, &dst, c_canvas);
497 if (tb->img) {
498 dst.x = 1;
499 dst.y = 3;
500 widget_blit(tb, tb->img, NULL, &dst);
504 char s[1024];
505 int w, h;
507 strncpy(s, tb->text, tb->i);
508 s[tb->i] = 0;
509 TTF_SizeText(font, s, &w, &h);
511 dst.x = w;
512 dst.y = 2;
513 dst.w = 1;
514 dst.h = vsize - 2;
515 widget_fillrect(p, &dst, c_text);
519 void textbox_init(textbox_ptr l)
521 widget_init((widget_ptr) l);
522 l->img = NULL;
523 l->widget.update = textbox_update;
526 void textbox_put_text(textbox_ptr tb, char *s)
528 strcpy(tb->text, s);
529 tb->i = strlen(s);
530 textbox_update_img(tb);
533 static widget_ptr button_selection;
535 void button_handle_click(widget_ptr p, int button, int x, int y)
537 state = state_button;
538 button_selection = p;
541 void button_update(widget_ptr p)
543 SDL_Rect dst;
544 label_ptr l = (label_ptr) p;
546 if (state == state_button && button_selection == p
547 && in_widget(p, lastmousex, lastmousey)) {
548 widget_lowered_background(p);
549 } else {
550 widget_raised_background(p);
552 if (l->img) {
553 dst.x = 5;
554 dst.y = 2;
555 widget_blit(l, l->img, NULL, &dst);
559 void button_init(button_ptr b)
561 widget_init((widget_ptr) b);
562 b->text = NULL;
563 b->img = NULL;
564 b->widget.update = button_update;
565 b->widget.handle_click = button_handle_click;
568 void button_put_text(button_ptr b, char *s)
570 SDL_Surface *tmp;
572 if (b->img) SDL_FreeSurface(b->img);
573 if (b->text) free(b->text);
574 b->text = clonestr(s);
575 tmp = font_render(s, c_text);
576 b->img = SDL_DisplayFormat(tmp);
577 SDL_FreeSurface(tmp);
580 void set_video(int w, int h)
582 int flags;
583 flags = SDL_DOUBLEBUF;
585 screen = SDL_SetVideoMode(w, h, 0, flags);
586 init_ctable(screen->format);
588 if (!screen) {
589 fprintf(stderr, "Can't set video mode: %s\n", SDL_GetError());
590 exit(1);
593 //SDL_ShowCursor(SDL_DISABLE);
596 void set_interrupted(int i)
598 interrupted = 1;
601 void init()
603 int status;
605 signal(SIGINT, set_interrupted);
606 signal(SIGTERM, set_interrupted);
608 //if (SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) {
609 if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) {
610 fprintf(stderr, "Can't init SDL: %s\n", SDL_GetError());
611 exit(1);
613 atexit(SDL_Quit);
614 status = TTF_Init();
615 if (status) {
616 fprintf(stderr, "Can't init SDL_ttf\n");
617 exit(-1);
619 atexit(TTF_Quit);
621 SDL_WM_SetCaption("NetWalk", "NetWalk");
624 SDL_Surface *tileimg[64];
625 int level = level_medium;
626 int tick;
627 int tick_old;
628 int pipey, pipex;
629 int pipew, pipeh;
630 int pipet = 4;
631 int move_count;
632 int ms_count;
633 int second_count;
635 void draw_tile(widget_ptr wid, int i, int j)
637 SDL_Rect rect;
638 int index;
640 rect.x = padding + border + i * (cellw + border);
641 rect.y = padding + border + j * (cellh + border);
643 index = board[i][j] - 1;
644 widget_blit(wid, tileimg[index], NULL, &rect);
647 typedef struct {
648 int x, y;
649 int dir;
650 int tick;
651 } pulse_s;
653 pulse_s pulse_list[pulse_max];
654 int pulse_count;
656 void new_pulse(int x, int y, int d)
658 int i, j;
660 if (pulse_count >= pulse_max) return;
662 //stop incoming server pulses
663 if (x == sourcex && (y == sourceybottom || y ==sourceytop) && d != -1) {
664 return;
667 i = board[x][y];
668 for (j=0; j<4; j++) {
669 if ((j != d) && (i & (1 << j))) {
670 pulse_list[pulse_count].x = x;
671 pulse_list[pulse_count].y = y;
672 pulse_list[pulse_count].dir = j;
673 pulse_list[pulse_count].tick = tick;
674 pulse_count++;
675 if (pulse_count >= pulse_max) return;
680 void server_pulse()
682 new_pulse(sourcex, sourceybottom, -1);
683 new_pulse(sourcex, sourceytop, -1);
686 void animate_pulse(widget_ptr wid)
688 int i, dt, d;
689 int x, y;
690 SDL_Rect rect;
691 int speed = 500;
693 if (!pulse_count) {
694 server_pulse();
697 rect.w = pipet + 2;
698 rect.h = pipet + 2;
699 i = 0;
700 while (i<pulse_count) {
701 x = pulse_list[i].x;
702 y = pulse_list[i].y;
703 d = pulse_list[i].dir;
704 dt = tick - pulse_list[i].tick;
705 if (dt > speed) {
706 pulse_count--;
707 memmove(&pulse_list[i], &pulse_list[i+1], sizeof(pulse_s) * (pulse_count - i));
709 add_dir(&x, &y, x, y, d);
710 new_pulse(x, y, (d + 2) % 4);
711 } else {
712 //wrap cases:
713 if (dir[d].x == -1 && 2 * dt > speed && !x) {
714 x += boardw;
716 if (dir[d].x == 1 && 2 * dt > speed && x == boardw - 1) {
717 x -= boardw;
719 if (dir[d].y == -1 && 2 * dt > speed && !y) {
720 y += boardh;
722 if (dir[d].y == 1 && 2 * dt > speed && y == boardh - 1) {
723 y -= boardh;
726 rect.x = x * (cellw + border) + pipex - 1;
727 rect.x += dir[d].x * (cellw + border) * dt / speed;
728 rect.x += border + padding;
730 rect.y = y * (cellh + border) + border + padding;
731 rect.y += dir[d].y * (cellh + border) * dt / speed;
732 rect.y += pipey - 1;
733 widget_fillrect(wid, &rect, c_pulse);
734 i++;
739 void arena_update(widget_ptr wid)
741 int i, j;
742 SDL_Rect rect;
743 int bc;
744 int c;
746 //draw grid
747 rect.x = padding;
748 rect.y = padding;
749 rect.w = cellw * boardw + (boardw + 1) * border;
750 rect.h = border;
752 if (game_won) bc = c_borderwon;
753 else bc = c_border;
755 for (i=0; i<=boardh; i++) {
756 widget_fillrect(wid, &rect, bc);
757 rect.y += cellh + border;
760 rect.y = padding;
761 rect.w = border;
762 rect.h = cellh * boardh + (boardh + 1) * border;
763 for (i=0; i<=boardw; i++) {
764 widget_fillrect(wid, &rect, bc);
765 rect.x += cellw + border;
768 //highlight cursor
769 if (lastmousex > wid->x && lastmousey > wid->y) {
770 i = (lastmousex - padding - border - wid->x) / (cellw + border);
771 j = (lastmousey - padding - border - wid->y) / (cellh + border);
772 if (i < boardw && j < boardh) {
773 rect.x = (cellw + border) * i + padding;
774 rect.y = (cellh + border) * j + padding;
775 rect.w = cellw + 2 * border;
776 rect.h = cellh + 2 * border;
777 widget_fillrect(wid, &rect, c_highlight);
781 //draw in tiles
782 for (i=0; i<boardw; i++) {
783 for (j=0; j<boardh; j++) {
784 if (board[i][j]) {
785 draw_tile(wid, i, j);
789 //draw server
790 if (game_won) c = c_serverwon;
791 else c = c_server;
793 rect.x = padding + border + (cellw + border) * sourcex;
794 rect.y = padding + border + (cellh + border) * sourceytop;
796 rect.x += 5;
797 rect.y += 5;
798 rect.w = cellw - 10;
799 rect.h = cellh - 5;
800 widget_fillrect(wid, &rect, c);
802 rect.y = padding + border + (cellh + border) * sourceybottom;
803 widget_fillrect(wid, &rect, c);
805 //victory animation
806 if (game_won) {
807 animate_pulse(wid);
811 char* read_field(FILE *fp)
813 char *r;
814 int i;
815 char c;
817 r = (char *) malloc(1024);
818 i = 0;
820 for(;;) {
821 c = getc(fp);
823 if (feof(fp)) {
824 free(r);
825 return NULL;
828 switch(c) {
829 case ',':
830 r[i] = 0;
831 return r;
832 case '\n':
833 r[i] = 0;
834 return r;
835 default:
836 r[i] = c;
837 i++;
838 break;
843 void update_hsw()
845 int i;
846 for (i=0; i<level_max; i++) {
847 if (hstable[i]->name) {
848 label_put_text(hsw[i]->name, hstable[i]->name);
849 } else {
850 label_put_text(hsw[i]->name, "None");
852 if (hstable[i]->time != -1) {
853 char s[80];
855 sprintf(s, "%d", hstable[i]->time);
856 label_put_text(hsw[i]->time, s);
857 } else {
858 label_put_text(hsw[i]->name, "None");
863 void read_hstable()
865 FILE *fp;
866 int i;
868 for (i=0; i<level_max; i++) {
869 hstable[i]->name = NULL;
870 hstable[i]->time = -1;
873 fp = fopen(config->hsfile, "r");
874 if (!fp) return;
876 for(i=0; i<level_max; i++) {
877 char *s;
878 s = read_field(fp);
879 if (!s) goto done;
881 hstable[i]->name = s;
883 s = read_field(fp);
884 if (!s) goto done;
886 hstable[i]->time = atoi(s);
887 free(s);
890 done:
891 fclose(fp);
894 void write_hstable()
896 FILE *fp;
897 int i;
899 fp = fopen(config->hsfile, "w");
900 if (!fp) return;
902 for(i=0; i<level_max; i++) {
903 fprintf(fp, "%s,%d\n", hstable[i]->name, hstable[i]->time);
906 fclose(fp);
909 int enkludge;
911 void enter_name_open()
913 modalwindow = enter_name_window;
914 enkludge = 1;
917 void enter_name_close()
919 modalwindow = NULL;
920 enkludge = 0;
922 player_name = tb_en1->text;
924 if (hstable[level]->name) {
925 free(hstable[level]->name);
927 hstable[level]->name = clonestr(player_name);
928 hstable[level]->time = second_count;
929 update_hsw();
930 write_hstable();
933 void check_hs()
935 if (hstable[level]->time == -1 || second_count < hstable[level]->time) {
936 enter_name_open();
940 void init_tileimg()
942 int i, j;
943 SDL_PixelFormat *fmt = screen->format;
944 SDL_Rect rect;
945 int c;
947 pipex = cellw / 2 - pipet / 2;
948 pipey = cellw / 2 - pipet / 2;
949 pipew = cellw / 2 + pipet / 2;
950 pipeh = cellh / 2 + pipet / 2;
952 for (i=0; i<64; i++) {
953 tileimg[i] = SDL_CreateRGBSurface(0, cellw, cellh, fmt->BitsPerPixel,
954 fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
955 for (j=0; j<4; j++) {
956 if ((i + 1) & (1 << j)) {
957 switch (j) {
958 case 0:
959 rect.x = pipex;
960 rect.y = 0;
961 rect.w = pipet;
962 rect.h = pipeh;
963 break;
964 case 1:
965 rect.x = pipex;
966 rect.y = pipey;
967 rect.w = pipew;
968 rect.h = pipet;
969 break;
970 case 2:
971 rect.x = pipex;
972 rect.y = pipey;
973 rect.w = pipet;
974 rect.h = pipeh;
975 break;
976 case 3:
977 rect.x = 0;
978 rect.y = pipey;
979 rect.w = pipew;
980 rect.h = pipet;
981 break;
983 if (i >= 16) {
984 c = ctable[c_on];
985 } else c = ctable[c_off];
986 SDL_FillRect(tileimg[i], &rect, c);
991 for (i=1; i<32; i*=2) {
992 rect.x = cellw / 2 - 2 * pipet;
993 rect.y = cellh / 2 - 2 * pipet;
994 rect.w = pipet * 4;
995 rect.h = pipet * 4;
996 rect.x++;
997 rect.w-=2;
998 rect.y++;
999 rect.h-=2;
1001 SDL_FillRect(tileimg[i-1], &rect, ctable[c_off]);
1002 SDL_FillRect(tileimg[i-1+16], &rect, ctable[c_on]);
1003 rect.x++;
1004 rect.w-=2;
1005 rect.y++;
1006 rect.h-=2;
1008 SDL_FillRect(tileimg[i-1], &rect, ctable[c_down]);
1009 SDL_FillRect(tileimg[i-1+16], &rect, ctable[c_up]);
1013 void reset_move_count()
1015 move_count = 0;
1016 label_put_text(l_moves, "moves: 0");
1019 void increment_move_count()
1021 char s[80];
1022 move_count++;
1023 sprintf(s, "moves: %d", move_count);
1024 label_put_text(l_moves, s);
1027 void reset_time()
1029 label_put_text(l_time, "time: 0");
1032 void update_time()
1034 static char s[80];
1036 ms_count += tick - tick_old;
1037 while (ms_count >= 1000) {
1038 ms_count -= 1000;
1039 second_count++;
1040 sprintf(s, "time: %d", second_count);
1041 label_put_text(l_time, s);
1045 void resize()
1046 //position everything based on board size
1048 int w, h;
1050 sourcex = boardw / 2 - 1;
1051 sourceytop = boardh / 2;
1052 sourceybottom = sourceytop + 1;
1054 w = cellw * boardw + (boardw + 1) * border + 2 * padding;
1055 h = cellh * boardh + (boardh + 1) * border + 2 * padding;
1056 widget_put_geometry(arena, 0, vsize, w, h);
1057 set_video(w, h + 2 * vsize);
1058 widget_put_geometry(root, 0, 0, w, h + 2 * vsize);
1059 widget_put_geometry(menu, 0, 0, w, vsize);
1060 menubar_auto_layout(menu);
1061 widget_put_geometry(statusbar, 0, h + vsize, w, vsize);
1063 widget_put_geometry(l_moves, 8, h + vsize, 64, vsize);
1064 widget_put_geometry(l_time, w - 48, h + vsize, 64, vsize);
1066 widget_put_geometry((widget_ptr) about_window,
1067 w/2 - 50, h/2 - 50, 100, 100);
1068 widget_put_geometry((widget_ptr) l_about1, 10, 10, 60, vsize);
1069 widget_put_geometry((widget_ptr) l_about2, 10, 30, 60, vsize);
1070 widget_put_geometry((widget_ptr) b_about1, 30, 80, 30, vsize);
1072 widget_put_geometry((widget_ptr) hs_window,
1073 w/2 - 75, h/2 - 60, 150, 120);
1074 widget_put_geometry((widget_ptr) l_hs1, 10, 5, 60, vsize);
1075 widget_put_geometry((widget_ptr) b_hs1, 100, 100, 30, vsize);
1078 int i;
1079 for (i=0; i<level_max; i++) {
1080 int y = vsize * i + 8 + vsize;
1081 widget_put_geometry((widget_ptr) hsw[i]->level, 10, y, 20, vsize);
1082 widget_put_geometry((widget_ptr) hsw[i]->time, 60, y, 20, vsize);
1083 widget_put_geometry((widget_ptr) hsw[i]->name, 90, y, 20, vsize);
1087 widget_put_geometry((widget_ptr) enter_name_window,
1088 10, h/2 - 30, w - 20, 67);
1089 widget_put_geometry((widget_ptr) l_en1, 10, 0, 60, vsize);
1090 widget_put_geometry((widget_ptr) tb_en1, 5, vsize + 5, w - 30, vsize);
1091 widget_put_geometry((widget_ptr) b_en1, w - 60, 45, 30, vsize);
1093 ((widget_ptr) root)->computexy((widget_ptr) root);
1094 ((widget_ptr) about_window)->computexy((widget_ptr) about_window);
1095 ((widget_ptr) hs_window)->computexy((widget_ptr) hs_window);
1096 ((widget_ptr) enter_name_window)->computexy((widget_ptr) enter_name_window);
1099 void new_game()
1101 switch(level) {
1102 case level_easy:
1103 boardw = 5; boardh = 5;
1104 wrap_flag = 0;
1105 no_fourway = 0;
1106 break;
1107 case level_medium:
1108 boardw = 10; boardh = 9;
1109 wrap_flag = 0;
1110 no_fourway = 0;
1111 break;
1112 case level_hard:
1113 boardw = 10; boardh = 9;
1114 wrap_flag = 1;
1115 no_fourway = 1;
1116 break;
1117 case level_veryhard:
1118 boardw = 20; boardh = 18;
1119 wrap_flag = 1;
1120 no_fourway = 1;
1121 break;
1122 default:
1123 break;
1125 resize();
1126 srand(time(NULL));
1127 generate_maze();
1128 scramble();
1129 check_live();
1130 reset_move_count();
1131 reset_time();
1132 ms_count = 0;
1133 tick = SDL_GetTicks();
1134 second_count = 0;
1137 void handle_mousebuttonup(SDL_Event *event)
1139 int x = event->button.x;
1140 int y = event->button.y;
1141 if (openedmenu) {
1142 int i;
1143 menuitem_ptr it;
1144 menu_ptr m;
1146 m = openedmenu->submenu;
1147 for (i=0; i<m->item_count; i++) {
1148 it = m->item_list[i];
1149 if (in_widget((widget_ptr) it, x, y)) {
1150 widget_raise_signal((widget_ptr) it, signal_activate);
1151 break;
1154 openedmenu = NULL;
1156 return;
1157 } else if (state == state_button) {
1158 state = state_game;
1159 if (in_widget(button_selection, x, y)) {
1160 widget_raise_signal(button_selection, signal_activate);
1161 return;
1166 void handle_click(int button, int x, int y)
1168 widget_ptr wid;
1170 if (modalwindow) {
1171 wid = (widget_ptr) modalwindow;
1172 if (in_widget(wid, x, y) && (wid->handle_click)) {
1173 wid->handle_click(wid, button, x, y);
1175 return;
1178 wid = (widget_ptr) root;
1179 wid->handle_click(wid, button, x, y);
1182 void arena_handle_click(widget_ptr p, int button, int x, int y)
1184 int i, j;
1185 int d;
1187 if (state != state_game) return;
1189 i = (x - padding - border) / (cellw + border);
1190 j = (y - padding - border) / (cellh + border);
1191 if (i >= boardw || j >= boardh) return;
1193 if (game_won) {
1194 if (i == sourcex && (j == sourceytop || j == sourceybottom)) {
1195 new_pulse(sourcex, sourceybottom, -1);
1196 new_pulse(sourcex, sourceytop, -1);
1197 } else {
1198 new_pulse(i, j, -1);
1200 return;
1203 //temporarily merge server squares
1204 board[sourcex][sourceybottom] |= board[sourcex][sourceytop] & 1;
1205 if (i == sourcex && j == sourceytop) {
1206 j = sourceybottom;
1208 d = board[i][j] & 15;
1209 switch(button) {
1210 case SDL_BUTTON_LEFT:
1211 d = rotatecw(d, 3);
1212 increment_move_count();
1213 break;
1214 case SDL_BUTTON_RIGHT:
1215 d = rotatecw(d, 1);
1216 increment_move_count();
1217 break;
1219 board[i][j] &= ~15;
1220 board[i][j] += d;
1222 board[sourcex][sourceytop] &= ~1;
1223 board[sourcex][sourceytop] |= board[sourcex][sourceybottom] & 1;
1224 board[sourcex][sourceybottom] &= ~1;
1226 check_live();
1227 if (game_won) {
1228 pulse_count = 0;
1230 check_hs();
1234 void quit()
1236 state = state_quit;
1239 void quit_menu(widget_ptr w, void *data)
1241 quit();
1244 void new_game_menu(widget_ptr w, void *data)
1246 new_game();
1249 void about_open(widget_ptr w, void *data)
1251 modalwindow = about_window;
1254 void about_close(widget_ptr w, void *data)
1256 modalwindow = NULL;
1259 void hs_open(widget_ptr w, void *data)
1261 modalwindow = hs_window;
1264 void hs_close(widget_ptr w, void *data)
1266 modalwindow = NULL;
1269 void set_level(widget_ptr w, void *data)
1271 level = (int) data;
1272 new_game();
1275 void handle_key(int key, int mod)
1277 if (openedmenu) {
1278 switch(key) {
1279 case SDLK_ESCAPE:
1280 openedmenu = NULL;
1282 return;
1285 if (enkludge) {
1286 switch(key) {
1287 case SDLK_LEFT:
1288 textbox_left(tb_en1);
1289 break;
1290 case SDLK_RIGHT:
1291 textbox_right(tb_en1);
1292 break;
1293 case SDLK_DELETE:
1294 textbox_delete(tb_en1);
1295 break;
1296 case SDLK_BACKSPACE:
1297 textbox_backspace(tb_en1);
1298 break;
1299 default:
1300 if (key < 256 && key >= 32) {
1301 if (mod & KMOD_SHIFT) {
1302 textbox_insert(tb_en1, shifttable[key]);
1303 } else {
1304 textbox_insert(tb_en1, key);
1307 break;
1309 return;
1312 switch(key) {
1313 case SDLK_d:
1314 enter_name_open();
1315 break;
1316 case SDLK_ESCAPE:
1317 case SDLK_q:
1318 quit();
1319 break;
1320 case SDLK_F2:
1321 new_game();
1322 break;
1326 void update_screen()
1328 SDL_FillRect(screen, NULL, 0);
1329 widget_update((widget_ptr) root);
1330 if (openedmenu) {
1331 int i;
1332 menuitem_ptr it;
1334 for (i=0; i<menu->item_count; i++) {
1335 it = menu->item_list[i];
1336 if (in_widget((widget_ptr) it, lastmousex, lastmousey)) {
1337 open_submenu((widget_ptr) it, NULL);
1340 menu_update(openedmenu->submenu);
1342 if (modalwindow) {
1343 widget_update((widget_ptr) modalwindow);
1345 SDL_Flip(screen);
1348 void window_update(widget_ptr p)
1350 window_ptr w = (window_ptr) p;
1351 widget_ptr wid;
1352 int i;
1353 SDL_Rect dst;
1355 if (p != (widget_ptr) root) {
1356 dst.x = -1;
1357 dst.y = -1;
1358 dst.w = p->w + 2;
1359 dst.h = p->h + 2;
1360 widget_fillrect(p, &dst, c_windowborder);
1361 widget_fill(p, c_background);
1364 for (i=0; i<w->widget_count; i++) {
1365 wid = w->widget_list[i];
1366 widget_update(wid);
1370 void window_handle_click(widget_ptr p, int button, int x, int y)
1372 widget_ptr wid;
1373 window_ptr window = (window_ptr) p;
1374 int i;
1376 for (i=0; i<window->widget_count; i++) {
1377 wid = window->widget_list[i];
1378 if (in_widget(wid, x, y) && (wid->handle_click)) {
1379 wid->handle_click(wid, button, x - wid->x, y - wid->y);
1380 return;
1385 void window_add_widget(window_ptr r, void *p)
1387 widget_ptr wid = (widget_ptr) p;
1388 r->widget_list[r->widget_count] = wid;
1389 r->widget_count++;
1390 wid->parent = (widget_ptr) r;
1391 wid->x += r->widget.x;
1392 wid->y += r->widget.y;
1395 void window_computexy(widget_ptr wid)
1397 int i;
1398 window_ptr w = (window_ptr) wid;
1400 widget_computexy(wid);
1401 for (i=0; i<w->widget_count; i++) {
1402 w->widget_list[i]->computexy(w->widget_list[i]);
1406 void window_init(window_ptr w)
1408 widget_init((widget_ptr) w);
1409 w->widget_count = 0;
1410 w->widget.update = window_update;
1411 w->widget.handle_click = window_handle_click;
1412 w->widget.computexy = window_computexy;
1415 static void add_shiftstring(char *s1, char *s2)
1417 int i;
1419 for (i=0; i<strlen(s1); i++) {
1420 shifttable[(int) s1[i]] = s2[i];
1424 int main(int argc, char *argv[])
1426 SDL_Event event;
1428 //setup names
1429 level_name[level_easy] = "Newbie";
1430 level_name[level_medium] = "Normal";
1431 level_name[level_hard] = "Nerd";
1432 level_name[level_veryhard] = "Nutcase";
1434 //setup shifttable
1436 int i;
1438 for (i=0; i<256; i++) shifttable[i] = i;
1440 for (i='a'; i<='z'; i++) {
1441 shifttable[i] = i - 32;
1444 add_shiftstring("1234567890-=", "!@#$%^&*()_+");
1445 add_shiftstring("[]\\;',./`", "{}|:\"<>?~");
1448 config_load(config);
1449 read_hstable();
1450 init();
1452 init_rgbtable();
1454 font = TTF_OpenFont(config->fontname, config->fontsize);
1455 if (!font) {
1456 fprintf(stderr, "error loading font %s\n", config->fontname);
1457 exit(1);
1460 window_init(root);
1462 //need to set video mode here to initialize colour table
1463 set_video(100, 100);
1465 //setup enter name box
1467 window_init(enter_name_window);
1469 label_init(l_en1);
1470 label_put_text(l_en1, "Enter name:");
1471 window_add_widget(enter_name_window, l_en1);
1473 textbox_init(tb_en1);
1474 textbox_put_text(tb_en1, "Anonymous");
1475 window_add_widget(enter_name_window, tb_en1);
1477 button_init(b_en1);
1478 button_put_text(b_en1, "Ok");
1479 window_add_widget(enter_name_window, b_en1);
1480 widget_put_handler((widget_ptr) b_en1, enter_name_close, signal_activate);
1483 //setup the "arena": where the action is
1485 widget_init((widget_ptr) arena);
1486 arena->widget.update = arena_update;
1487 arena->widget.handle_click = arena_handle_click;
1488 window_add_widget(root, arena);
1491 //status bar: mainly for showing the time
1493 widget_init((widget_ptr) statusbar);
1494 statusbar->update = statusbar_update;
1495 window_add_widget(root, statusbar);
1497 //setup moves and time
1498 label_init(l_moves);
1499 if (config->showmoves) {
1500 window_add_widget(root, l_moves);
1503 label_init(l_time);
1504 window_add_widget(root, l_time);
1507 //setup the menus
1509 menuitem_t it1, it2;
1510 menuitem_ptr it;
1511 menu_t m1, m2;
1513 int i;
1515 menubar_init(menu);
1516 window_add_widget(root, menu);
1517 menuitem_init(it1);
1518 menuitem_put_text(it1, "Game");
1519 menubar_add_item(menu, it1);
1520 menuitem_init(it2);
1521 menuitem_put_text(it2, "Help");
1522 menubar_add_item(menu, it2);
1524 menu_init(m1);
1526 it = menuitem_new();
1527 menuitem_put_text(it, "New game");
1528 widget_put_handler((widget_ptr) it, new_game_menu, signal_activate);
1529 menu_add_item(m1, it);
1531 for (i=0; i<level_max; i++) {
1532 it = menuitem_new();
1533 menuitem_put_text(it, level_name[i]);
1534 widget_put_handler_data((widget_ptr) it,
1535 set_level, (void *) i, signal_activate);
1536 menu_add_item(m1, it);
1538 it = menuitem_new();
1539 menuitem_put_text(it, "High Scores");
1540 widget_put_handler((widget_ptr) it, hs_open, signal_activate);
1541 menu_add_item(m1, it);
1542 it = menuitem_new();
1543 menuitem_put_text(it, "Quit");
1544 widget_put_handler((widget_ptr) it, quit_menu, signal_activate);
1545 menu_add_item(m1, it);
1547 menuitem_set_submenu(it1, m1);
1549 menu_init(m2);
1551 it = menuitem_new();
1552 menuitem_put_text(it, "About");
1553 widget_put_handler((widget_ptr) it, about_open, signal_activate);
1554 menu_add_item(m2, it);
1556 menuitem_set_submenu(it2, m2);
1559 //setup about box
1561 window_init(about_window);
1563 label_init(l_about1);
1564 label_put_text(l_about1, "NetWalk " VERSION_STRING);
1565 window_add_widget(about_window, l_about1);
1567 label_init(l_about2);
1568 label_put_text(l_about2, "Ben Lynn");
1569 window_add_widget(about_window, l_about2);
1571 button_init(b_about1);
1572 button_put_text(b_about1, "Ok");
1573 window_add_widget(about_window, b_about1);
1574 widget_put_handler((widget_ptr) b_about1, about_close, signal_activate);
1577 //setup hiscores box
1579 int i;
1580 window_init(hs_window);
1582 label_init(l_hs1);
1583 label_put_text(l_hs1, "High Scores");
1584 window_add_widget(hs_window, l_hs1);
1586 button_init(b_hs1);
1587 button_put_text(b_hs1, "Ok");
1588 window_add_widget(hs_window, b_hs1);
1589 widget_put_handler((widget_ptr) b_hs1, hs_close, signal_activate);
1591 for (i=0; i<level_max; i++) {
1592 label_init(hsw[i]->level);
1593 label_put_text(hsw[i]->level, level_name[i]);
1594 window_add_widget(hs_window, hsw[i]->level);
1595 label_init(hsw[i]->name);
1596 window_add_widget(hs_window, hsw[i]->name);
1597 label_init(hsw[i]->time);
1598 window_add_widget(hs_window, hsw[i]->time);
1602 resize();
1604 update_hsw();
1606 init_tileimg();
1607 new_game();
1609 while (state != state_quit && !interrupted) {
1610 tick_old = tick;
1611 tick = SDL_GetTicks();
1612 if (!game_won) {
1613 update_time();
1615 SDL_GetMouseState(&lastmousex, &lastmousey);
1616 while (SDL_PollEvent(&event)) {
1617 switch (event.type) {
1618 case SDL_KEYDOWN:
1619 handle_key(event.key.keysym.sym, SDL_GetModState());
1620 break;
1621 case SDL_MOUSEBUTTONDOWN:
1622 handle_click(event.button.button, event.button.x, event.button.y);
1623 break;
1624 case SDL_MOUSEBUTTONUP:
1625 handle_mousebuttonup(&event);
1626 break;
1627 case SDL_QUIT:
1628 quit();
1629 break;
1632 update_screen();
1633 SDL_Delay(20);
1635 return 0;