summaryrefslogtreecommitdiff
path: root/apps/plugins/snake2.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/snake2.c')
-rw-r--r--apps/plugins/snake2.c961
1 files changed, 961 insertions, 0 deletions
diff --git a/apps/plugins/snake2.c b/apps/plugins/snake2.c
new file mode 100644
index 0000000000..faa89cddd1
--- /dev/null
+++ b/apps/plugins/snake2.c
@@ -0,0 +1,961 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2003 Mat Holton
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20/*
21Snake2!
22
23Board consists of a WIDTHxHEIGHT grid. If board element is 0 then nothing is
24there otherwise it is part of the snake or a wall.
25
26Head and Tail are stored
27
28*/
29
30#include "plugin.h"
31#ifdef HAVE_LCD_BITMAP
32
33#define WIDTH 28
34#define HEIGHT 16
35
36/*Board itself - 2D int array*/
37static int board[WIDTH][HEIGHT];
38/*
39 Buffer for sorting movement (in case user presses two movements during a
40 single frame
41*/
42static int ardirectionbuffer[2];
43static unsigned int score, hiscore = 0;
44static short applex;
45static short appley;
46static short dir;
47static short frames;
48static short apple;
49static short level = 4, speed = 5,dead = 0, quit = 0, sillydir = 0, num_levels=0;
50static short level_from_file = 1;
51static struct plugin_api* rb;
52static int headx, heady, tailx, taily, applecountdown = 5;
53static int game_type = 0;
54static int num_apples_to_get=1;
55static int num_apples_to_got=0;
56static int game_b_level=1;
57
58#define NORTH 1
59#define EAST 2
60#define SOUTH 4
61#define WEST 8
62#define HEAD 16
63
64#define EAST_NORTH 32
65#define EAST_SOUTH 64
66#define WEST_NORTH 128
67#define WEST_SOUTH 256
68
69#define NORTH_EAST 512
70#define NORTH_WEST 1024
71#define SOUTH_EAST 2048
72#define SOUTH_WEST 4096
73
74#define LEVELS_FILE "/.rockbox/snake2.levels"
75
76static void set_level_count(void)
77{
78 int line_count=0;
79 int fd = 0;
80 char buffer[WIDTH+2]; /* WIDTH plus CR/LF and \0 */
81
82 if ((fd = rb->open(LEVELS_FILE, O_RDONLY)) < 0)
83 {
84 rb->splash(0, true, "Unable to open %s", LEVELS_FILE);
85 }
86
87 if(!(fd < 0))
88 {
89 while(1)
90 {
91 int len = rb->read_line(fd, buffer, sizeof(buffer));
92 if(len <= 0)
93 break;
94 else
95 line_count++;
96
97 if(line_count==HEIGHT)
98 {
99 num_levels++;
100 line_count=0;
101 }
102 }
103
104 rb->close(fd);
105 }
106
107}
108
109/*
110** Completely clear the board of walls and/or snake
111*/
112void clear_board( void)
113{
114 int x,y;
115
116 for (x = 0; x < WIDTH; x++)
117 {
118 for (y = 0; y < HEIGHT; y++)
119 {
120 board[x][y] = 0;
121 }
122 }
123}
124
125int load_level( int level_number )
126{
127 int fd = 0;
128 int x,y, len;
129 unsigned char buffer[WIDTH+2];
130
131 /* open file */
132 if ((fd = rb->open(LEVELS_FILE, O_RDONLY)) < 0)
133 {
134 return -1;
135 }
136
137 rb->lseek(fd, level_number*(WIDTH+2)*(HEIGHT+1), SEEK_SET);
138
139 clear_board();
140
141 for(y=0; y < HEIGHT; y++)
142 {
143 len = rb->read_line(fd, buffer, WIDTH+2);
144 if(len <= 0)
145 {
146 rb->close(fd);
147 return -1;
148 }
149 else
150 {
151 /*Read a line in, now add to the array:*/
152 for(x=0;x < WIDTH;x++)
153 {
154 switch(buffer[x])
155 {
156 case '1':
157 board[x][y] = NORTH;
158 break;
159
160 case '2':
161 board[x][y] = EAST;
162 break;
163
164 case 'H':
165 board[x][y] = HEAD;
166 break;
167 }
168 }
169 }
170 }
171 rb->close(fd);
172 return 1;
173}
174
175/*
176** Gets the currently chosen direction from the first place
177** in the direction buffer. If there is something in the
178** next part of the buffer then that is moved to the first place
179*/
180void get_direction( void )
181{
182 /*if 1st place is empty*/
183 if(ardirectionbuffer[0] != -1)
184 {
185 /*return this direction*/
186 dir = ardirectionbuffer[0];
187 ardirectionbuffer[0]=-1;
188 /*now see if one needs moving:*/
189 if(ardirectionbuffer[1] != -1)
190 {
191 /*there's a move waiting to be done
192 so move it into the space:*/
193 ardirectionbuffer[0] = ardirectionbuffer[1];
194 ardirectionbuffer[1] = -1;
195 }
196 }
197}
198
199/*
200** Sets the direction
201*/
202void set_direction(int newdir)
203{
204 if(ardirectionbuffer[0] != newdir)
205 {
206 /*if 1st place is empty*/
207 if(ardirectionbuffer[0] == -1)
208 {
209 /*use 1st space:*/
210 ardirectionbuffer[0] = newdir;
211 }
212 else
213 {
214 /*use 2nd space:*/
215 if(ardirectionbuffer[0] != newdir) ardirectionbuffer[1] = newdir;
216 }
217
218 if(frames < 0) ardirectionbuffer[0] = newdir;
219 }
220}
221
222void init_snake( void )
223{
224 ardirectionbuffer[0] = -1;
225 ardirectionbuffer[1] = -1;
226 dir = EAST;
227 headx = WIDTH/2;
228 heady = HEIGHT/2;
229 tailx = headx - 4;
230 taily = heady;
231 applecountdown = 0;
232 /*Create a small snake to start off with*/
233 board[headx][heady] = dir;
234 board[headx-1][heady] = dir;
235 board[headx-2][heady] = dir;
236 board[headx-3][heady] = dir;
237 board[headx-4][heady] = dir;
238 num_apples_to_got=0;
239 num_apples_to_get=1;
240 level_from_file = 1;
241 game_b_level=1;
242}
243
244/*
245** Draws the apple. If it doesn't exist then
246** a new one get's created.
247*/
248void draw_apple( void )
249{
250 short x,y;
251 if (!apple)
252 {
253 do
254 {
255 x = (rb->rand() % (WIDTH-1))+1;
256 y = (rb->rand() % (HEIGHT-1))+1;
257 } while (board[x][y]);
258 apple=1;
259 board[x][y]=-1;
260 applex = x;appley = y;
261 }
262 rb->lcd_fillrect((applex*4)+1,appley*4,2,4);
263 rb->lcd_fillrect(applex*4,(appley*4)+1,4,2);
264}
265
266/*
267 * x x *
268 * x x *
269 * x x *
270 * x x *
271*/
272void draw_vertical_bit(int x, int y)
273{
274 rb->lcd_fillrect(x*4+1,y*4,2,4);
275}
276
277/*
278 * * * *
279 X X X X
280 X X X X
281 * * * *
282*/
283void draw_horizontal_bit(int x, int y)
284{
285 rb->lcd_fillrect(x*4,y*4+1,4,2);
286}
287
288/*
289 * * * *
290 * * X X
291 * X X X
292 * X X *
293*/
294void draw_n_to_e_bit(int x, int y)
295{
296 rb->lcd_fillrect(x*4+1,y*4+2,2,2);
297 rb->lcd_fillrect(x*4+2,y*4+1,2,2);
298}
299
300/*
301 * * * *
302 * * X X
303 * X X X
304 * X X *
305*/
306void draw_w_to_s_bit(int x, int y)
307{
308 draw_n_to_e_bit(x,y);
309}
310
311/*
312 * * * *
313 X X * *
314 X X X *
315 * X X *
316*/
317void draw_n_to_w_bit(int x, int y)
318{
319 rb->lcd_fillrect(x*4,y*4+1,2,2);
320 rb->lcd_fillrect(x*4+1,y*4+2,2,2);
321}
322
323/*
324 * * * *
325 X X * *
326 X X X *
327 * X X *
328*/
329void draw_e_to_s_bit(int x, int y)
330{
331 draw_n_to_w_bit(x, y);
332}
333
334/*
335 * X X *
336 * X X X
337 * * X X
338 * * * *
339*/
340void draw_s_to_e_bit(int x, int y)
341{
342 rb->lcd_fillrect(x*4+1,y*4,2,2);
343 rb->lcd_fillrect(x*4+2,y*4+1,2,2);
344}
345
346/*
347 * X X *
348 * X X X
349 * * X X
350 * * * *
351*/
352void draw_w_to_n_bit(int x, int y)
353{
354 draw_s_to_e_bit(x,y);
355}
356
357/*
358 * X X *
359 X X X *
360 X X * *
361 * * * *
362*/
363void draw_e_to_n_bit(int x, int y)
364{
365 rb->lcd_fillrect(x*4+1,y*4,2,2);
366 rb->lcd_fillrect(x*4,y*4+1,2,2);
367}
368
369/*
370 * X X *
371 X X X *
372 X X * *
373 * * * *
374*/
375void draw_s_to_w_bit(int x, int y)
376{
377 draw_e_to_n_bit(x, y);
378}
379
380/*
381** Draws a wall/obsticals
382*/
383void draw_boundary ( void )
384{
385 int x, y;
386
387 /*TODO: Load levels from file!*/
388
389 /*top and bottom line*/
390 for(x=0; x < WIDTH; x++)
391 {
392 board[x][0] = EAST;
393 board[x][HEIGHT-1] = WEST;
394 }
395
396 /*left and right lines*/
397 for(y=0; y < HEIGHT; y++)
398 {
399 board[0][y] = NORTH;
400 board[WIDTH-1][y] = SOUTH;
401 }
402
403 /*corners:*/
404 board[0][0] = NORTH_EAST;
405 board[WIDTH-1][0] = EAST_SOUTH;
406 board[0][HEIGHT-1] = SOUTH_EAST;
407 board[WIDTH-1][HEIGHT-1] = EAST_NORTH;
408}
409
410/*
411** Redraw the entire board
412*/
413void redraw (void)
414{
415 short x,y;
416 rb->lcd_clear_display();
417
418 for (x = 0; x < WIDTH; x++)
419 {
420 for (y = 0; y < HEIGHT; y++)
421 {
422 switch (board[x][y])
423 {
424 case -1:
425 rb->lcd_fillrect((x*4)+1,y*4,2,4);
426 rb->lcd_fillrect(x*4,(y*4)+1,4,2);
427 break;
428 case 0:
429 break;
430
431 case NORTH:
432 case SOUTH:
433 draw_vertical_bit(x,y);
434 break;
435
436 case EAST:
437 case WEST:
438 draw_horizontal_bit(x,y);
439 break;
440
441 default:
442 rb->lcd_fillrect(x*4,y*4,4,4);
443 break;
444 }
445 }
446 }
447 rb->lcd_update();
448}
449
450/*
451** Draws the snake bit described by nCurrentBit at position x/y
452** deciding whether it's a corner bit by examing the nPrevious bit
453*/
454void draw_snake_bit(int currentbit, int previousbit, int x, int y)
455{
456 rb->lcd_clearrect(x*4,y*4,4,4);
457 switch(currentbit)
458 {
459 case(NORTH):
460 switch(previousbit)
461 {
462 case(SOUTH):
463 case(NORTH):
464 draw_vertical_bit(x,y);
465 break;
466
467 case(EAST):
468 draw_e_to_n_bit(x,y);
469 break;
470
471 case(WEST):
472 draw_w_to_n_bit(x,y);
473 break;
474 }
475 break;
476
477 case(EAST):
478 switch(previousbit)
479 {
480 case(WEST):
481 case(EAST):
482 draw_horizontal_bit(x,y);
483 break;
484
485 case(NORTH):
486 draw_n_to_e_bit(x,y);
487 break;
488
489 case(SOUTH):
490 draw_s_to_e_bit(x,y);
491 break;
492 }
493 break;
494
495 case(SOUTH):
496 switch(previousbit)
497 {
498 case(SOUTH):
499 case(NORTH):
500 draw_vertical_bit(x,y);
501 break;
502
503 case(EAST):
504 draw_e_to_s_bit(x,y);
505 break;
506
507 case(WEST):
508 draw_w_to_s_bit(x,y);
509 break;
510 }
511 break;
512
513 case(WEST):
514 switch(previousbit)
515 {
516 case(EAST):
517 case(WEST):
518 draw_horizontal_bit(x,y);
519 break;
520
521 case(SOUTH):
522 draw_s_to_w_bit(x,y);
523 break;
524
525 case(NORTH):
526 draw_n_to_w_bit(x,y);
527 break;
528 }
529 break;
530 }
531}
532
533/*
534** Death 'sequence' and end game stuff.
535*/
536void die (void)
537{
538 int n=100;
539 int count;
540 char pscore[15],hscore[17];
541
542 /*Flashy death sequence (flashy as in 'flashes')*/
543 for(count=0;count<24;count++)
544 {
545 rb->sleep(HZ/n);
546 rb->lcd_clear_display();
547 draw_apple();
548 rb->lcd_update();
549
550 rb->sleep(HZ/n);
551 redraw();
552 rb->lcd_update();
553 }
554
555 rb->lcd_clear_display();
556 draw_apple();
557 rb->lcd_update();
558
559 rb->snprintf(pscore,sizeof(pscore),"%d",score);
560 rb->lcd_putsxy(LCD_WIDTH/2 - 15,12,"Dead");
561 rb->lcd_putsxy(LCD_WIDTH/2 - 35,22,"Your score :");
562 rb->lcd_putsxy(LCD_WIDTH/2 - 35,32, pscore);
563 if (score>hiscore)
564 {
565 hiscore=score;
566 rb->lcd_putsxy(3,42,"New High Score!");
567 }
568 else
569 {
570 rb->snprintf(hscore,sizeof(hscore),"High Score: %d",hiscore);
571 rb->lcd_putsxy(5,42,hscore);
572 }
573 rb->lcd_update();
574 rb->sleep(3*HZ);
575 dead=1;
576}
577
578/*
579** Check for collision. TODO: Currently this
580** sets of the death sequence. What we want is it to only return a true/false
581** depending on whether a collision occured.
582*/
583void collision ( int x, int y )
584{
585 int bdeath=0;
586
587 switch (board[x][y])
588 {
589 case 0:
590
591 break;
592 case -1:
593 score+=2;
594 apple=0;
595 applecountdown=2;
596
597 if(game_type==1)
598 {
599 if(num_apples_to_get == num_apples_to_got)
600 {
601 level_from_file++;
602 if(level_from_file >= num_levels)
603 {
604 level_from_file = 1;
605 /*and increase the number of apples to pick up
606 before level changes*/
607 num_apples_to_get+=2;
608 game_b_level++;
609 }
610 rb->splash(0, true, "Level Completed!");
611 rb->sleep(HZ);
612 rb->lcd_clear_display();
613 num_apples_to_got=0;
614 load_level(level_from_file);
615 init_snake();
616 redraw();
617 }
618 else
619 num_apples_to_got++;
620 }
621 break;
622 default:
623 bdeath=1;
624 break;
625 }
626
627 if(bdeath==1)
628 {
629 die();
630 sillydir = dir;
631 frames = -110;
632 }
633}
634
635void move( void )
636{
637 int taildir;
638 /*this actually sets the dir variable.*/
639 get_direction();
640 /*draw head*/
641 switch (dir)
642 {
643 case (NORTH):
644 board[headx][heady]=NORTH;
645 heady--;
646 break;
647 case (EAST):
648 board[headx][heady]=EAST;
649 headx++;
650 break;
651 case (SOUTH):
652 board[headx][heady]=SOUTH;
653 heady++;
654 break;
655 case (WEST):
656 board[headx][heady]=WEST;
657 headx--;
658 break;
659 }
660
661 if(headx == WIDTH)
662 headx = 0;
663 else if(headx < 0)
664 headx = WIDTH-1;
665
666 if(heady == HEIGHT)
667 heady = 0;
668 else if(heady < 0)
669 heady = HEIGHT-1;
670
671 rb->lcd_fillrect(headx*4,heady*4,4,4);
672
673 /*clear tail*/
674 if(applecountdown <= 0)
675 {
676 rb->lcd_clearrect(tailx*4,taily*4,4,4);
677
678 taildir = board[tailx][taily];
679 board[tailx][taily] = 0;
680
681 switch (taildir)
682 {
683 case(NORTH):
684 taily--;
685 break;
686
687 case(EAST):
688 tailx++;
689 break;
690
691 case(SOUTH):
692 taily++;
693 break;
694
695 case(WEST):
696 tailx--;
697 break;
698 }
699
700 if(tailx == WIDTH)
701 tailx = 0;
702 else if(tailx < 0)
703 tailx = WIDTH-1;
704
705 if(taily == HEIGHT)
706 taily = 0;
707 else if(taily < 0)
708 taily = HEIGHT-1;
709 }
710 else
711 applecountdown--;
712}
713
714void frame (void)
715{
716 int olddir, noldx, noldy, temp;
717 noldx = headx;
718 noldy = heady;
719 olddir = 0;
720 switch(dir)
721 {
722 case(NORTH):
723 if(heady == HEIGHT-1)
724 temp = 0;
725 else
726 temp = heady + 1;
727
728 olddir = board[headx][temp];
729 break;
730
731 case(EAST):
732 if(headx == 0)
733 temp = WIDTH-1;
734 else
735 temp = headx - 1;
736
737 olddir = board[temp][heady];
738 break;
739
740 case(SOUTH):
741 if(heady == 0)
742 temp = HEIGHT-1;
743 else
744 temp = heady - 1;
745
746 olddir = board[headx][temp];
747 break;
748
749 case(WEST):
750 if(headx == WIDTH-1)
751 temp = 0;
752 else
753 temp = headx + 1;
754
755 olddir = board[temp][heady];
756 break;
757 }
758
759 move();
760
761 /*
762 now redraw the bit that was
763 the tail, to something snake-like:
764 */
765 draw_snake_bit(dir, olddir, noldx, noldy);
766
767 collision(headx, heady);
768
769 rb->lcd_update();
770}
771
772void game_pause (void)
773{
774 rb->lcd_clear_display();
775 rb->lcd_putsxy(33,12,"Paused");
776
777 rb->lcd_update();
778 while (1)
779 {
780 switch (rb->button_get(true))
781 {
782 case BUTTON_PLAY:
783 redraw();
784 rb->sleep(HZ/2);
785 return;
786 }
787 }
788}
789
790void game (void)
791{
792 redraw();
793 /*main loop:*/
794 while (1)
795 {
796 if(frames==5)
797 {
798 frame();
799 if(frames>0) frames=0;
800 }
801 frames++;
802
803 if(frames == 0)
804 {
805 die();
806 }
807 else
808 {
809 if(frames < 0)
810 {
811 if(sillydir != dir)
812 {
813 /*it has, great set frames to a positive value again:*/
814 frames = 1;
815 }
816 }
817 }
818
819 if (dead) return;
820
821 draw_apple();
822
823 rb->sleep(HZ/speed);
824
825 switch (rb->button_get(false))
826 {
827 case BUTTON_UP:
828 case BUTTON_UP | BUTTON_REPEAT:
829 if (dir != SOUTH) set_direction(NORTH);
830 break;
831
832 case BUTTON_RIGHT:
833 case BUTTON_RIGHT | BUTTON_REPEAT:
834 if (dir != WEST) set_direction(EAST);
835 break;
836
837 case BUTTON_DOWN:
838 case BUTTON_DOWN | BUTTON_REPEAT:
839 if (dir != NORTH) set_direction(SOUTH);
840 break;
841
842 case BUTTON_LEFT:
843 case BUTTON_LEFT | BUTTON_REPEAT:
844 if (dir != EAST) set_direction(WEST);
845 break;
846
847 case BUTTON_OFF:
848 dead=1;
849 return;
850
851 case BUTTON_PLAY:
852 game_pause();
853 break;
854 }
855 }
856}
857
858void game_init(void)
859{
860 char plevel[30];
861 char phscore[30];
862
863 dead=0;
864 apple=0;
865 score=0;
866
867 while (1)
868 {
869 switch (rb->button_get(true))
870 {
871 case BUTTON_RIGHT:
872 case BUTTON_UP:
873 if (level<10)
874 level+=1;
875 break;
876 case BUTTON_LEFT:
877 case BUTTON_DOWN:
878 if (level>1)
879 level-=1;
880 break;
881 case BUTTON_OFF:
882 quit=1;
883 return;
884 break;
885 case BUTTON_PLAY:
886 speed = level*20;
887 return;
888 break;
889 case BUTTON_F3:
890 if(game_type==0)game_type=1; else game_type=0;
891 break;
892 case BUTTON_F1:
893
894 level_from_file++;
895 if(level_from_file > num_levels)
896 {
897 level_from_file = 1;
898 }
899
900 load_level( level_from_file );
901
902 break;
903 }
904
905 rb->lcd_clear_display();
906 redraw();
907 /*TODO: CENTER ALL TEXT!!!!*/
908 rb->snprintf(plevel,sizeof(plevel),"Speed - %d ",level);
909 rb->lcd_putsxy(LCD_WIDTH/2 - 30,5, plevel);
910 rb->snprintf(plevel,sizeof(plevel),"F1 - Maze %d ",level_from_file);
911 rb->lcd_putsxy(18, 20, plevel);
912 if(game_type==0)
913 rb->lcd_putsxy(18, 30, "F3 - Game A");
914 else
915 rb->lcd_putsxy(18, 30, "F3 - Game B");
916
917 rb->snprintf(phscore,sizeof(phscore),"Hi Score: %d",hiscore);
918 rb->lcd_putsxy(LCD_WIDTH/2 - 37,50, phscore);
919 rb->lcd_update();
920 }
921}
922
923enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
924{
925 TEST_PLUGIN_API(api);
926 (void)(parameter);
927 rb = api;
928 set_level_count();
929
930 if (num_levels == 0) {
931 rb->splash(HZ*2, true, "Failed loading levels!");
932 return PLUGIN_OK;
933 }
934
935 /* Lets use the default font */
936 rb->lcd_setfont(FONT_SYSFIXED);
937 /*load the 1st level in*/
938 load_level( level_from_file );
939
940 while(quit==0)
941 {
942 game_init();
943 rb->lcd_clear_display();
944 frames=1;
945
946 if(quit==0)
947 {
948 load_level( level_from_file );
949 init_snake();
950 /*Start Game:*/
951 game();
952 /* Game over, reload level*/
953 init_snake();
954 load_level( level_from_file );
955 }
956 }
957
958 return false;
959}
960
961#endif