v0.4.11
[netwalk.git] / main.c
blobf2a351035d0eb8176484ff863de2c463ed67b13a
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 //TODO use this somehow
94 struct gameparm_s {
95 int boardw, boardh;
96 int wrap;
98 typedef struct gameparm_s *gameparm_ptr;
99 typedef struct gameparm_s gameparm_t[1];
101 gameparm_t gp;
103 static config_t config;
105 SDL_Surface *screen;
106 TTF_Font *font;
107 int lastmousex, lastmousey;
108 char *player_name;
109 int game_won;
110 int state;
112 struct button_s {
113 struct widget_s widget;
114 char *text;
115 SDL_Surface *img;
117 typedef struct button_s button_t[1];
118 typedef struct button_s *button_ptr;
120 struct label_s {
121 struct widget_s widget;
122 char *text;
123 SDL_Surface *img;
125 typedef struct label_s label_t[1];
126 typedef struct label_s *label_ptr;
128 struct textbox_s {
129 struct widget_s widget;
130 char text[1024];
131 int i;
132 SDL_Surface *img;
134 typedef struct textbox_s textbox_t[1];
135 typedef struct textbox_s *textbox_ptr;
137 struct arena_s {
138 struct widget_s widget;
140 typedef struct arena_s arena_t[1];
141 typedef struct arena_s *arena_ptr;
143 struct menuitem_s {
144 struct widget_s widget;
145 char *text;
146 SDL_Surface *img;
147 struct menu_s *submenu;
149 typedef struct menuitem_s menuitem_t[1];
150 typedef struct menuitem_s *menuitem_ptr;
152 struct menu_s {
153 struct widget_s widget;
154 //TODO: replace array with list
155 menuitem_ptr item_list[64];
156 int item_count;
158 typedef struct menu_s menu_t[1];
159 typedef struct menu_s *menu_ptr;
161 struct menubar_s {
162 struct widget_s widget;
163 //TODO: replace array with list
164 menuitem_ptr item_list[64];
165 int item_count;
167 typedef struct menubar_s menubar_t[1];
168 typedef struct menubar_s *menubar_ptr;
170 struct window_s {
171 struct widget_s widget;
172 //TODO: replace array with list
173 struct widget_s *widget_list[64];
174 int widget_count;
177 typedef struct window_s window_t[1];
178 typedef struct window_s *window_ptr;
180 struct hsw_s {
181 label_t level;
182 label_t time;
183 label_t name;
185 typedef struct hsw_s *hsw_ptr;
186 typedef struct hsw_s hsw_t[1];
188 hsw_t hsw[level_max];
190 arena_t arena;
191 window_t root;
192 label_t l_moves;
193 label_t l_time;
194 menubar_t menu;
195 menuitem_ptr openedmenu;
196 window_ptr modalwindow;
197 window_t about_window;
198 window_t hs_window;
199 window_t enter_name_window;
200 label_t l_about1;
201 label_t l_about2;
202 button_t b_about1;
203 widget_t statusbar;
205 label_t l_hs1;
206 button_t b_hs1;
208 label_t l_en1;
209 textbox_t tb_en1;
210 button_t b_en1;
212 SDL_Surface *font_render(char *s, int c)
214 return TTF_RenderText_Solid(font, s, rgbtable[c]);
217 void statusbar_update(widget_ptr w)
219 widget_lowered_background(w);
222 void menu_init(menu_ptr m)
224 widget_init((widget_ptr) m);
225 m->item_count = 0;
228 void menu_update(menu_ptr m)
230 int i;
231 menuitem_ptr it;
232 SDL_Rect rect;
234 widget_fill((widget_ptr) m, c_background);
235 for (i=0; i<m->item_count; i++) {
236 it = m->item_list[i];
237 if (in_widget((widget_ptr) it, lastmousex, lastmousey)) {
238 rect.x = 2;
239 rect.y = vsize * i;
240 rect.w = ((widget_ptr) it)->w - 4;
241 rect.h = vsize;
242 widget_fillrect((widget_ptr) m, &rect, c_menubg);
244 rect.x = 8;
245 rect.y = vsize * i + 4;
246 widget_blit((widget_ptr) m, it->img, NULL, &rect);
250 void menu_add_item(menu_ptr m, menuitem_ptr it)
252 m->item_list[m->item_count] = it;
253 m->item_count++;
256 void menuitem_init(menuitem_ptr m)
258 widget_init((widget_ptr) m);
259 m->text = NULL;
260 m->img = NULL;
261 m->submenu = NULL;
264 menuitem_ptr menuitem_new()
266 menuitem_ptr it;
268 it = (menuitem_ptr) malloc(sizeof(menuitem_t));
269 menuitem_init(it);
271 return it;
274 void menuitem_put_text(menuitem_ptr m, char *s)
276 SDL_Surface *tmp;
278 m->text = s;
279 if (m->img) SDL_FreeSurface(m->img);
280 tmp = font_render(s, c_text);
281 m->img = SDL_DisplayFormat(tmp);
282 SDL_FreeSurface(tmp);
285 void open_submenu(widget_ptr p, void *data)
287 int i;
288 int w = 0;
289 menuitem_ptr it;
291 openedmenu = (menuitem_ptr) p;
292 menu_ptr m = openedmenu->submenu;
294 for (i=0; i<m->item_count; i++) {
295 it = m->item_list[i];
296 if (w < it->img->w) w = it->img->w;
298 w += 12;
300 m->widget.x = m->widget.parent->x;
301 m->widget.y = m->widget.parent->y + vsize;
303 m->widget.w = w;
304 m->widget.h = vsize * m->item_count + 1;
306 for (i=0; i<m->item_count; i++) {
307 it = m->item_list[i];
308 it->widget.x = 0 + m->widget.x;
309 it->widget.y = vsize * i + 4 + m->widget.y;
310 it->widget.w = w;
311 it->widget.h = vsize;
315 void menuitem_set_submenu(menuitem_ptr it, menu_ptr m)
317 it->submenu = m;
318 //it->widget.signal_handler[signal_activate] = open_submenu;
319 widget_put_handler((widget_ptr) it, open_submenu, signal_activate);
320 m->widget.parent = (widget_ptr) it;
323 void menubar_update(widget_ptr wid)
325 SDL_Rect dst;
326 menubar_ptr m = (menubar_ptr) wid;
327 menuitem_ptr it;
328 int i;
330 widget_raised_background(wid);
332 for (i=0; i<m->item_count; i++) {
333 it = m->item_list[i];
334 if (it == openedmenu) {
335 dst.x = it->widget.x + 2;
336 dst.y = it->widget.y + 2;
337 dst.w = it->widget.w - 4;
338 dst.h = vsize - 2;
339 widget_fillrect(wid, &dst, c_menubg);
341 if (it->img) {
342 dst.x = it->widget.x + 5;
343 dst.y = it->widget.y + 2;
344 widget_blit(m, it->img, NULL, &dst);
349 void menubar_handle_click(widget_ptr p, int button, int x, int y)
351 int i;
352 menubar_ptr m = (menubar_ptr) p;
353 menuitem_ptr it;
355 for (i=0; i<m->item_count; i++) {
356 it = m->item_list[i];
357 if (in_widget((widget_ptr) it, x, y)) {
358 widget_raise_signal((widget_ptr) it, signal_activate);
359 return;
364 void menubar_init(menubar_ptr m)
366 widget_init((widget_ptr) m);
367 m->widget.update = menubar_update;
368 m->item_count = 0;
369 m->widget.handle_click = menubar_handle_click;
372 void menubar_add_item(menubar_ptr m, menuitem_ptr it)
374 m->item_list[m->item_count] = it;
375 m->item_count++;
376 it->widget.parent = (widget_ptr) m;
379 void menubar_auto_layout(menubar_ptr m)
381 int i, x, y;
382 menuitem_ptr it;
384 x = 0;
385 y = 0;
386 for (i=0; i<m->item_count; i++) {
387 it = m->item_list[i];
388 if (it->img) {
389 it->widget.x = x;
390 it->widget.y = y;
391 it->widget.w = it->img->w + 10;
392 it->widget.h = it->img->h;
393 x += it->img->w + 10;
398 void label_update(widget_ptr p)
400 SDL_Rect dst;
401 label_ptr l = (label_ptr) p;
403 if (l->img) {
404 dst.x = 0;
405 dst.y = 4;
406 widget_blit(l, l->img, NULL, &dst);
410 void label_init(label_ptr l)
412 widget_init((widget_ptr) l);
413 l->text = NULL;
414 l->img = NULL;
415 l->widget.update = label_update;
418 void label_put_text(label_ptr l, char *s)
420 SDL_Surface *tmp;
422 if (l->img) SDL_FreeSurface(l->img);
423 if (l->text) free(l->text);
424 l->text = clonestr(s);
425 tmp = font_render(s, c_text);
426 l->img = SDL_DisplayFormat(tmp);
427 SDL_FreeSurface(tmp);
430 void textbox_update_img(textbox_ptr tb)
432 SDL_Surface *tmp;
434 if (tb->img) SDL_FreeSurface(tb->img);
435 tmp = font_render(tb->text, c_text);
436 if (tmp) {
437 tb->img = SDL_DisplayFormat(tmp);
438 SDL_FreeSurface(tmp);
439 } else {
440 tb->img = NULL;
444 void textbox_left(textbox_ptr tb)
446 if (tb->i > 0) tb->i--;
449 void textbox_right(textbox_ptr tb)
451 if (tb->i < strlen(tb->text)) tb->i++;
454 void textbox_delete(textbox_ptr tb)
456 if (tb->i > 0) {
457 tb->i--;
458 tb->text[tb->i] = 0;
459 textbox_update_img(tb);
463 void textbox_backspace(textbox_ptr tb)
465 char *s = &tb->text[tb->i];
466 if (tb->i) {
467 memmove(s - 1, s, strlen(s) + 1);
468 tb->i--;
469 textbox_update_img(tb);
473 void textbox_insert(textbox_ptr tb, char c)
475 char *s = &tb->text[tb->i];
476 memmove(s + 1, s, strlen(s) + 1);
477 tb->text[tb->i] = c;
478 tb->i++;
479 textbox_update_img(tb);
482 void textbox_update(widget_ptr p)
484 SDL_Rect dst;
485 textbox_ptr tb = (textbox_ptr) p;
487 dst.x = 0;
488 dst.y = 0;
489 dst.w = p->w;
490 dst.h = p->h;
491 widget_fillrect(p, &dst, c_text);
492 dst.x++;
493 dst.y++;
494 dst.w-=2;
495 dst.h-=2;
496 widget_fillrect(p, &dst, c_canvas);
498 if (tb->img) {
499 dst.x = 1;
500 dst.y = 3;
501 widget_blit(tb, tb->img, NULL, &dst);
505 char s[1024];
506 int w, h;
508 strncpy(s, tb->text, tb->i);
509 s[tb->i] = 0;
510 TTF_SizeText(font, s, &w, &h);
512 dst.x = w;
513 dst.y = 2;
514 dst.w = 1;
515 dst.h = vsize - 2;
516 widget_fillrect(p, &dst, c_text);
520 void textbox_init(textbox_ptr l)
522 widget_init((widget_ptr) l);
523 l->img = NULL;
524 l->widget.update = textbox_update;
527 void textbox_put_text(textbox_ptr tb, char *s)
529 strcpy(tb->text, s);
530 tb->i = strlen(s);
531 textbox_update_img(tb);
534 static widget_ptr button_selection;
536 void button_handle_click(widget_ptr p, int button, int x, int y)
538 state = state_button;
539 button_selection = p;
542 void button_update(widget_ptr p)
544 SDL_Rect dst;
545 label_ptr l = (label_ptr) p;
547 if (state == state_button && button_selection == p
548 && in_widget(p, lastmousex, lastmousey)) {
549 widget_lowered_background(p);
550 } else {
551 widget_raised_background(p);
553 if (l->img) {
554 dst.x = 5;
555 dst.y = 2;
556 widget_blit(l, l->img, NULL, &dst);
560 void button_init(button_ptr b)
562 widget_init((widget_ptr) b);
563 b->text = NULL;
564 b->img = NULL;
565 b->widget.update = button_update;
566 b->widget.handle_click = button_handle_click;
569 void button_put_text(button_ptr b, char *s)
571 SDL_Surface *tmp;
573 if (b->img) SDL_FreeSurface(b->img);
574 if (b->text) free(b->text);
575 b->text = clonestr(s);
576 tmp = font_render(s, c_text);
577 b->img = SDL_DisplayFormat(tmp);
578 SDL_FreeSurface(tmp);
581 void set_video(int w, int h)
583 int flags;
584 flags = SDL_DOUBLEBUF;
586 screen = SDL_SetVideoMode(w, h, 0, flags);
587 // flags = SDL_FULLSCREEN;
588 // screen = SDL_SetVideoMode(0, 0, 0, flags);
589 init_ctable(screen->format);
591 if (!screen) {
592 fprintf(stderr, "Can't set video mode: %s\n", SDL_GetError());
593 exit(1);
596 //SDL_ShowCursor(SDL_DISABLE);
599 void set_interrupted(int i)
601 interrupted = 1;
604 void init()
606 int status;
608 signal(SIGINT, set_interrupted);
609 signal(SIGTERM, set_interrupted);
611 //if (SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) {
612 if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) {
613 fprintf(stderr, "Can't init SDL: %s\n", SDL_GetError());
614 exit(1);
616 atexit(SDL_Quit);
617 status = TTF_Init();
618 if (status) {
619 fprintf(stderr, "Can't init SDL_ttf\n");
620 exit(-1);
622 atexit(TTF_Quit);
624 SDL_WM_SetCaption("NetWalk", "NetWalk");
626 SDL_EnableKeyRepeat(150, 50);
629 SDL_Surface *unmarked_tileimg[64];
630 SDL_Surface *marked_tileimg[64];
631 int level = level_medium;
632 int tick;
633 int tick_old;
634 int pipey, pipex;
635 int pipew, pipeh;
636 int pipet = 4;
637 int move_count;
638 int ms_count;
639 int second_count;
641 void draw_tile(widget_ptr wid, int i, int j)
643 SDL_Rect rect;
644 int index;
646 rect.x = padding + border + i * (cellw + border);
647 rect.y = padding + border + j * (cellh + border);
649 int const marked = flags[i][j] & 0x1;
650 index = board[i][j] - 1;
651 widget_blit(wid,
652 (marked?marked_tileimg:unmarked_tileimg)[index],
653 NULL,
654 &rect);
657 typedef struct {
658 int x, y;
659 int dir;
660 int tick;
661 } pulse_s;
663 pulse_s pulse_list[pulse_max];
664 int pulse_count;
666 void new_pulse(int x, int y, int d)
668 int i, j;
670 if (pulse_count >= pulse_max) return;
672 //stop incoming server pulses
673 if (x == sourcex && (y == sourceybottom || y ==sourceytop) && d != -1) {
674 return;
677 i = board[x][y];
678 for (j=0; j<4; j++) {
679 if ((j != d) && (i & (1 << j))) {
680 pulse_list[pulse_count].x = x;
681 pulse_list[pulse_count].y = y;
682 pulse_list[pulse_count].dir = j;
683 pulse_list[pulse_count].tick = tick;
684 pulse_count++;
685 if (pulse_count >= pulse_max) return;
690 void server_pulse()
692 new_pulse(sourcex, sourceybottom, -1);
693 new_pulse(sourcex, sourceytop, -1);
696 void animate_pulse(widget_ptr wid)
698 int i, dt, d;
699 int x, y;
700 SDL_Rect rect;
701 int speed = 500;
703 if (!pulse_count) {
704 server_pulse();
707 rect.w = pipet + 2;
708 rect.h = pipet + 2;
709 i = 0;
710 while (i<pulse_count) {
711 x = pulse_list[i].x;
712 y = pulse_list[i].y;
713 d = pulse_list[i].dir;
714 dt = tick - pulse_list[i].tick;
715 if (dt > speed) {
716 pulse_count--;
717 memmove(&pulse_list[i], &pulse_list[i+1], sizeof(pulse_s) * (pulse_count - i));
719 add_dir(&x, &y, x, y, d);
720 new_pulse(x, y, (d + 2) % 4);
721 } else {
722 //wrap cases:
723 if (dir[d].x == -1 && 2 * dt > speed && !x) {
724 x += boardw;
726 if (dir[d].x == 1 && 2 * dt > speed && x == boardw - 1) {
727 x -= boardw;
729 if (dir[d].y == -1 && 2 * dt > speed && !y) {
730 y += boardh;
732 if (dir[d].y == 1 && 2 * dt > speed && y == boardh - 1) {
733 y -= boardh;
736 rect.x = x * (cellw + border) + pipex - 1;
737 rect.x += dir[d].x * (cellw + border) * dt / speed;
738 rect.x += border + padding;
740 rect.y = y * (cellh + border) + border + padding;
741 rect.y += dir[d].y * (cellh + border) * dt / speed;
742 rect.y += pipey - 1;
743 widget_fillrect(wid, &rect, c_pulse);
744 i++;
749 /** \brief Determine which cell of the arena the mouse is currently
750 * pointing at, if any.
751 * \param wid The arena widget.
752 * \param mousex The mouse X position.
753 * \param mousey The mouse Y position.
754 * \param[out] col_p Returns the current cell's column in the arena.
755 * \param[out] row_p Returns the current cell's row in the arena.
756 * \retval 0 The mouse is over the arena and the cell index is returned
757 * through \a row_p and \a col_p.
758 * \retval -1 The mouse is not over the area.
761 int get_cell_from_mouse_position(widget_ptr const wid,
762 int const mousex,int const mousey,
763 unsigned int * const col_p,unsigned int * const row_p)
765 if((mousex < wid->x) || (mousey < wid->y))
766 return -1;
768 unsigned int const col =
769 (mousex - padding - border - wid->x) / (cellw + border);
770 unsigned int const row =
771 (mousey - padding - border - wid->y) / (cellh + border);
773 if((col >= boardw) || (row >= boardh))
774 return -1;
776 *col_p = col;
777 *row_p = row;
778 return 0;
781 /** \brief Fill a cell (including border) in the arena with a color.
782 * \param wid The arena widget.
783 * \param col The column of the cell to be filled.
784 * \param row The row of the cell to be filled.
785 * \param coloridx The index of the color to use when filling.
788 void fill_arena_cell(widget_ptr const wid,
789 unsigned int const col,unsigned int const row,int const coloridx)
791 SDL_Rect rect = {
792 .x = (cellw + border) * col + padding,
793 .y = (cellh + border) * row + padding,
794 .w = cellw + 2 * border,
795 .h = cellh + 2 * border
797 widget_fillrect(wid, &rect, coloridx);
800 void arena_update(widget_ptr wid)
802 int i, j;
803 SDL_Rect rect;
804 int bc;
805 int c;
807 //draw grid
808 rect.x = padding;
809 rect.y = padding;
810 rect.w = cellw * boardw + (boardw + 1) * border;
811 rect.h = border;
813 if (game_won) bc = c_borderwon;
814 else bc = c_border;
816 for (i=0; i<=boardh; i++) {
817 widget_fillrect(wid, &rect, bc);
818 rect.y += cellh + border;
821 rect.y = padding;
822 rect.w = border;
823 rect.h = cellh * boardh + (boardh + 1) * border;
824 for (i=0; i<=boardw; i++) {
825 widget_fillrect(wid, &rect, bc);
826 rect.x += cellw + border;
829 //highlight cursor
830 unsigned int col;
831 unsigned int row;
832 if(get_cell_from_mouse_position(wid,lastmousex,lastmousey,&col,&row) == 0)
834 /* highlight the cell the mouse is pointing at */
835 fill_arena_cell(wid,col,row,c_highlight);
837 if(wrap_flag)
840 * If the highlighted cell is an edge cell, also
841 * highlight the corresponding cells on opposite
842 * edges. This will make it easier to work with large
843 * wrapped grids.
845 if(col == 0)
846 fill_arena_cell(wid,boardw - 1,row,c_edgematch);
847 else
848 if(col == boardw - 1)
849 fill_arena_cell(wid,0,row,c_edgematch);
851 if(row == 0)
852 fill_arena_cell(wid,col,boardh - 1,c_edgematch);
853 else
854 if(row == boardh - 1)
855 fill_arena_cell(wid,col,0,c_edgematch);
859 //draw in tiles
860 for (i=0; i<boardw; i++) {
861 for (j=0; j<boardh; j++) {
862 if (board[i][j]) {
863 draw_tile(wid, i, j);
867 //draw server
868 if (game_won) c = c_serverwon;
869 else c = c_server;
871 rect.x = padding + border + (cellw + border) * sourcex;
872 rect.y = padding + border + (cellh + border) * sourceytop;
874 rect.x += 5;
875 rect.y += 5;
876 rect.w = cellw - 10;
877 rect.h = cellh - 5;
878 widget_fillrect(wid, &rect, c);
880 rect.y = padding + border + (cellh + border) * sourceybottom;
881 widget_fillrect(wid, &rect, c);
883 //victory animation
884 if (game_won) {
885 animate_pulse(wid);
889 char* read_field(FILE *fp)
891 char *r;
892 int i;
893 char c;
895 r = (char *) malloc(1024);
896 i = 0;
898 for(;;) {
899 c = getc(fp);
901 if (feof(fp)) {
902 free(r);
903 return NULL;
906 switch(c) {
907 case ',':
908 r[i] = 0;
909 return r;
910 case '\n':
911 r[i] = 0;
912 return r;
913 default:
914 r[i] = c;
915 i++;
916 break;
921 void update_hsw()
923 int i;
924 for (i=0; i<level_max; i++) {
925 if (hstable[i]->name) {
926 label_put_text(hsw[i]->name, hstable[i]->name);
927 } else {
928 label_put_text(hsw[i]->name, "None");
930 if (hstable[i]->time != -1) {
931 char s[80];
933 sprintf(s, "%d", hstable[i]->time);
934 label_put_text(hsw[i]->time, s);
935 } else {
936 label_put_text(hsw[i]->name, "None");
941 void read_hstable()
943 FILE *fp;
944 int i;
946 for (i=0; i<level_max; i++) {
947 hstable[i]->name = NULL;
948 hstable[i]->time = -1;
951 fp = fopen(config->hsfile, "r");
952 if (!fp) return;
954 for(i=0; i<level_max; i++) {
955 char *s;
956 s = read_field(fp);
957 if (!s) goto done;
959 hstable[i]->name = s;
961 s = read_field(fp);
962 if (!s) goto done;
964 hstable[i]->time = atoi(s);
965 free(s);
968 done:
969 fclose(fp);
972 void write_hstable()
974 FILE *fp;
975 int i;
977 fp = fopen(config->hsfile, "w");
978 if (!fp) return;
980 for(i=0; i<level_max; i++) {
981 fprintf(fp, "%s,%d\n", hstable[i]->name, hstable[i]->time);
984 fclose(fp);
987 int enkludge;
989 void enter_name_open()
991 modalwindow = enter_name_window;
992 enkludge = 1;
995 void enter_name_close()
997 modalwindow = NULL;
998 enkludge = 0;
1000 player_name = tb_en1->text;
1002 if (hstable[level]->name) {
1003 free(hstable[level]->name);
1005 hstable[level]->name = clonestr(player_name);
1006 hstable[level]->time = second_count;
1007 update_hsw();
1008 write_hstable();
1011 void check_hs()
1013 if (hstable[level]->time == -1 || second_count < hstable[level]->time) {
1014 enter_name_open();
1018 void init_tileimg(SDL_Surface * tileimg[64],int bgcolor)
1020 int i, j;
1021 SDL_PixelFormat *fmt = screen->format;
1022 SDL_Rect rect;
1023 int c;
1025 pipex = cellw / 2 - pipet / 2;
1026 pipey = cellw / 2 - pipet / 2;
1027 pipew = cellw / 2 + pipet / 2;
1028 pipeh = cellh / 2 + pipet / 2;
1030 SDL_Rect entirecell = (SDL_Rect){
1031 .x = 0,
1032 .y = 0,
1033 .w = cellw,
1034 .h = cellh
1037 for (i=0; i<64; i++) {
1038 tileimg[i] = SDL_CreateRGBSurface(0, cellw, cellh, fmt->BitsPerPixel,
1039 fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
1041 SDL_FillRect(tileimg[i], &entirecell, ctable[bgcolor]);
1043 for (j=0; j<4; j++) {
1044 if ((i + 1) & (1 << j)) {
1045 switch (j) {
1046 case 0:
1047 rect.x = pipex;
1048 rect.y = 0;
1049 rect.w = pipet;
1050 rect.h = pipeh;
1051 break;
1052 case 1:
1053 rect.x = pipex;
1054 rect.y = pipey;
1055 rect.w = pipew;
1056 rect.h = pipet;
1057 break;
1058 case 2:
1059 rect.x = pipex;
1060 rect.y = pipey;
1061 rect.w = pipet;
1062 rect.h = pipeh;
1063 break;
1064 case 3:
1065 rect.x = 0;
1066 rect.y = pipey;
1067 rect.w = pipew;
1068 rect.h = pipet;
1069 break;
1071 if (i >= 16) {
1072 c = ctable[c_on];
1073 } else c = ctable[c_off];
1074 SDL_FillRect(tileimg[i], &rect, c);
1079 for (i=1; i<32; i*=2) {
1080 rect.x = cellw / 2 - 2 * pipet;
1081 rect.y = cellh / 2 - 2 * pipet;
1082 rect.w = pipet * 4;
1083 rect.h = pipet * 4;
1084 rect.x++;
1085 rect.w-=2;
1086 rect.y++;
1087 rect.h-=2;
1089 SDL_FillRect(tileimg[i-1], &rect, ctable[c_off]);
1090 SDL_FillRect(tileimg[i-1+16], &rect, ctable[c_on]);
1091 rect.x++;
1092 rect.w-=2;
1093 rect.y++;
1094 rect.h-=2;
1096 SDL_FillRect(tileimg[i-1], &rect, ctable[c_down]);
1097 SDL_FillRect(tileimg[i-1+16], &rect, ctable[c_up]);
1101 void reset_move_count()
1103 move_count = 0;
1104 label_put_text(l_moves, "moves: 0");
1107 void increment_move_count()
1109 char s[80];
1110 move_count++;
1111 sprintf(s, "moves: %d", move_count);
1112 label_put_text(l_moves, s);
1115 void reset_time()
1117 label_put_text(l_time, "time: 0");
1120 void update_time()
1122 static char s[80];
1124 ms_count += tick - tick_old;
1125 while (ms_count >= 1000) {
1126 ms_count -= 1000;
1127 second_count++;
1128 sprintf(s, "time: %d", second_count);
1129 label_put_text(l_time, s);
1133 void resize()
1134 //position everything based on board size
1136 int w, h;
1138 sourcex = boardw / 2 - 1;
1139 sourceytop = boardh / 2;
1140 sourceybottom = sourceytop + 1;
1142 w = cellw * boardw + (boardw + 1) * border + 2 * padding;
1143 h = cellh * boardh + (boardh + 1) * border + 2 * padding;
1144 widget_put_geometry(arena, 0, vsize, w, h);
1145 set_video(w, h + 2 * vsize);
1146 widget_put_geometry(root, 0, 0, w, h + 2 * vsize);
1147 widget_put_geometry(menu, 0, 0, w, vsize);
1148 menubar_auto_layout(menu);
1149 widget_put_geometry(statusbar, 0, h + vsize, w, vsize);
1151 widget_put_geometry(l_moves, 8, h + vsize, 64, vsize);
1152 widget_put_geometry(l_time, w - 48, h + vsize, 64, vsize);
1154 widget_put_geometry((widget_ptr) about_window,
1155 w/2 - 50, h/2 - 50, 100, 100);
1156 widget_put_geometry((widget_ptr) l_about1, 10, 10, 60, vsize);
1157 widget_put_geometry((widget_ptr) l_about2, 10, 30, 60, vsize);
1158 widget_put_geometry((widget_ptr) b_about1, 30, 80, 30, vsize);
1160 /* vertical sizes and positions for the high score window */
1161 int const hslabely = 5;
1162 int const hslabelheight = vsize;
1163 int const hslisty = hslabely + hslabelheight + 8;
1164 int const hslisteachheight = vsize;
1165 int const hslistheight = hslisteachheight * level_max;
1166 int const hsoky = hslisty + hslistheight + 8;
1167 int const hsokheight = vsize;
1168 int const hswindowheight = hsoky + hsokheight + 5;
1170 widget_put_geometry((widget_ptr) hs_window,
1171 w/2 - 75, h/2 - 60, 170, hswindowheight);
1172 widget_put_geometry((widget_ptr) l_hs1, 10, hslabely, 60, hslabelheight);
1173 widget_put_geometry((widget_ptr) b_hs1, 100, hsoky, 30, hsokheight);
1176 int i;
1177 for (i=0; i<level_max; i++) {
1178 int y = hslisty + (hslisteachheight * i);
1179 widget_put_geometry((widget_ptr) hsw[i]->level,
1180 10, y, 20, hslisteachheight);
1181 widget_put_geometry((widget_ptr) hsw[i]->time,
1182 60, y, 20, hslisteachheight);
1183 widget_put_geometry((widget_ptr) hsw[i]->name,
1184 90, y, 20, hslisteachheight);
1188 widget_put_geometry((widget_ptr) enter_name_window,
1189 10, h/2 - 30, w - 20, 67);
1190 widget_put_geometry((widget_ptr) l_en1, 10, 0, 60, vsize);
1191 widget_put_geometry((widget_ptr) tb_en1, 5, vsize + 5, w - 30, vsize);
1192 widget_put_geometry((widget_ptr) b_en1, w - 60, 45, 30, vsize);
1194 ((widget_ptr) root)->computexy((widget_ptr) root);
1195 ((widget_ptr) about_window)->computexy((widget_ptr) about_window);
1196 ((widget_ptr) hs_window)->computexy((widget_ptr) hs_window);
1197 ((widget_ptr) enter_name_window)->computexy((widget_ptr) enter_name_window);
1200 void new_game()
1202 char s[BUFSIZ];
1203 strcpy(s,"NetWalk - ");
1204 strcat(s,level_name[level]);
1205 SDL_WM_SetCaption(s,s);
1207 switch(level) {
1208 case level_easy:
1209 boardw = 5; boardh = 5;
1210 wrap_flag = 0;
1211 no_fourway = 0;
1212 break;
1213 case level_medium:
1214 boardw = 10; boardh = 9;
1215 wrap_flag = 0;
1216 no_fourway = 0;
1217 break;
1218 case level_hard:
1219 boardw = 10; boardh = 9;
1220 wrap_flag = 1;
1221 no_fourway = 1;
1222 break;
1223 case level_veryhard:
1224 boardw = 20; boardh = 18;
1225 wrap_flag = 1;
1226 no_fourway = 1;
1227 break;
1228 case level_giant:
1229 boardw = 50; boardh = 50;
1230 wrap_flag = 0;
1231 no_fourway = 1;
1232 break;
1233 case level_absurd:
1234 boardw = 50; boardh = 50;
1235 wrap_flag = 1;
1236 no_fourway = 1;
1237 break;
1238 default:
1239 break;
1241 resize();
1242 srand(time(NULL));
1243 generate_maze();
1244 clear_flags();
1245 scramble();
1246 check_live();
1247 reset_move_count();
1248 reset_time();
1249 ms_count = 0;
1250 tick = SDL_GetTicks();
1251 second_count = 0;
1254 void handle_mousebuttonup(SDL_Event *event)
1256 int x = event->button.x;
1257 int y = event->button.y;
1258 if (openedmenu) {
1259 int i;
1260 menuitem_ptr it;
1261 menu_ptr m;
1263 m = openedmenu->submenu;
1264 for (i=0; i<m->item_count; i++) {
1265 it = m->item_list[i];
1266 if (in_widget((widget_ptr) it, x, y)) {
1267 widget_raise_signal((widget_ptr) it, signal_activate);
1268 break;
1271 openedmenu = NULL;
1273 return;
1274 } else if (state == state_button) {
1275 state = state_game;
1276 if (in_widget(button_selection, x, y)) {
1277 widget_raise_signal(button_selection, signal_activate);
1278 return;
1283 void handle_click(int button, int x, int y)
1285 widget_ptr wid;
1287 if (modalwindow) {
1288 wid = (widget_ptr) modalwindow;
1289 if (in_widget(wid, x, y) && (wid->handle_click)) {
1290 wid->handle_click(wid, button, x, y);
1292 return;
1295 wid = (widget_ptr) root;
1296 wid->handle_click(wid, button, x, y);
1299 void arena_handle_click(widget_ptr p, int button, int x, int y)
1301 int i, j;
1302 int d;
1304 if (state != state_game) return;
1306 i = (x - padding - border) / (cellw + border);
1307 j = (y - padding - border) / (cellh + border);
1308 if (i >= boardw || j >= boardh) return;
1310 if (game_won) {
1311 if (i == sourcex && (j == sourceytop || j == sourceybottom)) {
1312 new_pulse(sourcex, sourceybottom, -1);
1313 new_pulse(sourcex, sourceytop, -1);
1314 } else {
1315 new_pulse(i, j, -1);
1317 return;
1320 //temporarily merge server squares
1321 board[sourcex][sourceybottom] |= board[sourcex][sourceytop] & 1;
1322 if (i == sourcex && j == sourceytop) {
1323 j = sourceybottom;
1325 d = board[i][j] & 15;
1326 switch(button) {
1327 case SDL_BUTTON_LEFT:
1328 d = rotatecw(d, 3);
1329 increment_move_count();
1330 break;
1331 case SDL_BUTTON_RIGHT:
1332 d = rotatecw(d, 1);
1333 increment_move_count();
1334 break;
1336 board[i][j] &= ~15;
1337 board[i][j] += d;
1339 board[sourcex][sourceytop] &= ~1;
1340 board[sourcex][sourceytop] |= board[sourcex][sourceybottom] & 1;
1341 board[sourcex][sourceybottom] &= ~1;
1343 if(button == SDL_BUTTON_MIDDLE)
1344 flags[i][j] ^= 0x1;
1346 check_live();
1347 if (game_won) {
1348 pulse_count = 0;
1350 check_hs();
1354 void quit()
1356 state = state_quit;
1359 void quit_menu(widget_ptr w, void *data)
1361 quit();
1364 void new_game_menu(widget_ptr w, void *data)
1366 new_game();
1369 void about_open(widget_ptr w, void *data)
1371 modalwindow = about_window;
1374 void about_close(widget_ptr w, void *data)
1376 modalwindow = NULL;
1379 void hs_open(widget_ptr w, void *data)
1381 modalwindow = hs_window;
1384 void hs_close(widget_ptr w, void *data)
1386 modalwindow = NULL;
1389 void set_level(widget_ptr w, void *data)
1391 level = (intptr_t) data;
1392 new_game();
1395 void handle_key(int key, int mod)
1397 if (openedmenu) {
1398 switch(key) {
1399 case SDLK_ESCAPE:
1400 openedmenu = NULL;
1402 return;
1405 if (enkludge) {
1406 switch(key) {
1407 case SDLK_LEFT:
1408 textbox_left(tb_en1);
1409 break;
1410 case SDLK_RIGHT:
1411 textbox_right(tb_en1);
1412 break;
1413 case SDLK_DELETE:
1414 textbox_delete(tb_en1);
1415 break;
1416 case SDLK_BACKSPACE:
1417 textbox_backspace(tb_en1);
1418 break;
1419 default:
1420 if (key < 256 && key >= 32) {
1421 if (mod & KMOD_SHIFT) {
1422 textbox_insert(tb_en1, shifttable[key]);
1423 } else {
1424 textbox_insert(tb_en1, key);
1427 break;
1429 return;
1432 switch(key) {
1433 case SDLK_d:
1434 enter_name_open();
1435 break;
1436 case SDLK_ESCAPE:
1437 case SDLK_q:
1438 quit();
1439 break;
1440 case SDLK_F2:
1441 new_game();
1442 break;
1446 void update_screen()
1448 SDL_FillRect(screen, NULL, 0);
1449 widget_update((widget_ptr) root);
1450 if (openedmenu) {
1451 int i;
1452 menuitem_ptr it;
1454 for (i=0; i<menu->item_count; i++) {
1455 it = menu->item_list[i];
1456 if (in_widget((widget_ptr) it, lastmousex, lastmousey)) {
1457 open_submenu((widget_ptr) it, NULL);
1460 menu_update(openedmenu->submenu);
1462 if (modalwindow) {
1463 widget_update((widget_ptr) modalwindow);
1465 SDL_Flip(screen);
1468 void window_update(widget_ptr p)
1470 window_ptr w = (window_ptr) p;
1471 widget_ptr wid;
1472 int i;
1473 SDL_Rect dst;
1475 if (p != (widget_ptr) root) {
1476 dst.x = -1;
1477 dst.y = -1;
1478 dst.w = p->w + 2;
1479 dst.h = p->h + 2;
1480 widget_fillrect(p, &dst, c_windowborder);
1481 widget_fill(p, c_background);
1484 for (i=0; i<w->widget_count; i++) {
1485 wid = w->widget_list[i];
1486 widget_update(wid);
1490 void window_handle_click(widget_ptr p, int button, int x, int y)
1492 widget_ptr wid;
1493 window_ptr window = (window_ptr) p;
1494 int i;
1496 for (i=0; i<window->widget_count; i++) {
1497 wid = window->widget_list[i];
1498 if (in_widget(wid, x, y) && (wid->handle_click)) {
1499 wid->handle_click(wid, button, x - wid->x, y - wid->y);
1500 return;
1505 void window_add_widget(window_ptr r, void *p)
1507 widget_ptr wid = (widget_ptr) p;
1508 r->widget_list[r->widget_count] = wid;
1509 r->widget_count++;
1510 wid->parent = (widget_ptr) r;
1511 wid->x += r->widget.x;
1512 wid->y += r->widget.y;
1515 void window_computexy(widget_ptr wid)
1517 int i;
1518 window_ptr w = (window_ptr) wid;
1520 widget_computexy(wid);
1521 for (i=0; i<w->widget_count; i++) {
1522 w->widget_list[i]->computexy(w->widget_list[i]);
1526 void window_init(window_ptr w)
1528 widget_init((widget_ptr) w);
1529 w->widget_count = 0;
1530 w->widget.update = window_update;
1531 w->widget.handle_click = window_handle_click;
1532 w->widget.computexy = window_computexy;
1535 static void add_shiftstring(char *s1, char *s2)
1537 int i;
1539 for (i=0; i<strlen(s1); i++) {
1540 shifttable[(int) s1[i]] = s2[i];
1544 int main(int argc, char *argv[])
1546 SDL_Event event;
1548 //setup names
1549 level_name[level_easy] = "Newbie";
1550 level_name[level_medium] = "Normal";
1551 level_name[level_hard] = "Nerd";
1552 level_name[level_veryhard] = "Nutcase";
1553 level_name[level_giant] = "Nonsense";
1554 level_name[level_absurd] = "No Sleep";
1556 //setup shifttable
1558 int i;
1560 for (i=0; i<256; i++) shifttable[i] = i;
1562 for (i='a'; i<='z'; i++) {
1563 shifttable[i] = i - 32;
1566 add_shiftstring("1234567890-=", "!@#$%^&*()_+");
1567 add_shiftstring("[]\\;',./`", "{}|:\"<>?~");
1570 config_load(config);
1571 read_hstable();
1572 init();
1574 init_rgbtable();
1576 font = TTF_OpenFont(config->fontname, config->fontsize);
1577 if (!font) {
1578 fprintf(stderr, "error loading font %s\n", config->fontname);
1579 exit(1);
1582 window_init(root);
1584 //need to set video mode here to initialize colour table
1585 set_video(100, 100);
1587 //setup enter name box
1589 window_init(enter_name_window);
1591 label_init(l_en1);
1592 label_put_text(l_en1, "Enter name:");
1593 window_add_widget(enter_name_window, l_en1);
1595 textbox_init(tb_en1);
1596 textbox_put_text(tb_en1, "Anonymous");
1597 window_add_widget(enter_name_window, tb_en1);
1599 button_init(b_en1);
1600 button_put_text(b_en1, "Ok");
1601 window_add_widget(enter_name_window, b_en1);
1602 widget_put_handler((widget_ptr) b_en1, enter_name_close, signal_activate);
1605 //setup the "arena": where the action is
1607 widget_init((widget_ptr) arena);
1608 arena->widget.update = arena_update;
1609 arena->widget.handle_click = arena_handle_click;
1610 window_add_widget(root, arena);
1613 //status bar: mainly for showing the time
1615 widget_init((widget_ptr) statusbar);
1616 statusbar->update = statusbar_update;
1617 window_add_widget(root, statusbar);
1619 //setup moves and time
1620 label_init(l_moves);
1621 if (config->showmoves) {
1622 window_add_widget(root, l_moves);
1625 label_init(l_time);
1626 window_add_widget(root, l_time);
1629 //setup the menus
1631 menuitem_t it1, it2;
1632 menuitem_ptr it;
1633 menu_t m1, m2;
1635 intptr_t i;
1637 menubar_init(menu);
1638 window_add_widget(root, menu);
1639 menuitem_init(it1);
1640 menuitem_put_text(it1, "Game");
1641 menubar_add_item(menu, it1);
1642 menuitem_init(it2);
1643 menuitem_put_text(it2, "Help");
1644 menubar_add_item(menu, it2);
1646 menu_init(m1);
1648 it = menuitem_new();
1649 menuitem_put_text(it, "New game");
1650 widget_put_handler((widget_ptr) it, new_game_menu, signal_activate);
1651 menu_add_item(m1, it);
1653 for (i=0; i<level_max; i++) {
1654 it = menuitem_new();
1655 menuitem_put_text(it, level_name[i]);
1656 widget_put_handler_data((widget_ptr) it,
1657 set_level, (void *) i, signal_activate);
1658 menu_add_item(m1, it);
1660 it = menuitem_new();
1661 menuitem_put_text(it, "High Scores");
1662 widget_put_handler((widget_ptr) it, hs_open, signal_activate);
1663 menu_add_item(m1, it);
1664 it = menuitem_new();
1665 menuitem_put_text(it, "Quit");
1666 widget_put_handler((widget_ptr) it, quit_menu, signal_activate);
1667 menu_add_item(m1, it);
1669 menuitem_set_submenu(it1, m1);
1671 menu_init(m2);
1673 it = menuitem_new();
1674 menuitem_put_text(it, "About");
1675 widget_put_handler((widget_ptr) it, about_open, signal_activate);
1676 menu_add_item(m2, it);
1678 menuitem_set_submenu(it2, m2);
1681 //setup about box
1683 window_init(about_window);
1685 label_init(l_about1);
1686 label_put_text(l_about1, "NetWalk " VERSION_STRING);
1687 window_add_widget(about_window, l_about1);
1689 label_init(l_about2);
1690 label_put_text(l_about2, "Ben Lynn");
1691 window_add_widget(about_window, l_about2);
1693 button_init(b_about1);
1694 button_put_text(b_about1, "Ok");
1695 window_add_widget(about_window, b_about1);
1696 widget_put_handler((widget_ptr) b_about1, about_close, signal_activate);
1699 //setup hiscores box
1701 int i;
1702 window_init(hs_window);
1704 label_init(l_hs1);
1705 label_put_text(l_hs1, "High Scores");
1706 window_add_widget(hs_window, l_hs1);
1708 button_init(b_hs1);
1709 button_put_text(b_hs1, "Ok");
1710 window_add_widget(hs_window, b_hs1);
1711 widget_put_handler((widget_ptr) b_hs1, hs_close, signal_activate);
1713 for (i=0; i<level_max; i++) {
1714 label_init(hsw[i]->level);
1715 label_put_text(hsw[i]->level, level_name[i]);
1716 window_add_widget(hs_window, hsw[i]->level);
1717 label_init(hsw[i]->name);
1718 window_add_widget(hs_window, hsw[i]->name);
1719 label_init(hsw[i]->time);
1720 window_add_widget(hs_window, hsw[i]->time);
1724 resize();
1726 update_hsw();
1728 init_tileimg(unmarked_tileimg,c_unmarkedbg);
1729 init_tileimg(marked_tileimg,c_markedbg);
1730 new_game();
1732 while (state != state_quit && !interrupted) {
1733 tick_old = tick;
1734 tick = SDL_GetTicks();
1735 if (!game_won) {
1736 update_time();
1738 SDL_GetMouseState(&lastmousex, &lastmousey);
1739 while (SDL_PollEvent(&event)) {
1740 switch (event.type) {
1741 case SDL_KEYDOWN:
1742 handle_key(event.key.keysym.sym, SDL_GetModState());
1743 break;
1744 case SDL_MOUSEBUTTONDOWN:
1745 handle_click(event.button.button, event.button.x, event.button.y);
1746 break;
1747 case SDL_MOUSEBUTTONUP:
1748 handle_mousebuttonup(&event);
1749 break;
1750 case SDL_QUIT:
1751 quit();
1752 break;
1755 update_screen();
1756 SDL_Delay(20);
1758 return 0;