diff options
Diffstat (limited to 'apps/plugins')
-rw-r--r-- | apps/plugins/Makefile | 45 | ||||
-rw-r--r-- | apps/plugins/bounce.c | 409 | ||||
-rw-r--r-- | apps/plugins/cube.c | 338 | ||||
-rw-r--r-- | apps/plugins/helloworld.c | 48 | ||||
-rw-r--r-- | apps/plugins/oscillograph.c | 207 | ||||
-rw-r--r-- | apps/plugins/plugin.lds | 26 | ||||
-rw-r--r-- | apps/plugins/snow.c | 111 | ||||
-rw-r--r-- | apps/plugins/sokoban.c | 868 | ||||
-rw-r--r-- | apps/plugins/viewer.c | 415 | ||||
-rw-r--r-- | apps/plugins/wormlet.c | 2009 |
10 files changed, 4476 insertions, 0 deletions
diff --git a/apps/plugins/Makefile b/apps/plugins/Makefile new file mode 100644 index 0000000000..4c02207016 --- /dev/null +++ b/apps/plugins/Makefile | |||
@@ -0,0 +1,45 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | # $Id$ | ||
8 | # | ||
9 | |||
10 | CC = sh-elf-gcc | ||
11 | OC = sh-elf-objcopy | ||
12 | |||
13 | FIRMWARE = ../../firmware | ||
14 | |||
15 | INCLUDES = -I$(FIRMWARE)/include -I$(FIRMWARE)/export -I$(FIRMWARE)/common -I$(FIRMWARE)/drivers -I.. | ||
16 | CFLAGS = -O -W -Wall -m1 -nostdlib -ffreestanding -Wstrict-prototypes $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) | ||
17 | |||
18 | LINKFILE = plugin.lds | ||
19 | |||
20 | SRC := $(wildcard *.c) | ||
21 | ROCKS := $(SRC:%.c=$(OBJDIR)/%.rock) | ||
22 | |||
23 | ifndef OBJDIR | ||
24 | no_configure: | ||
25 | @echo "Don't run make here. Run the tools/configure script from your own build" | ||
26 | @echo "directory, then run make there." | ||
27 | @echo | ||
28 | @echo "More help on how to build rockbox can be found here:" | ||
29 | @echo "http://rockbox.haxx.se/docs/how_to_compile.html" | ||
30 | endif | ||
31 | |||
32 | $(OBJDIR)/%.elf: $(OBJDIR)/%.o $(LINKFILE) | ||
33 | $(CC) -O -nostdlib -o $@ $< -lgcc -T$(LINKFILE) -Wl,-Map,$*.map | ||
34 | |||
35 | $(OBJDIR)/%.rock : $(OBJDIR)/%.elf | ||
36 | $(OC) -O binary $< $@ | ||
37 | |||
38 | $(OBJDIR)/%.o: %.c ../plugin.h Makefile | ||
39 | $(CC) $(CFLAGS) -c $< -o $@ | ||
40 | |||
41 | all: $(ROCKS) | ||
42 | @echo done | ||
43 | |||
44 | clean: | ||
45 | -rm -f $(ROCKS) | ||
diff --git a/apps/plugins/bounce.c b/apps/plugins/bounce.c new file mode 100644 index 0000000000..0c53d49887 --- /dev/null +++ b/apps/plugins/bounce.c | |||
@@ -0,0 +1,409 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Daniel Stenberg | ||
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 | #include "plugin.h" | ||
20 | |||
21 | #ifdef HAVE_LCD_BITMAP | ||
22 | |||
23 | #define SS_TITLE "Bouncer" | ||
24 | #define SS_TITLE_FONT 2 | ||
25 | |||
26 | #define LETTERS_ON_SCREEN 12 | ||
27 | |||
28 | #define YSPEED 2 | ||
29 | #define XSPEED 3 | ||
30 | #define YADD -4 | ||
31 | |||
32 | static struct plugin_api* rb; | ||
33 | |||
34 | static unsigned char table[]={ | ||
35 | 26,28,30,33,35,37,39,40,42,43,45,46,46,47,47,47,47,47,46,46,45,43,42,40,39,37,35,33,30,28,26,24,21,19,17,14,12,10,8,7,5,4,2,1,1,0,0,0,0,0,1,1,2,4,5,7,8,10,12,14,17,19,21,23, | ||
36 | }; | ||
37 | |||
38 | static unsigned char xtable[]={ | ||
39 | 54,58,63,67,71,75,79,82,85,88,91,93,95,97,98,99,99,99,99,99,97,96,94,92,90,87,84,80,77,73,69,65,60,56,52,47,43,39,34,30,26,22,19,15,12,9,7,5,3,2,0,0,0,0,0,1,2,4,6,8,11,14,17,20,24,28,32,36,41,45,49 | ||
40 | }; | ||
41 | |||
42 | static signed char speed[]={ | ||
43 | 1,2,3,3,3,2,1,0,-1,-2,-2,-2,-1,0,0,1, | ||
44 | }; | ||
45 | |||
46 | const unsigned char char_gen_12x16[][22] = | ||
47 | { | ||
48 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
49 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x7c,0x00,0xff,0x33,0xff,0x33,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
50 | { 0x00,0x00,0x00,0x00,0x3c,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x3c,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
51 | { 0x00,0x02,0x10,0x1e,0x90,0x1f,0xf0,0x03,0x7e,0x02,0x1e,0x1e,0x90,0x1f,0xf0,0x03,0x7e,0x02,0x1e,0x00,0x10,0x00 }, | ||
52 | { 0x00,0x00,0x78,0x04,0xfc,0x0c,0xcc,0x0c,0xff,0x3f,0xff,0x3f,0xcc,0x0c,0xcc,0x0f,0x88,0x07,0x00,0x00,0x00,0x00 }, | ||
53 | { 0x00,0x30,0x38,0x38,0x38,0x1c,0x38,0x0e,0x00,0x07,0x80,0x03,0xc0,0x01,0xe0,0x38,0x70,0x38,0x38,0x38,0x1c,0x00 }, | ||
54 | { 0x00,0x00,0x00,0x1f,0xb8,0x3f,0xfc,0x31,0xc6,0x21,0xe2,0x37,0x3e,0x1e,0x1c,0x1c,0x00,0x36,0x00,0x22,0x00,0x00 }, | ||
55 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x27,0x00,0x3f,0x00,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
56 | { 0x00,0x00,0x00,0x00,0xf0,0x03,0xfc,0x0f,0xfe,0x1f,0x07,0x38,0x01,0x20,0x01,0x20,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
57 | { 0x00,0x00,0x00,0x00,0x01,0x20,0x01,0x20,0x07,0x38,0xfe,0x1f,0xfc,0x0f,0xf0,0x03,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
58 | { 0x00,0x00,0x98,0x0c,0xb8,0x0e,0xe0,0x03,0xf8,0x0f,0xf8,0x0f,0xe0,0x03,0xb8,0x0e,0x98,0x0c,0x00,0x00,0x00,0x00 }, | ||
59 | { 0x00,0x00,0x80,0x01,0x80,0x01,0x80,0x01,0xf0,0x0f,0xf0,0x0f,0x80,0x01,0x80,0x01,0x80,0x01,0x00,0x00,0x00,0x00 }, | ||
60 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb8,0x00,0xf8,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
61 | { 0x00,0x00,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x00,0x00,0x00,0x00 }, | ||
62 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x38,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
63 | { 0x00,0x18,0x00,0x1c,0x00,0x0e,0x00,0x07,0x80,0x03,0xc0,0x01,0xe0,0x00,0x70,0x00,0x38,0x00,0x1c,0x00,0x0e,0x00 }, | ||
64 | { 0xf8,0x07,0xfe,0x1f,0x06,0x1e,0x03,0x33,0x83,0x31,0xc3,0x30,0x63,0x30,0x33,0x30,0x1e,0x18,0xfe,0x1f,0xf8,0x07 }, | ||
65 | { 0x00,0x00,0x00,0x00,0x0c,0x30,0x0c,0x30,0x0e,0x30,0xff,0x3f,0xff,0x3f,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x00 }, | ||
66 | { 0x1c,0x30,0x1e,0x38,0x07,0x3c,0x03,0x3e,0x03,0x37,0x83,0x33,0xc3,0x31,0xe3,0x30,0x77,0x30,0x3e,0x30,0x1c,0x30 }, | ||
67 | { 0x0c,0x0c,0x0e,0x1c,0x07,0x38,0xc3,0x30,0xc3,0x30,0xc3,0x30,0xc3,0x30,0xc3,0x30,0xe7,0x39,0x7e,0x1f,0x3c,0x0e }, | ||
68 | { 0xc0,0x03,0xe0,0x03,0x70,0x03,0x38,0x03,0x1c,0x03,0x0e,0x03,0x07,0x03,0xff,0x3f,0xff,0x3f,0x00,0x03,0x00,0x03 }, | ||
69 | { 0x3f,0x0c,0x7f,0x1c,0x63,0x38,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0x63,0x30,0xe3,0x38,0xc3,0x1f,0x83,0x0f }, | ||
70 | { 0xc0,0x0f,0xf0,0x1f,0xf8,0x39,0xdc,0x30,0xce,0x30,0xc7,0x30,0xc3,0x30,0xc3,0x30,0xc3,0x39,0x80,0x1f,0x00,0x0f }, | ||
71 | { 0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x30,0x03,0x3c,0x03,0x0f,0xc3,0x03,0xf3,0x00,0x3f,0x00,0x0f,0x00,0x03,0x00 }, | ||
72 | { 0x00,0x0f,0xbc,0x1f,0xfe,0x39,0xe7,0x30,0xc3,0x30,0xc3,0x30,0xc3,0x30,0xe7,0x30,0xfe,0x39,0xbc,0x1f,0x00,0x0f }, | ||
73 | { 0x3c,0x00,0x7e,0x00,0xe7,0x30,0xc3,0x30,0xc3,0x30,0xc3,0x38,0xc3,0x1c,0xc3,0x0e,0xe7,0x07,0xfe,0x03,0xfc,0x00 }, | ||
74 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x1c,0x70,0x1c,0x70,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
75 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x9c,0x70,0xfc,0x70,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
76 | { 0x00,0x00,0xc0,0x00,0xe0,0x01,0xf0,0x03,0x38,0x07,0x1c,0x0e,0x0e,0x1c,0x07,0x38,0x03,0x30,0x00,0x00,0x00,0x00 }, | ||
77 | { 0x00,0x00,0x60,0x06,0x60,0x06,0x60,0x06,0x60,0x06,0x60,0x06,0x60,0x06,0x60,0x06,0x60,0x06,0x60,0x06,0x00,0x00 }, | ||
78 | { 0x00,0x00,0x03,0x30,0x07,0x38,0x0e,0x1c,0x1c,0x0e,0x38,0x07,0xf0,0x03,0xe0,0x01,0xc0,0x00,0x00,0x00,0x00,0x00 }, | ||
79 | { 0x1c,0x00,0x1e,0x00,0x07,0x00,0x03,0x00,0x83,0x37,0xc3,0x37,0xe3,0x00,0x77,0x00,0x3e,0x00,0x1c,0x00,0x00,0x00 }, | ||
80 | { 0xf8,0x0f,0xfe,0x1f,0x07,0x18,0xf3,0x33,0xfb,0x37,0x1b,0x36,0xfb,0x37,0xfb,0x37,0x07,0x36,0xfe,0x03,0xf8,0x01 }, | ||
81 | { 0x00,0x38,0x00,0x3f,0xe0,0x07,0xfc,0x06,0x1f,0x06,0x1f,0x06,0xfc,0x06,0xe0,0x07,0x00,0x3f,0x00,0x38,0x00,0x00 }, | ||
82 | { 0xff,0x3f,0xff,0x3f,0xc3,0x30,0xc3,0x30,0xc3,0x30,0xc3,0x30,0xe7,0x30,0xfe,0x39,0xbc,0x1f,0x00,0x0f,0x00,0x00 }, | ||
83 | { 0xf0,0x03,0xfc,0x0f,0x0e,0x1c,0x07,0x38,0x03,0x30,0x03,0x30,0x03,0x30,0x07,0x38,0x0e,0x1c,0x0c,0x0c,0x00,0x00 }, | ||
84 | { 0xff,0x3f,0xff,0x3f,0x03,0x30,0x03,0x30,0x03,0x30,0x03,0x30,0x07,0x38,0x0e,0x1c,0xfc,0x0f,0xf0,0x03,0x00,0x00 }, | ||
85 | { 0xff,0x3f,0xff,0x3f,0xc3,0x30,0xc3,0x30,0xc3,0x30,0xc3,0x30,0xc3,0x30,0xc3,0x30,0x03,0x30,0x03,0x30,0x00,0x00 }, | ||
86 | { 0xff,0x3f,0xff,0x3f,0xc3,0x00,0xc3,0x00,0xc3,0x00,0xc3,0x00,0xc3,0x00,0xc3,0x00,0x03,0x00,0x03,0x00,0x00,0x00 }, | ||
87 | { 0xf0,0x03,0xfc,0x0f,0x0e,0x1c,0x07,0x38,0x03,0x30,0xc3,0x30,0xc3,0x30,0xc3,0x30,0xc7,0x3f,0xc6,0x3f,0x00,0x00 }, | ||
88 | { 0xff,0x3f,0xff,0x3f,0xc0,0x00,0xc0,0x00,0xc0,0x00,0xc0,0x00,0xc0,0x00,0xc0,0x00,0xff,0x3f,0xff,0x3f,0x00,0x00 }, | ||
89 | { 0x00,0x00,0x00,0x00,0x03,0x30,0x03,0x30,0xff,0x3f,0xff,0x3f,0x03,0x30,0x03,0x30,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
90 | { 0x00,0x0e,0x00,0x1e,0x00,0x38,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x38,0xff,0x1f,0xff,0x07,0x00,0x00 }, | ||
91 | { 0xff,0x3f,0xff,0x3f,0xc0,0x00,0xe0,0x01,0xf0,0x03,0x38,0x07,0x1c,0x0e,0x0e,0x1c,0x07,0x38,0x03,0x30,0x00,0x00 }, | ||
92 | { 0xff,0x3f,0xff,0x3f,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x00 }, | ||
93 | { 0xff,0x3f,0xff,0x3f,0x1e,0x00,0x78,0x00,0xe0,0x01,0xe0,0x01,0x78,0x00,0x1e,0x00,0xff,0x3f,0xff,0x3f,0x00,0x00 }, | ||
94 | { 0xff,0x3f,0xff,0x3f,0x0e,0x00,0x38,0x00,0xf0,0x00,0xc0,0x03,0x00,0x07,0x00,0x1c,0xff,0x3f,0xff,0x3f,0x00,0x00 }, | ||
95 | { 0xf0,0x03,0xfc,0x0f,0x0e,0x1c,0x07,0x38,0x03,0x30,0x03,0x30,0x07,0x38,0x0e,0x1c,0xfc,0x0f,0xf0,0x03,0x00,0x00 }, | ||
96 | { 0xff,0x3f,0xff,0x3f,0x83,0x01,0x83,0x01,0x83,0x01,0x83,0x01,0x83,0x01,0xc7,0x01,0xfe,0x00,0x7c,0x00,0x00,0x00 }, | ||
97 | { 0xf0,0x03,0xfc,0x0f,0x0e,0x1c,0x07,0x38,0x03,0x30,0x03,0x36,0x07,0x3e,0x0e,0x1c,0xfc,0x3f,0xf0,0x33,0x00,0x00 }, | ||
98 | { 0xff,0x3f,0xff,0x3f,0x83,0x01,0x83,0x01,0x83,0x03,0x83,0x07,0x83,0x0f,0xc7,0x1d,0xfe,0x38,0x7c,0x30,0x00,0x00 }, | ||
99 | { 0x3c,0x0c,0x7e,0x1c,0xe7,0x38,0xc3,0x30,0xc3,0x30,0xc3,0x30,0xc3,0x30,0xc7,0x39,0x8e,0x1f,0x0c,0x0f,0x00,0x00 }, | ||
100 | { 0x00,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0xff,0x3f,0xff,0x3f,0x03,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00 }, | ||
101 | { 0xff,0x07,0xff,0x1f,0x00,0x38,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x38,0xff,0x1f,0xff,0x07,0x00,0x00 }, | ||
102 | { 0x07,0x00,0x3f,0x00,0xf8,0x01,0xc0,0x0f,0x00,0x3e,0x00,0x3e,0xc0,0x0f,0xf8,0x01,0x3f,0x00,0x07,0x00,0x00,0x00 }, | ||
103 | { 0xff,0x3f,0xff,0x3f,0x00,0x1c,0x00,0x06,0x80,0x03,0x80,0x03,0x00,0x06,0x00,0x1c,0xff,0x3f,0xff,0x3f,0x00,0x00 }, | ||
104 | { 0x03,0x30,0x0f,0x3c,0x1c,0x0e,0x30,0x03,0xe0,0x01,0xe0,0x01,0x30,0x03,0x1c,0x0e,0x0f,0x3c,0x03,0x30,0x00,0x00 }, | ||
105 | { 0x03,0x00,0x0f,0x00,0x3c,0x00,0xf0,0x00,0xc0,0x3f,0xc0,0x3f,0xf0,0x00,0x3c,0x00,0x0f,0x00,0x03,0x00,0x00,0x00 }, | ||
106 | { 0x03,0x30,0x03,0x3c,0x03,0x3e,0x03,0x33,0xc3,0x31,0xe3,0x30,0x33,0x30,0x1f,0x30,0x0f,0x30,0x03,0x30,0x00,0x00 }, | ||
107 | { 0x00,0x00,0x00,0x00,0xff,0x3f,0xff,0x3f,0x03,0x30,0x03,0x30,0x03,0x30,0x03,0x30,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
108 | { 0x0e,0x00,0x1c,0x00,0x38,0x00,0x70,0x00,0xe0,0x00,0xc0,0x01,0x80,0x03,0x00,0x07,0x00,0x0e,0x00,0x1c,0x00,0x18 }, | ||
109 | { 0x00,0x00,0x00,0x00,0x03,0x30,0x03,0x30,0x03,0x30,0x03,0x30,0xff,0x3f,0xff,0x3f,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
110 | { 0x60,0x00,0x70,0x00,0x38,0x00,0x1c,0x00,0x0e,0x00,0x07,0x00,0x0e,0x00,0x1c,0x00,0x38,0x00,0x70,0x00,0x60,0x00 }, | ||
111 | { 0x00,0xc0,0x00,0xc0,0x00,0xc0,0x00,0xc0,0x00,0xc0,0x00,0xc0,0x00,0xc0,0x00,0xc0,0x00,0xc0,0x00,0xc0,0x00,0xc0 }, | ||
112 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x00,0x7e,0x00,0x4e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
113 | { 0x00,0x1c,0x40,0x3e,0x60,0x33,0x60,0x33,0x60,0x33,0x60,0x33,0x60,0x33,0x60,0x33,0xe0,0x3f,0xc0,0x3f,0x00,0x00 }, | ||
114 | { 0xff,0x3f,0xff,0x3f,0xc0,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xe0,0x38,0xc0,0x1f,0x80,0x0f,0x00,0x00 }, | ||
115 | { 0x80,0x0f,0xc0,0x1f,0xe0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xc0,0x18,0x80,0x08,0x00,0x00 }, | ||
116 | { 0x80,0x0f,0xc0,0x1f,0xe0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0xe0,0x30,0xc0,0x30,0xff,0x3f,0xff,0x3f,0x00,0x00 }, | ||
117 | { 0x80,0x0f,0xc0,0x1f,0xe0,0x3b,0x60,0x33,0x60,0x33,0x60,0x33,0x60,0x33,0x60,0x33,0xc0,0x13,0x80,0x01,0x00,0x00 }, | ||
118 | { 0xc0,0x00,0xc0,0x00,0xfc,0x3f,0xfe,0x3f,0xc7,0x00,0xc3,0x00,0xc3,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
119 | { 0x80,0x03,0xc0,0xc7,0xe0,0xce,0x60,0xcc,0x60,0xcc,0x60,0xcc,0x60,0xcc,0x60,0xe6,0xe0,0x7f,0xe0,0x3f,0x00,0x00 }, | ||
120 | { 0xff,0x3f,0xff,0x3f,0xc0,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0xe0,0x00,0xc0,0x3f,0x80,0x3f,0x00,0x00,0x00,0x00 }, | ||
121 | { 0x00,0x00,0x00,0x00,0x00,0x30,0x60,0x30,0xec,0x3f,0xec,0x3f,0x00,0x30,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
122 | { 0x00,0x00,0x00,0x00,0x00,0x60,0x00,0xe0,0x00,0xc0,0x60,0xc0,0xec,0xff,0xec,0x7f,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
123 | { 0x00,0x00,0xff,0x3f,0xff,0x3f,0x00,0x03,0x80,0x07,0xc0,0x0f,0xe0,0x1c,0x60,0x38,0x00,0x30,0x00,0x00,0x00,0x00 }, | ||
124 | { 0x00,0x00,0x00,0x00,0x00,0x30,0x03,0x30,0xff,0x3f,0xff,0x3f,0x00,0x30,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
125 | { 0xe0,0x3f,0xc0,0x3f,0xe0,0x00,0xe0,0x00,0xc0,0x3f,0xc0,0x3f,0xe0,0x00,0xe0,0x00,0xc0,0x3f,0x80,0x3f,0x00,0x00 }, | ||
126 | { 0x00,0x00,0xe0,0x3f,0xe0,0x3f,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0xe0,0x00,0xc0,0x3f,0x80,0x3f,0x00,0x00 }, | ||
127 | { 0x80,0x0f,0xc0,0x1f,0xe0,0x38,0x60,0x30,0x60,0x30,0x60,0x30,0x60,0x30,0xe0,0x38,0xc0,0x1f,0x80,0x0f,0x00,0x00 }, | ||
128 | { 0xe0,0xff,0xe0,0xff,0x60,0x0c,0x60,0x18,0x60,0x18,0x60,0x18,0x60,0x18,0xe0,0x1c,0xc0,0x0f,0x80,0x07,0x00,0x00 }, | ||
129 | { 0x80,0x07,0xc0,0x0f,0xe0,0x1c,0x60,0x18,0x60,0x18,0x60,0x18,0x60,0x18,0x60,0x0c,0xe0,0xff,0xe0,0xff,0x00,0x00 }, | ||
130 | { 0x00,0x00,0xe0,0x3f,0xe0,0x3f,0xc0,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0x60,0x00,0xe0,0x00,0xc0,0x00,0x00,0x00 }, | ||
131 | { 0xc0,0x11,0xe0,0x33,0x60,0x33,0x60,0x33,0x60,0x33,0x60,0x33,0x60,0x3f,0x40,0x1e,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
132 | { 0x60,0x00,0x60,0x00,0xfe,0x1f,0xfe,0x3f,0x60,0x30,0x60,0x30,0x60,0x30,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
133 | { 0xe0,0x0f,0xe0,0x1f,0x00,0x38,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x30,0x00,0x18,0xe0,0x3f,0xe0,0x3f,0x00,0x00 }, | ||
134 | { 0x60,0x00,0xe0,0x01,0x80,0x07,0x00,0x1e,0x00,0x38,0x00,0x38,0x00,0x1e,0x80,0x07,0xe0,0x01,0x60,0x00,0x00,0x00 }, | ||
135 | { 0xe0,0x07,0xe0,0x1f,0x00,0x38,0x00,0x1c,0xe0,0x0f,0xe0,0x0f,0x00,0x1c,0x00,0x38,0xe0,0x1f,0xe0,0x07,0x00,0x00 }, | ||
136 | { 0x60,0x30,0xe0,0x38,0xc0,0x1d,0x80,0x0f,0x00,0x07,0x80,0x0f,0xc0,0x1d,0xe0,0x38,0x60,0x30,0x00,0x00,0x00,0x00 }, | ||
137 | { 0x00,0x00,0x60,0x00,0xe0,0x81,0x80,0xe7,0x00,0x7e,0x00,0x1e,0x80,0x07,0xe0,0x01,0x60,0x00,0x00,0x00,0x00,0x00 }, | ||
138 | { 0x60,0x30,0x60,0x38,0x60,0x3c,0x60,0x36,0x60,0x33,0xe0,0x31,0xe0,0x30,0x60,0x30,0x20,0x30,0x00,0x00,0x00,0x00 }, | ||
139 | { 0x00,0x00,0x80,0x00,0xc0,0x01,0xfc,0x1f,0x7e,0x3f,0x07,0x70,0x03,0x60,0x03,0x60,0x03,0x60,0x00,0x00,0x00,0x00 }, | ||
140 | { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x3f,0xff,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, | ||
141 | { 0x00,0x00,0x03,0x60,0x03,0x60,0x03,0x60,0x07,0x70,0x7e,0x3f,0xfc,0x1f,0xc0,0x01,0x80,0x00,0x00,0x00,0x00,0x00 }, | ||
142 | { 0x10,0x00,0x18,0x00,0x0c,0x00,0x04,0x00,0x0c,0x00,0x18,0x00,0x10,0x00,0x18,0x00,0x0c,0x00,0x04,0x00,0x00,0x00 }, | ||
143 | { 0xff,0x3f,0xff,0x3f,0xff,0x3f,0xff,0x3f,0xff,0x3f,0xff,0x3f,0xff,0x3f,0xff,0x3f,0xff,0x3f,0xff,0x3f,0x00,0x00 } | ||
144 | }; | ||
145 | |||
146 | #define XDIFF -4 | ||
147 | #define YDIFF -6 | ||
148 | |||
149 | enum { | ||
150 | NUM_XSANKE, | ||
151 | NUM_YSANKE, | ||
152 | NUM_XADD, | ||
153 | NUM_YADD, | ||
154 | NUM_XDIST, | ||
155 | NUM_YDIST, | ||
156 | |||
157 | NUM_LAST | ||
158 | }; | ||
159 | |||
160 | struct counter { | ||
161 | char *what; | ||
162 | int num; | ||
163 | }; | ||
164 | |||
165 | struct counter values[]={ | ||
166 | {"xsanke", 1}, | ||
167 | {"ysanke", 1}, | ||
168 | {"xadd", 1}, | ||
169 | {"yadd", 1}, | ||
170 | {"xdist", -4}, | ||
171 | {"ydistt", -6}, | ||
172 | }; | ||
173 | |||
174 | #ifdef USE_CLOCK | ||
175 | static unsigned char yminute[]={ | ||
176 | 53,53,52,52,51,50,49,47,46,44,42,40,38,36,34,32,29,27,25,23,21,19,17,16,14,13,12,11,11,10,10,10,11,11,12,13,14,16,17,19,21,23,25,27,29,31,34,36,38,40,42,44,46,47,49,50,51,52,52,53, | ||
177 | }; | ||
178 | static unsigned char yhour[]={ | ||
179 | 42,42,42,42,41,41,40,39,39,38,37,36,35,34,33,32,30,29,28,27,26,25,24,24,23,22,22,21,21,21,21,21,21,21,22,22,23,24,24,25,26,27,28,29,30,31,33,34,35,36,37,38,39,39,40,41,41,42,42,42, | ||
180 | }; | ||
181 | |||
182 | static unsigned char xminute[]={ | ||
183 | 56,59,63,67,71,74,77,80,83,86,88,90,91,92,93,93,93,92,91,90,88,86,83,80,77,74,71,67,63,59,56,52,48,44,40,37,34,31,28,25,23,21,20,19,18,18,18,19,20,21,23,25,28,31,34,37,40,44,48,52, | ||
184 | }; | ||
185 | static unsigned char xhour[]={ | ||
186 | 56,57,59,61,63,65,66,68,69,71,72,73,73,74,74,74,74,74,73,73,72,71,69,68,66,65,63,61,59,57,56,54,52,50,48,46,45,43,42,40,39,38,38,37,37,37,37,37,38,38,39,40,42,43,45,46,48,50,52,54, | ||
187 | }; | ||
188 | |||
189 | static void addclock(void) | ||
190 | { | ||
191 | int i; | ||
192 | int hour; | ||
193 | int minute; | ||
194 | int pos; | ||
195 | |||
196 | hour = rtc_read(3); | ||
197 | hour = (((hour & 0x30) >> 4) * 10 + (hour & 0x0f))%12; | ||
198 | minute = rtc_read(2); | ||
199 | minute = ((minute & 0x70) >> 4) * 10 + (minute & 0x0f); | ||
200 | |||
201 | pos = 90-minute; | ||
202 | if(pos >= 60) | ||
203 | pos -= 60; | ||
204 | |||
205 | rb->lcd_drawline(LCD_WIDTH/2, LCD_HEIGHT/2, xminute[pos], yminute[pos]); | ||
206 | |||
207 | hour = hour*5 + minute/12; | ||
208 | pos = 90-hour; | ||
209 | if(pos >= 60) | ||
210 | pos -= 60; | ||
211 | |||
212 | rb->lcd_drawline(LCD_WIDTH/2, LCD_HEIGHT/2, xhour[pos], yhour[pos]); | ||
213 | |||
214 | /* draw a circle */ | ||
215 | for(i=0; i < 60; i+=3) { | ||
216 | rb->lcd_drawline( xminute[i], | ||
217 | yminute[i], | ||
218 | xminute[(i+1)%60], | ||
219 | yminute[(i+1)%60]); | ||
220 | } | ||
221 | } | ||
222 | #endif | ||
223 | |||
224 | static int scrollit(void) | ||
225 | { | ||
226 | int b; | ||
227 | unsigned int y=100; | ||
228 | int x=LCD_WIDTH; | ||
229 | unsigned int yy,xx; | ||
230 | unsigned int i; | ||
231 | int textpos=0; | ||
232 | |||
233 | char* rock="Rockbox! Pure pleasure. Pure fun. Oooh. What fun! ;-) "; | ||
234 | int letter; | ||
235 | |||
236 | rb->lcd_clear_display(); | ||
237 | while(1) | ||
238 | { | ||
239 | b = rb->button_get_w_tmo(HZ/10); | ||
240 | if ( b == (BUTTON_OFF|BUTTON_REL) ) | ||
241 | return 0; | ||
242 | else if ( b == (BUTTON_ON|BUTTON_REL) ) | ||
243 | return 1; | ||
244 | |||
245 | rb->lcd_clear_display(); | ||
246 | |||
247 | for(i=0, yy=y, xx=x; i< LETTERS_ON_SCREEN; i++) { | ||
248 | letter = rock[(i+textpos) % (sizeof(rock)-1) ]; | ||
249 | |||
250 | rb->lcd_bitmap((char *)char_gen_12x16[letter-0x20], | ||
251 | xx, table[yy&63], | ||
252 | 11, 16, false); | ||
253 | yy += YADD; | ||
254 | xx+= LCD_WIDTH/LETTERS_ON_SCREEN; | ||
255 | } | ||
256 | #ifdef USE_CLOCK | ||
257 | addclock(); | ||
258 | #endif | ||
259 | rb->lcd_update(); | ||
260 | |||
261 | x-= XSPEED; | ||
262 | |||
263 | if(x < 0) { | ||
264 | x += LCD_WIDTH/LETTERS_ON_SCREEN; | ||
265 | y += YADD; | ||
266 | textpos++; | ||
267 | } | ||
268 | |||
269 | y+=YSPEED; | ||
270 | |||
271 | } | ||
272 | } | ||
273 | |||
274 | static int loopit(void) | ||
275 | { | ||
276 | int b; | ||
277 | unsigned int y=100; | ||
278 | unsigned int x=100; | ||
279 | unsigned int yy,xx; | ||
280 | unsigned int i; | ||
281 | unsigned int ysanke=0; | ||
282 | unsigned int xsanke=0; | ||
283 | |||
284 | char* rock="ROCKbox"; | ||
285 | |||
286 | int show=0; | ||
287 | int timeout=0; | ||
288 | char buffer[30]; | ||
289 | |||
290 | rb->lcd_clear_display(); | ||
291 | while(1) | ||
292 | { | ||
293 | b = rb->button_get_w_tmo(HZ/10); | ||
294 | if ( b == (BUTTON_OFF|BUTTON_REL) ) | ||
295 | return 0; | ||
296 | |||
297 | if ( b == SYS_USB_CONNECTED) { | ||
298 | rb->usb_screen(); | ||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | if ( b == (BUTTON_ON|BUTTON_REL) ) | ||
303 | return 1; | ||
304 | else if(b != BUTTON_NONE) | ||
305 | timeout=20; | ||
306 | |||
307 | y+= speed[ysanke&15] + values[NUM_YADD].num; | ||
308 | x+= speed[xsanke&15] + values[NUM_XADD].num; | ||
309 | |||
310 | rb->lcd_clear_display(); | ||
311 | #ifdef USE_CLOCK | ||
312 | addclock(); | ||
313 | #endif | ||
314 | if(timeout) { | ||
315 | switch(b) { | ||
316 | case BUTTON_LEFT: | ||
317 | values[show].num--; | ||
318 | break; | ||
319 | case BUTTON_RIGHT: | ||
320 | values[show].num++; | ||
321 | break; | ||
322 | case BUTTON_UP: | ||
323 | if(++show == NUM_LAST) | ||
324 | show=0; | ||
325 | break; | ||
326 | case BUTTON_DOWN: | ||
327 | if(--show < 0) | ||
328 | show=NUM_LAST-1; | ||
329 | break; | ||
330 | } | ||
331 | rb->snprintf(buffer, 30, "%s: %d", | ||
332 | values[show].what, values[show].num); | ||
333 | rb->lcd_putsxy(0, 56, buffer); | ||
334 | timeout--; | ||
335 | } | ||
336 | for(i=0, yy=y, xx=x; | ||
337 | i<sizeof(rock)-1; | ||
338 | i++, yy+=values[NUM_YDIST].num, xx+=values[NUM_XDIST].num) | ||
339 | rb->lcd_bitmap((char *)char_gen_12x16[rock[i]-0x20], | ||
340 | xtable[xx%71], table[yy&63], | ||
341 | 11, 16, false); | ||
342 | rb->lcd_update(); | ||
343 | |||
344 | ysanke+= values[NUM_YSANKE].num; | ||
345 | xsanke+= values[NUM_XSANKE].num; | ||
346 | } | ||
347 | } | ||
348 | |||
349 | |||
350 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | ||
351 | { | ||
352 | int w, h; | ||
353 | char *off = "[Off] to stop"; | ||
354 | int len; | ||
355 | |||
356 | TEST_PLUGIN_API(api); | ||
357 | (void)(parameter); | ||
358 | rb = api; | ||
359 | |||
360 | len = rb->strlen(SS_TITLE); | ||
361 | rb->lcd_setfont(FONT_SYSFIXED); | ||
362 | rb->lcd_getstringsize(SS_TITLE,&w, &h); | ||
363 | |||
364 | /* Get horizontel centering for text */ | ||
365 | len *= w; | ||
366 | if (len%2 != 0) | ||
367 | len = ((len+1)/2)+(w/2); | ||
368 | else | ||
369 | len /= 2; | ||
370 | |||
371 | if (h%2 != 0) | ||
372 | h = (h/2)+1; | ||
373 | else | ||
374 | h /= 2; | ||
375 | |||
376 | rb->lcd_clear_display(); | ||
377 | rb->lcd_putsxy(LCD_WIDTH/2-len, (LCD_HEIGHT/2)-h, SS_TITLE); | ||
378 | |||
379 | len = 1; | ||
380 | rb->lcd_getstringsize(off, &w, &h); | ||
381 | |||
382 | /* Get horizontel centering for text */ | ||
383 | len *= w; | ||
384 | if (len%2 != 0) | ||
385 | len = ((len+1)/2)+(w/2); | ||
386 | else | ||
387 | len /= 2; | ||
388 | |||
389 | if (h%2 != 0) | ||
390 | h = (h/2)+1; | ||
391 | else | ||
392 | h /= 2; | ||
393 | |||
394 | rb->lcd_putsxy(LCD_WIDTH/2-len, LCD_HEIGHT-(2*h), off); | ||
395 | rb->lcd_update(); | ||
396 | rb->sleep(HZ); | ||
397 | |||
398 | do { | ||
399 | h= loopit(); | ||
400 | if(h) | ||
401 | h = scrollit(); | ||
402 | } while(h); | ||
403 | |||
404 | rb->lcd_setfont(FONT_UI); | ||
405 | |||
406 | return false; | ||
407 | } | ||
408 | |||
409 | #endif | ||
diff --git a/apps/plugins/cube.c b/apps/plugins/cube.c new file mode 100644 index 0000000000..996a1a81dd --- /dev/null +++ b/apps/plugins/cube.c | |||
@@ -0,0 +1,338 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Damien Teney | ||
11 | * modified to use int instead of float math by Andreas Zwirtes | ||
12 | * | ||
13 | * All files in this archive are subject to the GNU General Public License. | ||
14 | * See the file COPYING in the source tree root for full license agreement. | ||
15 | * | ||
16 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
17 | * KIND, either express or implied. | ||
18 | * | ||
19 | ***************************************************************************/ | ||
20 | #include "plugin.h" | ||
21 | |||
22 | #ifdef HAVE_LCD_BITMAP | ||
23 | |||
24 | /* Loops that the values are displayed */ | ||
25 | #define DISP_TIME 30 | ||
26 | |||
27 | struct point_3D { | ||
28 | long x, y, z; | ||
29 | }; | ||
30 | |||
31 | struct point_2D { | ||
32 | long x, y; | ||
33 | }; | ||
34 | |||
35 | static struct point_3D sommet[8]; | ||
36 | static struct point_3D point3D[8]; | ||
37 | static struct point_2D point2D[8]; | ||
38 | |||
39 | static long matrice[3][3]; | ||
40 | |||
41 | static int nb_points = 8; | ||
42 | |||
43 | static int x_off = 56; | ||
44 | static int y_off = 95; | ||
45 | static int z_off = 600; | ||
46 | |||
47 | /* Precalculated sine and cosine * 10000 (four digit fixed point math) */ | ||
48 | static int sin_table[91] = | ||
49 | { | ||
50 | 0, 174, 348, 523, 697, | ||
51 | 871,1045,1218,1391,1564, | ||
52 | 1736,1908,2079,2249,2419, | ||
53 | 2588,2756,2923,3090,3255, | ||
54 | 3420,3583,3746,3907,4067, | ||
55 | 4226,4383,4539,4694,4848, | ||
56 | 5000,5150,5299,5446,5591, | ||
57 | 5735,5877,6018,6156,6293, | ||
58 | 6427,6560,6691,6819,6946, | ||
59 | 7071,7193,7313,7431,7547, | ||
60 | 7660,7771,7880,7986,8090, | ||
61 | 8191,8290,8386,8480,8571, | ||
62 | 8660,8746,8829,8910,8987, | ||
63 | 9063,9135,9205,9271,9335, | ||
64 | 9396,9455,9510,9563,9612, | ||
65 | 9659,9702,9743,9781,9816, | ||
66 | 9848,9876,9902,9925,9945, | ||
67 | 9961,9975,9986,9993,9998, | ||
68 | 10000 | ||
69 | }; | ||
70 | |||
71 | static struct plugin_api* rb; | ||
72 | |||
73 | static long sin(int val) | ||
74 | { | ||
75 | /* Speed improvement through sukzessive lookup */ | ||
76 | if (val<181) | ||
77 | { | ||
78 | if (val<91) | ||
79 | { | ||
80 | /* phase 0-90 degree */ | ||
81 | return (long)sin_table[val]; | ||
82 | } | ||
83 | else | ||
84 | { | ||
85 | /* phase 91-180 degree */ | ||
86 | return (long)sin_table[180-val]; | ||
87 | } | ||
88 | } | ||
89 | else | ||
90 | { | ||
91 | if (val<271) | ||
92 | { | ||
93 | /* phase 181-270 degree */ | ||
94 | return (-1L)*(long)sin_table[val-180]; | ||
95 | } | ||
96 | else | ||
97 | { | ||
98 | /* phase 270-359 degree */ | ||
99 | return (-1L)*(long)sin_table[360-val]; | ||
100 | } | ||
101 | } | ||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | static long cos(int val) | ||
106 | { | ||
107 | /* Speed improvement through sukzessive lookup */ | ||
108 | if (val<181) | ||
109 | { | ||
110 | if (val<91) | ||
111 | { | ||
112 | /* phase 0-90 degree */ | ||
113 | return (long)sin_table[90-val]; | ||
114 | } | ||
115 | else | ||
116 | { | ||
117 | /* phase 91-180 degree */ | ||
118 | return (-1L)*(long)sin_table[val-90]; | ||
119 | } | ||
120 | } | ||
121 | else | ||
122 | { | ||
123 | if (val<271) | ||
124 | { | ||
125 | /* phase 181-270 degree */ | ||
126 | return (-1L)*(long)sin_table[270-val]; | ||
127 | } | ||
128 | else | ||
129 | { | ||
130 | /* phase 270-359 degree */ | ||
131 | return (long)sin_table[val-270]; | ||
132 | } | ||
133 | } | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | |||
138 | static void cube_rotate(int xa, int ya, int za) | ||
139 | { | ||
140 | int i; | ||
141 | |||
142 | /* Just to prevent unnecessary lookups */ | ||
143 | long sxa,cxa,sya,cya,sza,cza; | ||
144 | sxa=sin(xa); | ||
145 | cxa=cos(xa); | ||
146 | sya=sin(ya); | ||
147 | cya=cos(ya); | ||
148 | sza=sin(za); | ||
149 | cza=cos(za); | ||
150 | |||
151 | /* calculate overall translation matrix */ | ||
152 | matrice[0][0] = cza*cya/10000L; | ||
153 | matrice[1][0] = sza*cya/10000L; | ||
154 | matrice[2][0] = -sya; | ||
155 | |||
156 | matrice[0][1] = cza*sya/10000L*sxa/10000L - sza*cxa/10000L; | ||
157 | matrice[1][1] = sza*sya/10000L*sxa/10000L + cxa*cza/10000L; | ||
158 | matrice[2][1] = sxa*cya/10000L; | ||
159 | |||
160 | matrice[0][2] = cza*sya/10000L*cxa/10000L + sza*sxa/10000L; | ||
161 | matrice[1][2] = sza*sya/10000L*cxa/10000L - cza*sxa/10000L; | ||
162 | matrice[2][2] = cxa*cya/10000L; | ||
163 | |||
164 | /* apply translation matrix to all points */ | ||
165 | for(i=0;i<nb_points;i++) | ||
166 | { | ||
167 | point3D[i].x = matrice[0][0]*sommet[i].x + matrice[1][0]*sommet[i].y | ||
168 | + matrice[2][0]*sommet[i].z; | ||
169 | |||
170 | point3D[i].y = matrice[0][1]*sommet[i].x + matrice[1][1]*sommet[i].y | ||
171 | + matrice[2][1]*sommet[i].z; | ||
172 | |||
173 | point3D[i].z = matrice[0][2]*sommet[i].x + matrice[1][2]*sommet[i].y | ||
174 | + matrice[2][2]*sommet[i].z; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | static void cube_viewport(void) | ||
179 | { | ||
180 | int i; | ||
181 | |||
182 | /* Do viewport transformation for all points */ | ||
183 | for(i=0;i<nb_points;i++) | ||
184 | { | ||
185 | point2D[i].x=(((point3D[i].x)<<8)/10000L)/ | ||
186 | (point3D[i].z/10000L+z_off)+x_off; | ||
187 | point2D[i].y=(((point3D[i].y)<<8)/10000L)/ | ||
188 | (point3D[i].z/10000L+z_off)+y_off; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | static void cube_init(void) | ||
193 | { | ||
194 | /* Original 3D-position of cube's corners */ | ||
195 | sommet[0].x = -40; sommet[0].y = -40; sommet[0].z = -40; | ||
196 | sommet[1].x = 40; sommet[1].y = -40; sommet[1].z = -40; | ||
197 | sommet[2].x = 40; sommet[2].y = 40; sommet[2].z = -40; | ||
198 | sommet[3].x = -40; sommet[3].y = 40; sommet[3].z = -40; | ||
199 | sommet[4].x = 40; sommet[4].y = -40; sommet[4].z = 40; | ||
200 | sommet[5].x = -40; sommet[5].y = -40; sommet[5].z = 40; | ||
201 | sommet[6].x = -40; sommet[6].y = 40; sommet[6].z = 40; | ||
202 | sommet[7].x = 40; sommet[7].y = 40; sommet[7].z = 40; | ||
203 | } | ||
204 | |||
205 | static void line(int a, int b) | ||
206 | { | ||
207 | rb->lcd_drawline(point2D[a].x, point2D[a].y, point2D[b].x, point2D[b].y); | ||
208 | } | ||
209 | |||
210 | static void cube_draw(void) | ||
211 | { | ||
212 | /* Draws front face */ | ||
213 | line(0,1); line(1,2); | ||
214 | line(2,3); line(3,0); | ||
215 | |||
216 | /* Draws rear face */ | ||
217 | line(4,5); line(5,6); | ||
218 | line(6,7); line(7,4); | ||
219 | |||
220 | /* Draws the other edges */ | ||
221 | line(0,5); | ||
222 | line(1,4); | ||
223 | line(2,7); | ||
224 | line(3,6); | ||
225 | } | ||
226 | |||
227 | |||
228 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | ||
229 | { | ||
230 | int t_disp=0; | ||
231 | char buffer[30]; | ||
232 | |||
233 | int xa=0; | ||
234 | int ya=0; | ||
235 | int za=0; | ||
236 | int xs=1; | ||
237 | int ys=3; | ||
238 | int zs=1; | ||
239 | bool highspeed=0; | ||
240 | bool exit=0; | ||
241 | |||
242 | TEST_PLUGIN_API(api); | ||
243 | (void)(parameter); | ||
244 | rb = api; | ||
245 | |||
246 | rb->lcd_setfont(FONT_SYSFIXED); | ||
247 | |||
248 | cube_init(); | ||
249 | |||
250 | while(!exit) | ||
251 | { | ||
252 | if (!highspeed) | ||
253 | rb->sleep(4); | ||
254 | |||
255 | rb->lcd_clear_display(); | ||
256 | cube_rotate(xa,ya,za); | ||
257 | cube_viewport(); | ||
258 | cube_draw(); | ||
259 | if (t_disp>0) | ||
260 | { | ||
261 | t_disp--; | ||
262 | rb->snprintf(buffer, 30, "x:%d y:%d z:%d h:%d",xs,ys,zs,highspeed); | ||
263 | rb->lcd_putsxy(0, 56, buffer); | ||
264 | } | ||
265 | rb->lcd_update(); | ||
266 | |||
267 | xa+=xs; | ||
268 | if (xa>359) | ||
269 | xa-=360; | ||
270 | if (xa<0) | ||
271 | xa+=360; | ||
272 | ya+=ys; | ||
273 | if (ya>359) | ||
274 | ya-=360; | ||
275 | if (ya<0) | ||
276 | ya+=360; | ||
277 | za+=zs; | ||
278 | if (za>359) | ||
279 | za-=360; | ||
280 | if (za<0) | ||
281 | za+=360; | ||
282 | |||
283 | switch(rb->button_get(false)) | ||
284 | { | ||
285 | case BUTTON_RIGHT: | ||
286 | xs+=1; | ||
287 | if (xs>10) | ||
288 | xs=10; | ||
289 | t_disp=DISP_TIME; | ||
290 | break; | ||
291 | case BUTTON_LEFT: | ||
292 | xs-=1; | ||
293 | if (xs<-10) | ||
294 | xs=-10; | ||
295 | t_disp=DISP_TIME; | ||
296 | break; | ||
297 | case BUTTON_UP: | ||
298 | ys+=1; | ||
299 | if (ys>10) | ||
300 | ys=10; | ||
301 | t_disp=DISP_TIME; | ||
302 | break; | ||
303 | case BUTTON_DOWN: | ||
304 | ys-=1; | ||
305 | if (ys<-10) | ||
306 | ys=-10; | ||
307 | t_disp=DISP_TIME; | ||
308 | break; | ||
309 | case BUTTON_F2: | ||
310 | zs+=1; | ||
311 | if (zs>10) | ||
312 | zs=10; | ||
313 | t_disp=DISP_TIME; | ||
314 | break; | ||
315 | case BUTTON_F1: | ||
316 | zs-=1; | ||
317 | if (zs<-10) | ||
318 | zs=-10; | ||
319 | t_disp=DISP_TIME; | ||
320 | break; | ||
321 | case BUTTON_PLAY: | ||
322 | highspeed=!highspeed; | ||
323 | t_disp=DISP_TIME; | ||
324 | break; | ||
325 | case BUTTON_OFF|BUTTON_REL: | ||
326 | exit=1; | ||
327 | break; | ||
328 | |||
329 | case SYS_USB_CONNECTED: | ||
330 | rb->usb_screen(); | ||
331 | return PLUGIN_USB_CONNECTED; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | return PLUGIN_OK; | ||
336 | } | ||
337 | |||
338 | #endif | ||
diff --git a/apps/plugins/helloworld.c b/apps/plugins/helloworld.c new file mode 100644 index 0000000000..ea347fbf79 --- /dev/null +++ b/apps/plugins/helloworld.c | |||
@@ -0,0 +1,48 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Björn Stenberg | ||
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 | #include "plugin.h" | ||
20 | |||
21 | /* welcome to the example rockbox plugin */ | ||
22 | |||
23 | /* here is a global api struct pointer. while not strictly necessary, | ||
24 | it's nice not to have to pass the api pointer in all function calls | ||
25 | in the plugin */ | ||
26 | static struct plugin_api* rb; | ||
27 | |||
28 | /* this is the plugin entry point */ | ||
29 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | ||
30 | { | ||
31 | /* this macro should be called as the first thing you do in the plugin. | ||
32 | it test that the api version and model the plugin was compiled for | ||
33 | matches the machine it is running on */ | ||
34 | TEST_PLUGIN_API(api); | ||
35 | |||
36 | /* if you don't use the parameter, you can do like | ||
37 | this to avoid the compiler warning about it */ | ||
38 | (void)parameter; | ||
39 | |||
40 | /* if you are using a global api pointer, don't forget to copy it! | ||
41 | otherwise you will get lovely "I04: IllInstr" errors... :-) */ | ||
42 | rb = api; | ||
43 | |||
44 | /* now go ahead and have fun! */ | ||
45 | rb->splash(HZ*2, 0, true, "Hello world!"); | ||
46 | |||
47 | return PLUGIN_OK; | ||
48 | } | ||
diff --git a/apps/plugins/oscillograph.c b/apps/plugins/oscillograph.c new file mode 100644 index 0000000000..a34aa8bfa9 --- /dev/null +++ b/apps/plugins/oscillograph.c | |||
@@ -0,0 +1,207 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Philipp Pertermann | ||
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 | #include "plugin.h" | ||
20 | |||
21 | #ifdef HAVE_LCD_BITMAP | ||
22 | #ifndef SIMULATOR /* don't want this code in the simulator */ | ||
23 | |||
24 | /* The different drawing modes */ | ||
25 | #define DRAW_MODE_FILLED 0 | ||
26 | #define DRAW_MODE_OUTLINE 1 | ||
27 | #define DRAW_MODE_PIXEL 2 | ||
28 | #define DRAW_MODE_COUNT 3 | ||
29 | |||
30 | #define MAX_PEAK 0x8000 | ||
31 | |||
32 | /* number of ticks between two volume samples */ | ||
33 | static int speed = 1; | ||
34 | /* roll == true -> lcd rolls */ | ||
35 | static bool roll = true; | ||
36 | /* see DRAW_MODE_XXX constants for valid values */ | ||
37 | static int drawMode = DRAW_MODE_FILLED; | ||
38 | |||
39 | /** | ||
40 | * Displays a vertically scrolling oscillosgraph using | ||
41 | * hardware scrolling of the display. The user can change | ||
42 | * speed | ||
43 | */ | ||
44 | enum plugin_status plugin_start(struct plugin_api* rb, void* parameter) | ||
45 | { | ||
46 | /* stores current volume value left */ | ||
47 | int left; | ||
48 | /* stores current volume value right */ | ||
49 | int right; | ||
50 | /* specifies the current position on the lcd */ | ||
51 | int y = LCD_WIDTH - 1; | ||
52 | |||
53 | /* only needed when drawing lines */ | ||
54 | int lastLeft = 0; | ||
55 | int lastRight = 0; | ||
56 | int lasty = 0; | ||
57 | |||
58 | bool exit = false; | ||
59 | |||
60 | TEST_PLUGIN_API(rb); | ||
61 | (void)parameter; | ||
62 | |||
63 | /* the main loop */ | ||
64 | while (!exit) { | ||
65 | |||
66 | /* read the volume info from MAS */ | ||
67 | left = rb->mas_codec_readreg(0xC) / (MAX_PEAK / (LCD_WIDTH / 2 - 2)); | ||
68 | right = rb->mas_codec_readreg(0xD) / (MAX_PEAK / (LCD_WIDTH / 2 - 2)); | ||
69 | |||
70 | /* delete current line */ | ||
71 | rb->lcd_clearline(0, y, LCD_WIDTH-1, y); | ||
72 | |||
73 | switch (drawMode) { | ||
74 | case DRAW_MODE_FILLED: | ||
75 | rb->lcd_drawline(LCD_WIDTH / 2 + 1 , y, | ||
76 | LCD_WIDTH / 2 + 1 + right, y); | ||
77 | rb->lcd_drawline(LCD_WIDTH / 2 - 1 , y, | ||
78 | LCD_WIDTH / 2 - 1 -left , y); | ||
79 | break; | ||
80 | |||
81 | case DRAW_MODE_OUTLINE: | ||
82 | /* last position needed for lines */ | ||
83 | lasty = MAX(y-1, 0); | ||
84 | |||
85 | /* Here real lines were neccessary because | ||
86 | anything else was ugly. */ | ||
87 | rb->lcd_drawline(LCD_WIDTH / 2 + right , y, | ||
88 | LCD_WIDTH / 2 + lastRight , lasty); | ||
89 | rb->lcd_drawline(LCD_WIDTH / 2 - left , y, | ||
90 | LCD_WIDTH / 2 - lastLeft, lasty); | ||
91 | |||
92 | /* have to store the old values for drawing lines | ||
93 | the next time */ | ||
94 | lastRight = right; | ||
95 | lastLeft = left; | ||
96 | break; | ||
97 | |||
98 | case DRAW_MODE_PIXEL: | ||
99 | /* straight and simple */ | ||
100 | rb->lcd_drawpixel(LCD_WIDTH / 2 + right, y); | ||
101 | rb->lcd_drawpixel(LCD_WIDTH / 2 - left, y); | ||
102 | break; | ||
103 | } | ||
104 | |||
105 | |||
106 | /* increment and adjust the drawing position */ | ||
107 | y++; | ||
108 | if (y >= LCD_HEIGHT) | ||
109 | y = 0; | ||
110 | |||
111 | /* I roll before update because otherwise the new | ||
112 | line would appear at the wrong end of the display */ | ||
113 | if (roll) | ||
114 | rb->lcd_roll(y); | ||
115 | |||
116 | /* now finally make the new sample visible */ | ||
117 | rb->lcd_update_rect(0, MAX(y-1, 0), LCD_WIDTH, 2); | ||
118 | |||
119 | /* There are two mechanisms to alter speed: | ||
120 | 1.) slowing down is achieved by increasing | ||
121 | the time waiting for user input. This | ||
122 | mechanism uses positive values. | ||
123 | 2.) speeding up is achieved by leaving out | ||
124 | the user input check for (-speed) volume | ||
125 | samples. For this mechanism negative values | ||
126 | are used. | ||
127 | */ | ||
128 | |||
129 | if (speed >= 0 || ((speed < 0) && (y % (-speed) == 0))) { | ||
130 | bool draw = false; | ||
131 | |||
132 | /* speed values > 0 slow the oszi down. By user input | ||
133 | speed might become < 1. If a value < 1 was | ||
134 | passed user input would be disabled. Thus | ||
135 | it must be ensured that at least 1 is passed. */ | ||
136 | |||
137 | /* react to user input */ | ||
138 | switch (rb->button_get_w_tmo(MAX(speed, 1))) { | ||
139 | case BUTTON_UP: | ||
140 | speed++; | ||
141 | draw = true; | ||
142 | break; | ||
143 | |||
144 | case BUTTON_DOWN: | ||
145 | speed--; | ||
146 | draw = true; | ||
147 | break; | ||
148 | |||
149 | case BUTTON_PLAY: | ||
150 | /* pause the demo */ | ||
151 | rb->button_get(true); | ||
152 | break; | ||
153 | |||
154 | case BUTTON_F1: | ||
155 | /* toggle rolling */ | ||
156 | roll = !roll; | ||
157 | break; | ||
158 | |||
159 | case BUTTON_F2: | ||
160 | /* step through the display modes */ | ||
161 | drawMode ++; | ||
162 | drawMode = drawMode % DRAW_MODE_COUNT; | ||
163 | |||
164 | /* lcd buffer might be rolled so that | ||
165 | the transition from LCD_HEIGHT to 0 | ||
166 | takes place in the middle of the screen. | ||
167 | That produces ugly results in DRAW_MODE_OUTLINE | ||
168 | mode. If rolling is enabled this change will | ||
169 | be reverted before the next update anyway.*/ | ||
170 | rb->lcd_roll(0); | ||
171 | break; | ||
172 | |||
173 | case BUTTON_F3: | ||
174 | speed = 1; | ||
175 | draw = true; | ||
176 | break; | ||
177 | |||
178 | case BUTTON_OFF: | ||
179 | exit = true; | ||
180 | break; | ||
181 | |||
182 | case SYS_USB_CONNECTED: | ||
183 | rb->usb_screen(); | ||
184 | return PLUGIN_USB_CONNECTED; | ||
185 | } | ||
186 | |||
187 | if (draw) { | ||
188 | char buf[16]; | ||
189 | rb->snprintf(buf, sizeof buf, "Speed: %d", -speed); | ||
190 | rb->lcd_putsxy(0, (y + LCD_HEIGHT - 8) % LCD_HEIGHT, buf); | ||
191 | rb->lcd_update_rect(0, (y + LCD_HEIGHT - 8) % LCD_HEIGHT, | ||
192 | LCD_WIDTH, 8); | ||
193 | } | ||
194 | } | ||
195 | } | ||
196 | |||
197 | /* restore to default roll position. | ||
198 | Looks funny if you forget to do this... */ | ||
199 | rb->lcd_roll(0); | ||
200 | rb->lcd_update(); | ||
201 | |||
202 | /* standard return */ | ||
203 | return PLUGIN_OK; | ||
204 | } | ||
205 | |||
206 | #endif /* #ifndef SIMULATOR */ | ||
207 | #endif | ||
diff --git a/apps/plugins/plugin.lds b/apps/plugins/plugin.lds new file mode 100644 index 0000000000..be6b6fd0a6 --- /dev/null +++ b/apps/plugins/plugin.lds | |||
@@ -0,0 +1,26 @@ | |||
1 | OUTPUT_FORMAT(elf32-sh) | ||
2 | |||
3 | MEMORY | ||
4 | { | ||
5 | PLUGIN_RAM : ORIGIN = 0x091f8000, LENGTH = 0x8000 | ||
6 | } | ||
7 | |||
8 | SECTIONS | ||
9 | { | ||
10 | .text : { | ||
11 | *(.entry) | ||
12 | *(.text) | ||
13 | } > PLUGIN_RAM | ||
14 | |||
15 | .data : { | ||
16 | *(.data) | ||
17 | } > PLUGIN_RAM | ||
18 | |||
19 | .bss : { | ||
20 | *(.bss) | ||
21 | } > PLUGIN_RAM | ||
22 | |||
23 | .rodata : { | ||
24 | *(.rodata) | ||
25 | } > PLUGIN_RAM | ||
26 | } | ||
diff --git a/apps/plugins/snow.c b/apps/plugins/snow.c new file mode 100644 index 0000000000..df9966eb38 --- /dev/null +++ b/apps/plugins/snow.c | |||
@@ -0,0 +1,111 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Itai Shaked | ||
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 | #include "plugin.h" | ||
20 | |||
21 | #ifdef HAVE_LCD_BITMAP | ||
22 | |||
23 | #define NUM_PARTICLES 100 | ||
24 | |||
25 | static short particles[NUM_PARTICLES][2]; | ||
26 | static struct plugin_api* rb; | ||
27 | |||
28 | static bool particle_exists(int particle) | ||
29 | { | ||
30 | if (particles[particle][0]>=0 && particles[particle][1]>=0 && | ||
31 | particles[particle][0]<112 && particles[particle][1]<64) | ||
32 | return true; | ||
33 | else | ||
34 | return false; | ||
35 | } | ||
36 | |||
37 | static int create_particle(void) | ||
38 | { | ||
39 | int i; | ||
40 | |||
41 | for (i=0; i<NUM_PARTICLES; i++) { | ||
42 | if (!particle_exists(i)) { | ||
43 | particles[i][0]=(rb->rand()%112); | ||
44 | particles[i][1]=0; | ||
45 | return i; | ||
46 | } | ||
47 | } | ||
48 | return -1; | ||
49 | } | ||
50 | |||
51 | static void snow_move(void) | ||
52 | { | ||
53 | int i; | ||
54 | |||
55 | if (!(rb->rand()%2)) | ||
56 | create_particle(); | ||
57 | |||
58 | for (i=0; i<NUM_PARTICLES; i++) { | ||
59 | if (particle_exists(i)) { | ||
60 | rb->lcd_clearpixel(particles[i][0],particles[i][1]); | ||
61 | switch ((rb->rand()%7)) { | ||
62 | case 0: | ||
63 | particles[i][0]++; | ||
64 | break; | ||
65 | |||
66 | case 1: | ||
67 | particles[i][0]--; | ||
68 | break; | ||
69 | |||
70 | case 2: | ||
71 | break; | ||
72 | |||
73 | default: | ||
74 | particles[i][1]++; | ||
75 | break; | ||
76 | } | ||
77 | if (particle_exists(i)) | ||
78 | rb->lcd_drawpixel(particles[i][0],particles[i][1]); | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | |||
83 | static void snow_init(void) | ||
84 | { | ||
85 | int i; | ||
86 | |||
87 | for (i=0; i<NUM_PARTICLES; i++) { | ||
88 | particles[i][0]=-1; | ||
89 | particles[i][1]=-1; | ||
90 | } | ||
91 | rb->lcd_clear_display(); | ||
92 | } | ||
93 | |||
94 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | ||
95 | { | ||
96 | TEST_PLUGIN_API(api); | ||
97 | (void)(parameter); | ||
98 | rb = api; | ||
99 | |||
100 | snow_init(); | ||
101 | while (1) { | ||
102 | snow_move(); | ||
103 | rb->lcd_update(); | ||
104 | rb->sleep(HZ/20); | ||
105 | |||
106 | if (rb->button_get(false) == BUTTON_OFF) | ||
107 | return false; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | #endif | ||
diff --git a/apps/plugins/sokoban.c b/apps/plugins/sokoban.c new file mode 100644 index 0000000000..2387fa9517 --- /dev/null +++ b/apps/plugins/sokoban.c | |||
@@ -0,0 +1,868 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Eric Linenberg | ||
11 | * February 2003: Robert Hak performs a cleanup/rewrite/feature addition. | ||
12 | * Eric smiles. Bjorn cries. Linus say 'huh?'. | ||
13 | * | ||
14 | * All files in this archive are subject to the GNU General Public License. | ||
15 | * See the file COPYING in the source tree root for full license agreement. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "plugin.h" | ||
22 | |||
23 | #ifdef HAVE_LCD_BITMAP | ||
24 | |||
25 | #define SOKOBAN_TITLE "Sokoban" | ||
26 | #define SOKOBAN_TITLE_FONT 2 | ||
27 | |||
28 | #define LEVELS_FILE "/.rockbox/sokoban/levels.txt" | ||
29 | |||
30 | #define ROWS 16 | ||
31 | #define COLS 20 | ||
32 | #define MAX_UNDOS 5 | ||
33 | |||
34 | #define SOKOBAN_LEVEL_SIZE (ROWS*COLS) | ||
35 | |||
36 | static void init_undo(void); | ||
37 | static void undo(void); | ||
38 | static void add_undo(int button); | ||
39 | |||
40 | static int get_level(char *level, int level_size); | ||
41 | static int get_level_count(void); | ||
42 | static int load_level(void); | ||
43 | static void draw_level(void); | ||
44 | |||
45 | static void init_boards(void); | ||
46 | static void update_screen(void); | ||
47 | static bool sokoban_loop(void); | ||
48 | |||
49 | /* The Location, Undo and LevelInfo structs are OO-flavored. | ||
50 | * (oooh!-flavored as Schnueff puts it.) It makes more you have to know, | ||
51 | * but the overall data layout becomes more manageable. */ | ||
52 | |||
53 | /* We use the same three values in 2 structs. Makeing them a struct | ||
54 | * hopefully ensures that if you change things in one, the other changes | ||
55 | * as well. */ | ||
56 | struct LevelInfo { | ||
57 | short level; | ||
58 | short moves; | ||
59 | short boxes_to_go; | ||
60 | }; | ||
61 | |||
62 | /* What a given location on the board looks like at a given time */ | ||
63 | struct Location { | ||
64 | char spot; | ||
65 | short row; | ||
66 | short col; | ||
67 | }; | ||
68 | |||
69 | /* A single level of undo. Each undo move can affect upto, | ||
70 | * but not more then, 3 spots on the board */ | ||
71 | struct Undo { | ||
72 | struct LevelInfo level; | ||
73 | struct Location location[3]; | ||
74 | }; | ||
75 | |||
76 | /* Our full undo history */ | ||
77 | static struct UndoInfo { | ||
78 | short count; /* How many undos are there in history */ | ||
79 | short current; /* Which history is the current undo */ | ||
80 | struct Undo history[MAX_UNDOS]; | ||
81 | } undo_info; | ||
82 | |||
83 | /* Our playing board */ | ||
84 | static struct BoardInfo { | ||
85 | char board[ROWS][COLS]; | ||
86 | struct LevelInfo level; | ||
87 | struct Location player; | ||
88 | int max_level; /* How many levels do we have? */ | ||
89 | int level_offset; /* Where in the level file is this level */ | ||
90 | int loaded_level; /* Which level is in memory */ | ||
91 | } current_info; | ||
92 | |||
93 | static struct plugin_api* rb; | ||
94 | |||
95 | static void init_undo(void) | ||
96 | { | ||
97 | undo_info.count = 0; | ||
98 | undo_info.current = 0; | ||
99 | } | ||
100 | |||
101 | static void undo(void) | ||
102 | { | ||
103 | struct Undo *undo; | ||
104 | int i = 0; | ||
105 | short row, col; | ||
106 | |||
107 | if (undo_info.count == 0) | ||
108 | return; | ||
109 | |||
110 | /* Update board info */ | ||
111 | undo = &undo_info.history[undo_info.current]; | ||
112 | |||
113 | rb->memcpy(¤t_info.level, &undo->level, sizeof(undo->level)); | ||
114 | rb->memcpy(¤t_info.player, &undo->location[0], sizeof(undo->location[0])); | ||
115 | |||
116 | row = undo->location[0].row; | ||
117 | col = undo->location[0].col; | ||
118 | current_info.board[row][col] = '@'; | ||
119 | |||
120 | /* Update the two other possible spots */ | ||
121 | for (i = 1; i < 3; i++) { | ||
122 | if (undo->location[i].spot != '\0') { | ||
123 | row = undo->location[i].row; | ||
124 | col = undo->location[i].col; | ||
125 | current_info.board[row][col] = undo->location[i].spot; | ||
126 | undo->location[i].spot = '\0'; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | /* Remove this undo from the list */ | ||
131 | if (undo_info.current == 0) { | ||
132 | if (undo_info.count > 1) | ||
133 | undo_info.current = MAX_UNDOS - 1; | ||
134 | } else { | ||
135 | undo_info.current--; | ||
136 | } | ||
137 | |||
138 | undo_info.count--; | ||
139 | |||
140 | return; | ||
141 | } | ||
142 | |||
143 | static void add_undo(int button) | ||
144 | { | ||
145 | struct Undo *undo; | ||
146 | int row, col, i; | ||
147 | bool storable; | ||
148 | |||
149 | if ((button != BUTTON_LEFT) && (button != BUTTON_RIGHT) && | ||
150 | (button != BUTTON_UP) && (button != BUTTON_DOWN)) | ||
151 | return; | ||
152 | |||
153 | if (undo_info.count != 0) { | ||
154 | if (undo_info.current < (MAX_UNDOS - 1)) | ||
155 | undo_info.current++; | ||
156 | else | ||
157 | undo_info.current = 0; | ||
158 | } | ||
159 | |||
160 | /* Make what follows more readable */ | ||
161 | undo = &undo_info.history[undo_info.current]; | ||
162 | |||
163 | /* Store our level info */ | ||
164 | rb->memcpy(&undo->level, ¤t_info.level, sizeof(undo->level)); | ||
165 | |||
166 | /* Store our player info */ | ||
167 | rb->memcpy(&undo->location[0], ¤t_info.player, sizeof(undo->location[0])); | ||
168 | |||
169 | /* Now we need to store upto 2 blocks that may be affected. | ||
170 | * If player.spot is NULL, then there is no info stored | ||
171 | * for that block */ | ||
172 | |||
173 | row = current_info.player.row; | ||
174 | col = current_info.player.col; | ||
175 | |||
176 | /* This must stay as _1_ because the first block (0) is the player */ | ||
177 | for (i = 1; i < 3; i++) { | ||
178 | storable = true; | ||
179 | |||
180 | switch (button) { | ||
181 | case BUTTON_LEFT: | ||
182 | col--; | ||
183 | if (col < 0) | ||
184 | storable = false; | ||
185 | break; | ||
186 | |||
187 | case BUTTON_RIGHT: | ||
188 | col++; | ||
189 | if (col >= COLS) | ||
190 | storable = false; | ||
191 | break; | ||
192 | |||
193 | case BUTTON_UP: | ||
194 | row--; | ||
195 | if (row < 0) | ||
196 | storable = false; | ||
197 | break; | ||
198 | |||
199 | case BUTTON_DOWN: | ||
200 | row++; | ||
201 | if (row >= ROWS) | ||
202 | storable = false; | ||
203 | break; | ||
204 | |||
205 | default: | ||
206 | return; | ||
207 | } | ||
208 | |||
209 | if (storable) { | ||
210 | undo->location[i].col = col; | ||
211 | undo->location[i].row = row; | ||
212 | undo->location[i].spot = current_info.board[row][col]; | ||
213 | } else { | ||
214 | undo->location[i].spot = '\0'; | ||
215 | } | ||
216 | } | ||
217 | |||
218 | if (undo_info.count < MAX_UNDOS) | ||
219 | undo_info.count++; | ||
220 | } | ||
221 | |||
222 | static void init_boards(void) | ||
223 | { | ||
224 | current_info.level.level = 0; | ||
225 | current_info.level.moves = 0; | ||
226 | current_info.level.boxes_to_go = 0; | ||
227 | current_info.player.row = 0; | ||
228 | current_info.player.col = 0; | ||
229 | current_info.player.spot = ' '; | ||
230 | current_info.max_level = 0; | ||
231 | current_info.level_offset = 0; | ||
232 | current_info.loaded_level = 0; | ||
233 | |||
234 | init_undo(); | ||
235 | } | ||
236 | |||
237 | static int get_level_count(void) | ||
238 | { | ||
239 | int fd = 0; | ||
240 | int lastlen = 0; | ||
241 | char buffer[COLS + 3]; /* COLS plus CR/LF and \0 */ | ||
242 | |||
243 | if ((fd = rb->open(LEVELS_FILE, O_RDONLY)) < 0) { | ||
244 | rb->splash(0, 0, true, "Unable to open %s", LEVELS_FILE); | ||
245 | return -1; | ||
246 | } | ||
247 | |||
248 | while(1) { | ||
249 | int len = rb->read_line(fd, buffer, sizeof(buffer)); | ||
250 | if(len <= 0) | ||
251 | break; | ||
252 | |||
253 | /* Two short lines in a row means new level */ | ||
254 | if(len < 3 && lastlen < 3) | ||
255 | current_info.max_level++; | ||
256 | |||
257 | lastlen = len; | ||
258 | } | ||
259 | |||
260 | rb->close(fd); | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int get_level(char *level, int level_size) | ||
265 | { | ||
266 | int fd = 0, i = 0; | ||
267 | int nread = 0; | ||
268 | int count = 0; | ||
269 | int lastlen = 0; | ||
270 | int level_ct = 1; | ||
271 | unsigned char buffer[SOKOBAN_LEVEL_SIZE * 2]; | ||
272 | bool level_found = false; | ||
273 | |||
274 | /* open file */ | ||
275 | if ((fd = rb->open(LEVELS_FILE, O_RDONLY)) < 0) | ||
276 | return -1; | ||
277 | |||
278 | /* Lets not reparse the full file if we can avoid it */ | ||
279 | if (current_info.loaded_level < current_info.level.level) { | ||
280 | rb->lseek(fd, current_info.level_offset, SEEK_SET); | ||
281 | level_ct = current_info.loaded_level; | ||
282 | } | ||
283 | |||
284 | if(current_info.level.level > 1) { | ||
285 | while(!level_found) { | ||
286 | int len = rb->read_line(fd, buffer, SOKOBAN_LEVEL_SIZE); | ||
287 | if(len <= 0) { | ||
288 | rb->close(fd); | ||
289 | return -1; | ||
290 | } | ||
291 | |||
292 | /* Two short lines in a row means new level */ | ||
293 | if(len < 3 && lastlen < 3) { | ||
294 | level_ct++; | ||
295 | if(level_ct == current_info.level.level) | ||
296 | level_found = true; | ||
297 | } | ||
298 | lastlen = len; | ||
299 | } | ||
300 | } | ||
301 | |||
302 | /* Remember the current offset */ | ||
303 | current_info.level_offset = rb->lseek(fd, 0, SEEK_CUR); | ||
304 | |||
305 | /* read a full buffer chunk from here */ | ||
306 | nread = rb->read(fd, buffer, sizeof(buffer)-1); | ||
307 | if (nread < 0) | ||
308 | return -1; | ||
309 | buffer[nread] = 0; | ||
310 | |||
311 | rb->close(fd); | ||
312 | |||
313 | /* If we read less then a level, error */ | ||
314 | if (nread < level_size) | ||
315 | return -1; | ||
316 | |||
317 | /* Load our new level */ | ||
318 | for(i=0, count=0; (count < nread) && (i<level_size);) { | ||
319 | if (buffer[count] != '\n' && buffer[count] != '\r') | ||
320 | level[i++] = buffer[count]; | ||
321 | count++; | ||
322 | } | ||
323 | level[i] = 0; | ||
324 | |||
325 | current_info.loaded_level = current_info.level.level; | ||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | /* return non-zero on error */ | ||
330 | static int load_level(void) | ||
331 | { | ||
332 | short c = 0; | ||
333 | short r = 0; | ||
334 | short i = 0; | ||
335 | char level[ROWS*COLS+1]; | ||
336 | int x = 0; | ||
337 | |||
338 | current_info.player.spot=' '; | ||
339 | current_info.level.boxes_to_go = 0; | ||
340 | current_info.level.moves = 0; | ||
341 | |||
342 | if (get_level(level, sizeof(level)) != 0) | ||
343 | return -1; | ||
344 | |||
345 | i = 0; | ||
346 | for (r = 0; r < ROWS; r++) { | ||
347 | x++; | ||
348 | for (c = 0; c < COLS; c++, i++) { | ||
349 | current_info.board[r][c] = level[i]; | ||
350 | |||
351 | if (current_info.board[r][c] == '.') | ||
352 | current_info.level.boxes_to_go++; | ||
353 | |||
354 | else if (current_info.board[r][c] == '@') { | ||
355 | current_info.player.row = r; | ||
356 | current_info.player.col = c; | ||
357 | } | ||
358 | } | ||
359 | } | ||
360 | |||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | static void update_screen(void) | ||
365 | { | ||
366 | short b = 0, c = 0; | ||
367 | short rows = 0, cols = 0; | ||
368 | char s[25]; | ||
369 | |||
370 | short magnify = 4; | ||
371 | |||
372 | /* load the board to the screen */ | ||
373 | for (rows=0 ; rows < ROWS ; rows++) { | ||
374 | for (cols = 0 ; cols < COLS ; cols++) { | ||
375 | c = cols * magnify; | ||
376 | b = rows * magnify; | ||
377 | |||
378 | switch(current_info.board[rows][cols]) { | ||
379 | case 'X': /* black space */ | ||
380 | rb->lcd_drawrect(c, b, magnify, magnify); | ||
381 | rb->lcd_drawrect(c+1, b+1, 2, 2); | ||
382 | break; | ||
383 | |||
384 | case '#': /* this is a wall */ | ||
385 | rb->lcd_drawpixel(c, b); | ||
386 | rb->lcd_drawpixel(c+2, b); | ||
387 | rb->lcd_drawpixel(c+1, b+1); | ||
388 | rb->lcd_drawpixel(c+3, b+1); | ||
389 | rb->lcd_drawpixel(c, b+2); | ||
390 | rb->lcd_drawpixel(c+2, b+2); | ||
391 | rb->lcd_drawpixel(c+1, b+3); | ||
392 | rb->lcd_drawpixel(c+3, b+3); | ||
393 | break; | ||
394 | |||
395 | case '.': /* this is a home location */ | ||
396 | rb->lcd_drawrect(c+1, b+1, 2, 2); | ||
397 | break; | ||
398 | |||
399 | case '$': /* this is a box */ | ||
400 | rb->lcd_drawrect(c, b, magnify, magnify); | ||
401 | break; | ||
402 | |||
403 | case '@': /* this is you */ | ||
404 | rb->lcd_drawline(c+1, b, c+2, b); | ||
405 | rb->lcd_drawline(c, b+1, c+3, b+1); | ||
406 | rb->lcd_drawline(c+1, b+2, c+2, b+2); | ||
407 | |||
408 | rb->lcd_drawpixel(c, b+3); | ||
409 | rb->lcd_drawpixel(c+3, b+3); | ||
410 | break; | ||
411 | |||
412 | case '%': /* this is a box on a home spot */ | ||
413 | rb->lcd_drawrect(c, b, magnify, magnify); | ||
414 | rb->lcd_drawrect(c+1, b+1, 2, 2); | ||
415 | break; | ||
416 | } | ||
417 | } | ||
418 | } | ||
419 | |||
420 | |||
421 | rb->snprintf(s, sizeof(s), "%d", current_info.level.level); | ||
422 | rb->lcd_putsxy(86, 22, s); | ||
423 | rb->snprintf(s, sizeof(s), "%d", current_info.level.moves); | ||
424 | rb->lcd_putsxy(86, 54, s); | ||
425 | |||
426 | rb->lcd_drawrect(80,0,32,32); | ||
427 | rb->lcd_drawrect(80,32,32,64); | ||
428 | rb->lcd_putsxy(81, 10, "Level"); | ||
429 | rb->lcd_putsxy(81, 42, "Moves"); | ||
430 | |||
431 | /* print out the screen */ | ||
432 | rb->lcd_update(); | ||
433 | } | ||
434 | |||
435 | static void draw_level(void) | ||
436 | { | ||
437 | load_level(); | ||
438 | rb->lcd_clear_display(); | ||
439 | update_screen(); | ||
440 | } | ||
441 | |||
442 | static bool sokoban_loop(void) | ||
443 | { | ||
444 | char new_spot; | ||
445 | bool moved = true; | ||
446 | int i = 0, button = 0; | ||
447 | short r = 0, c = 0; | ||
448 | |||
449 | current_info.level.level = 1; | ||
450 | |||
451 | load_level(); | ||
452 | update_screen(); | ||
453 | |||
454 | while (1) { | ||
455 | moved = true; | ||
456 | |||
457 | r = current_info.player.row; | ||
458 | c = current_info.player.col; | ||
459 | |||
460 | button = rb->button_get(true); | ||
461 | |||
462 | add_undo(button); | ||
463 | |||
464 | switch(button) | ||
465 | { | ||
466 | case BUTTON_OFF: | ||
467 | /* get out of here */ | ||
468 | return PLUGIN_OK; | ||
469 | |||
470 | case BUTTON_ON: | ||
471 | case BUTTON_ON | BUTTON_REPEAT: | ||
472 | /* this is UNDO */ | ||
473 | undo(); | ||
474 | rb->lcd_clear_display(); | ||
475 | update_screen(); | ||
476 | moved = false; | ||
477 | break; | ||
478 | |||
479 | case BUTTON_F3: | ||
480 | case BUTTON_F3 | BUTTON_REPEAT: | ||
481 | /* increase level */ | ||
482 | init_undo(); | ||
483 | current_info.level.boxes_to_go=0; | ||
484 | moved = true; | ||
485 | break; | ||
486 | |||
487 | case BUTTON_F1: | ||
488 | case BUTTON_F1 | BUTTON_REPEAT: | ||
489 | /* previous level */ | ||
490 | init_undo(); | ||
491 | if (current_info.level.level > 1) | ||
492 | current_info.level.level--; | ||
493 | |||
494 | draw_level(); | ||
495 | moved = false; | ||
496 | break; | ||
497 | |||
498 | case BUTTON_F2: | ||
499 | case BUTTON_F2 | BUTTON_REPEAT: | ||
500 | /* same level */ | ||
501 | init_undo(); | ||
502 | draw_level(); | ||
503 | moved = false; | ||
504 | break; | ||
505 | |||
506 | case BUTTON_LEFT: | ||
507 | switch(current_info.board[r][c-1]) | ||
508 | { | ||
509 | case ' ': /* if it is a blank spot */ | ||
510 | case '.': /* if it is a home spot */ | ||
511 | new_spot = current_info.board[r][c-1]; | ||
512 | current_info.board[r][c-1] = '@'; | ||
513 | current_info.board[r][c] = current_info.player.spot; | ||
514 | current_info.player.spot = new_spot; | ||
515 | break; | ||
516 | |||
517 | case '$': | ||
518 | switch(current_info.board[r][c-2]) | ||
519 | { | ||
520 | case ' ': /* going from blank to blank */ | ||
521 | current_info.board[r][c-2] = current_info.board[r][c-1]; | ||
522 | current_info.board[r][c-1] = current_info.board[r][c]; | ||
523 | current_info.board[r][c] = current_info.player.spot; | ||
524 | current_info.player.spot = ' '; | ||
525 | break; | ||
526 | |||
527 | case '.': /* going from a blank to home */ | ||
528 | current_info.board[r][c-2] = '%'; | ||
529 | current_info.board[r][c-1] = current_info.board[r][c]; | ||
530 | current_info.board[r][c] = current_info.player.spot; | ||
531 | current_info.player.spot = ' '; | ||
532 | current_info.level.boxes_to_go--; | ||
533 | break; | ||
534 | |||
535 | default: | ||
536 | moved = false; | ||
537 | break; | ||
538 | } | ||
539 | break; | ||
540 | |||
541 | case '%': | ||
542 | switch(current_info.board[r][c-2]) { | ||
543 | case ' ': /* we are going from a home to a blank */ | ||
544 | current_info.board[r][c-2] = '$'; | ||
545 | current_info.board[r][c-1] = current_info.board[r][c]; | ||
546 | current_info.board[r][c] = current_info.player.spot; | ||
547 | current_info.player.spot = '.'; | ||
548 | current_info.level.boxes_to_go++; | ||
549 | break; | ||
550 | |||
551 | case '.': /* if we are going from a home to home */ | ||
552 | current_info.board[r][c-2] = '%'; | ||
553 | current_info.board[r][c-1] = current_info.board[r][c]; | ||
554 | current_info.board[r][c] = current_info.player.spot; | ||
555 | current_info.player.spot = '.'; | ||
556 | break; | ||
557 | |||
558 | default: | ||
559 | moved = false; | ||
560 | break; | ||
561 | } | ||
562 | break; | ||
563 | |||
564 | default: | ||
565 | moved = false; | ||
566 | break; | ||
567 | } | ||
568 | |||
569 | if (moved) | ||
570 | current_info.player.col--; | ||
571 | break; | ||
572 | |||
573 | case BUTTON_RIGHT: /* if it is a blank spot */ | ||
574 | switch(current_info.board[r][c+1]) { | ||
575 | case ' ': | ||
576 | case '.': /* if it is a home spot */ | ||
577 | new_spot = current_info.board[r][c+1]; | ||
578 | current_info.board[r][c+1] = '@'; | ||
579 | current_info.board[r][c] = current_info.player.spot; | ||
580 | current_info.player.spot = new_spot; | ||
581 | break; | ||
582 | |||
583 | case '$': | ||
584 | switch(current_info.board[r][c+2]) { | ||
585 | case ' ': /* going from blank to blank */ | ||
586 | current_info.board[r][c+2] = current_info.board[r][c+1]; | ||
587 | current_info.board[r][c+1] = current_info.board[r][c]; | ||
588 | current_info.board[r][c] = current_info.player.spot; | ||
589 | current_info.player.spot = ' '; | ||
590 | break; | ||
591 | |||
592 | case '.': /* going from a blank to home */ | ||
593 | current_info.board[r][c+2] = '%'; | ||
594 | current_info.board[r][c+1] = current_info.board[r][c]; | ||
595 | current_info.board[r][c] = current_info.player.spot; | ||
596 | current_info.player.spot = ' '; | ||
597 | current_info.level.boxes_to_go--; | ||
598 | break; | ||
599 | |||
600 | default: | ||
601 | moved = false; | ||
602 | break; | ||
603 | } | ||
604 | break; | ||
605 | |||
606 | case '%': | ||
607 | switch(current_info.board[r][c+2]) { | ||
608 | case ' ': /* going from a home to a blank */ | ||
609 | current_info.board[r][c+2] = '$'; | ||
610 | current_info.board[r][c+1] = current_info.board[r][c]; | ||
611 | current_info.board[r][c] = current_info.player.spot; | ||
612 | current_info.player.spot = '.'; | ||
613 | current_info.level.boxes_to_go++; | ||
614 | break; | ||
615 | |||
616 | case '.': | ||
617 | current_info.board[r][c+2] = '%'; | ||
618 | current_info.board[r][c+1] = current_info.board[r][c]; | ||
619 | current_info.board[r][c] = current_info.player.spot; | ||
620 | current_info.player.spot = '.'; | ||
621 | break; | ||
622 | |||
623 | default: | ||
624 | moved = false; | ||
625 | break; | ||
626 | } | ||
627 | break; | ||
628 | |||
629 | default: | ||
630 | moved = false; | ||
631 | break; | ||
632 | } | ||
633 | |||
634 | if (moved) | ||
635 | current_info.player.col++; | ||
636 | break; | ||
637 | |||
638 | case BUTTON_UP: | ||
639 | switch(current_info.board[r-1][c]) { | ||
640 | case ' ': /* if it is a blank spot */ | ||
641 | case '.': /* if it is a home spot */ | ||
642 | new_spot = current_info.board[r-1][c]; | ||
643 | current_info.board[r-1][c] = '@'; | ||
644 | current_info.board[r][c] = current_info.player.spot; | ||
645 | current_info.player.spot = new_spot; | ||
646 | break; | ||
647 | |||
648 | case '$': | ||
649 | switch(current_info.board[r-2][c]) { | ||
650 | case ' ': /* going from blank to blank */ | ||
651 | current_info.board[r-2][c] = current_info.board[r-1][c]; | ||
652 | current_info.board[r-1][c] = current_info.board[r][c]; | ||
653 | current_info.board[r][c] = current_info.player.spot; | ||
654 | current_info.player.spot = ' '; | ||
655 | break; | ||
656 | |||
657 | case '.': /* going from a blank to home */ | ||
658 | current_info.board[r-2][c] = '%'; | ||
659 | current_info.board[r-1][c] = current_info.board[r][c]; | ||
660 | current_info.board[r][c] = current_info.player.spot; | ||
661 | current_info.player.spot = ' '; | ||
662 | current_info.level.boxes_to_go--; | ||
663 | break; | ||
664 | |||
665 | default: | ||
666 | moved = false; | ||
667 | break; | ||
668 | } | ||
669 | break; | ||
670 | |||
671 | case '%': | ||
672 | switch(current_info.board[r-2][c]) { | ||
673 | case ' ': /* we are going from a home to a blank */ | ||
674 | current_info.board[r-2][c] = '$'; | ||
675 | current_info.board[r-1][c] = current_info.board[r][c]; | ||
676 | current_info.board[r][c] = current_info.player.spot; | ||
677 | current_info.player.spot = '.'; | ||
678 | current_info.level.boxes_to_go++; | ||
679 | break; | ||
680 | |||
681 | case '.': /* if we are going from a home to home */ | ||
682 | current_info.board[r-2][c] = '%'; | ||
683 | current_info.board[r-1][c] = current_info.board[r][c]; | ||
684 | current_info.board[r][c] = current_info.player.spot; | ||
685 | current_info.player.spot = '.'; | ||
686 | break; | ||
687 | |||
688 | default: | ||
689 | moved = false; | ||
690 | break; | ||
691 | } | ||
692 | break; | ||
693 | |||
694 | default: | ||
695 | moved = false; | ||
696 | break; | ||
697 | } | ||
698 | |||
699 | if (moved) | ||
700 | current_info.player.row--; | ||
701 | break; | ||
702 | |||
703 | case BUTTON_DOWN: | ||
704 | switch(current_info.board[r+1][c]) { | ||
705 | case ' ': /* if it is a blank spot */ | ||
706 | case '.': /* if it is a home spot */ | ||
707 | new_spot = current_info.board[r+1][c]; | ||
708 | current_info.board[r+1][c] = '@'; | ||
709 | current_info.board[r][c] = current_info.player.spot; | ||
710 | current_info.player.spot = new_spot; | ||
711 | break; | ||
712 | |||
713 | case '$': | ||
714 | switch(current_info.board[r+2][c]) { | ||
715 | case ' ': /* going from blank to blank */ | ||
716 | current_info.board[r+2][c] = current_info.board[r+1][c]; | ||
717 | current_info.board[r+1][c] = current_info.board[r][c]; | ||
718 | current_info.board[r][c] = current_info.player.spot; | ||
719 | current_info.player.spot = ' '; | ||
720 | break; | ||
721 | |||
722 | case '.': /* going from a blank to home */ | ||
723 | current_info.board[r+2][c] = '%'; | ||
724 | current_info.board[r+1][c] = current_info.board[r][c]; | ||
725 | current_info.board[r][c] = current_info.player.spot; | ||
726 | current_info.player.spot = ' '; | ||
727 | current_info.level.boxes_to_go--; | ||
728 | break; | ||
729 | |||
730 | default: | ||
731 | moved = false; | ||
732 | break; | ||
733 | } | ||
734 | break; | ||
735 | |||
736 | case '%': | ||
737 | switch(current_info.board[r+2][c]) { | ||
738 | case ' ': /* going from a home to a blank */ | ||
739 | current_info.board[r+2][c] = '$'; | ||
740 | current_info.board[r+1][c] = current_info.board[r][c]; | ||
741 | current_info.board[r][c] = current_info.player.spot; | ||
742 | current_info.player.spot = '.'; | ||
743 | current_info.level.boxes_to_go++; | ||
744 | break; | ||
745 | |||
746 | case '.': /* going from a home to home */ | ||
747 | current_info.board[r+2][c] = '%'; | ||
748 | current_info.board[r+1][c] = current_info.board[r][c]; | ||
749 | current_info.board[r][c] = current_info.player.spot; | ||
750 | current_info.player.spot = '.'; | ||
751 | break; | ||
752 | |||
753 | default: | ||
754 | moved = false; | ||
755 | break; | ||
756 | } | ||
757 | break; | ||
758 | |||
759 | default: | ||
760 | moved = false; | ||
761 | break; | ||
762 | } | ||
763 | |||
764 | if (moved) | ||
765 | current_info.player.row++; | ||
766 | break; | ||
767 | |||
768 | case SYS_USB_CONNECTED: | ||
769 | rb->usb_screen(); | ||
770 | return PLUGIN_USB_CONNECTED; | ||
771 | |||
772 | default: | ||
773 | moved = false; | ||
774 | break; | ||
775 | } | ||
776 | |||
777 | if (moved) { | ||
778 | current_info.level.moves++; | ||
779 | rb->lcd_clear_display(); | ||
780 | update_screen(); | ||
781 | } | ||
782 | |||
783 | /* We have completed this level */ | ||
784 | if (current_info.level.boxes_to_go == 0) { | ||
785 | current_info.level.level++; | ||
786 | |||
787 | /* clear undo stats */ | ||
788 | init_undo(); | ||
789 | |||
790 | rb->lcd_clear_display(); | ||
791 | |||
792 | if (current_info.level.level > current_info.max_level) { | ||
793 | rb->lcd_putsxy(10, 20, "You WIN!!"); | ||
794 | |||
795 | for (i = 0; i < 30000 ; i++) { | ||
796 | rb->lcd_invertrect(0, 0, 111, 63); | ||
797 | rb->lcd_update(); | ||
798 | |||
799 | button = rb->button_get(false); | ||
800 | if (button && ((button & BUTTON_REL) != BUTTON_REL)) | ||
801 | break; | ||
802 | } | ||
803 | |||
804 | return PLUGIN_OK; | ||
805 | } | ||
806 | |||
807 | load_level(); | ||
808 | update_screen(); | ||
809 | } | ||
810 | |||
811 | } /* end while */ | ||
812 | |||
813 | return PLUGIN_OK; | ||
814 | } | ||
815 | |||
816 | |||
817 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | ||
818 | { | ||
819 | int w, h; | ||
820 | int len; | ||
821 | |||
822 | TEST_PLUGIN_API(api); | ||
823 | (void)(parameter); | ||
824 | rb = api; | ||
825 | |||
826 | rb->lcd_setfont(FONT_SYSFIXED); | ||
827 | rb->lcd_getstringsize(SOKOBAN_TITLE, &w, &h); | ||
828 | |||
829 | /* Get horizontel centering for text */ | ||
830 | len = w; | ||
831 | if (len%2 != 0) | ||
832 | len =((len+1)/2)+(w/2); | ||
833 | else | ||
834 | len /= 2; | ||
835 | |||
836 | if (h%2 != 0) | ||
837 | h = (h/2)+1; | ||
838 | else | ||
839 | h /= 2; | ||
840 | |||
841 | rb->lcd_clear_display(); | ||
842 | rb->lcd_putsxy(LCD_WIDTH/2-len,(LCD_HEIGHT/2)-h, SOKOBAN_TITLE); | ||
843 | rb->lcd_update(); | ||
844 | rb->sleep(HZ*2); | ||
845 | |||
846 | rb->lcd_clear_display(); | ||
847 | |||
848 | rb->lcd_putsxy(3, 6, "[OFF] To Stop"); | ||
849 | rb->lcd_putsxy(3, 16, "[ON] To Undo"); | ||
850 | rb->lcd_putsxy(3, 26, "[F1] - Level"); | ||
851 | rb->lcd_putsxy(3, 36, "[F2] Same Level"); | ||
852 | rb->lcd_putsxy(3, 46, "[F3] + Level"); | ||
853 | |||
854 | rb->lcd_update(); | ||
855 | rb->sleep(HZ*2); | ||
856 | rb->lcd_clear_display(); | ||
857 | |||
858 | init_boards(); | ||
859 | |||
860 | if (get_level_count() != 0) { | ||
861 | rb->splash(HZ*2,0,true,"Failed loading levels!"); | ||
862 | return PLUGIN_OK; | ||
863 | } | ||
864 | |||
865 | return sokoban_loop(); | ||
866 | } | ||
867 | |||
868 | #endif | ||
diff --git a/apps/plugins/viewer.c b/apps/plugins/viewer.c new file mode 100644 index 0000000000..f8dc309a6e --- /dev/null +++ b/apps/plugins/viewer.c | |||
@@ -0,0 +1,415 @@ | |||
1 | /*************************************************************************** | ||
2 | * | ||
3 | * __________ __ ___. | ||
4 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
5 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
6 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
7 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
8 | * \/ \/ \/ \/ \/ | ||
9 | * $Id$ | ||
10 | * | ||
11 | * Copyright (C) 2002 Gilles Roux | ||
12 | * | ||
13 | * All files in this archive are subject to the GNU General Public License. | ||
14 | * See the file COPYING in the source tree root for full license agreement. | ||
15 | * | ||
16 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
17 | * KIND, either express or implied. | ||
18 | * | ||
19 | ****************************************************************************/ | ||
20 | #include "plugin.h" | ||
21 | |||
22 | #define BUFFER_SIZE 1024 | ||
23 | #define OUTSIDE_BUFFER -10 | ||
24 | #define OUTSIDE_FILE -11 | ||
25 | |||
26 | static int fd; | ||
27 | static int file_size; | ||
28 | |||
29 | static char buffer[BUFFER_SIZE+1]; | ||
30 | static int buffer_pos; /* Position of the buffer in the file */ | ||
31 | |||
32 | static char display_lines; /* number of lines on the display */ | ||
33 | static char display_columns; /* number of columns on the display */ | ||
34 | static int begin_line; /* Index of the first line displayed on the lcd */ | ||
35 | static int end_line; /* Index of the last line displayed on the lcd */ | ||
36 | static int begin_line_pos; /* Position of the first_line in the bufffer */ | ||
37 | static int end_line_pos; /* Position of the last_line in the buffer */ | ||
38 | static struct plugin_api* rb; | ||
39 | |||
40 | /* | ||
41 | * Known issue: The caching algorithm will fail (display incoherent data) if | ||
42 | * the total space of the lines that are displayed on the screen exceeds the | ||
43 | * buffer size (only happens with very long lines). | ||
44 | */ | ||
45 | |||
46 | static void display_line_count(void) | ||
47 | { | ||
48 | #ifdef HAVE_LCD_BITMAP | ||
49 | int w,h; | ||
50 | rb->lcd_getstringsize("M", &w, &h); | ||
51 | display_lines = LCD_HEIGHT / h; | ||
52 | display_columns = LCD_WIDTH / w; | ||
53 | #else | ||
54 | display_lines = 2; | ||
55 | display_columns = 11; | ||
56 | #endif | ||
57 | } | ||
58 | |||
59 | static int find_next_line(int pos) | ||
60 | { | ||
61 | int i; | ||
62 | |||
63 | if (pos==OUTSIDE_BUFFER || pos==OUTSIDE_FILE) | ||
64 | return pos; | ||
65 | |||
66 | i = pos; | ||
67 | if (buffer_pos+i>=file_size) { | ||
68 | return OUTSIDE_FILE; | ||
69 | } | ||
70 | while (1) { | ||
71 | i++; | ||
72 | if (buffer_pos+i==file_size) { | ||
73 | return i; | ||
74 | } | ||
75 | if (i>=BUFFER_SIZE) { | ||
76 | return OUTSIDE_BUFFER; | ||
77 | } | ||
78 | if (buffer[i]==0) { | ||
79 | return i; | ||
80 | } | ||
81 | } | ||
82 | } | ||
83 | |||
84 | static int find_prev_line(int pos) | ||
85 | { | ||
86 | int i; | ||
87 | |||
88 | if (pos==OUTSIDE_BUFFER || pos==OUTSIDE_FILE) | ||
89 | return pos; | ||
90 | |||
91 | i = pos; | ||
92 | if (buffer_pos+i<0) { | ||
93 | return OUTSIDE_FILE; | ||
94 | } | ||
95 | while (1) { | ||
96 | i--; | ||
97 | if (buffer_pos+i<0) { | ||
98 | return i; | ||
99 | } | ||
100 | if (i<0) { | ||
101 | return OUTSIDE_BUFFER; | ||
102 | } | ||
103 | if (buffer[i]==0) { | ||
104 | return i; | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | static void viewer_draw(int col) | ||
110 | { | ||
111 | int i, j; | ||
112 | char* str; | ||
113 | int line_pos; | ||
114 | |||
115 | rb->lcd_clear_display(); | ||
116 | |||
117 | line_pos = begin_line_pos; | ||
118 | |||
119 | for (i=0; i <= end_line - begin_line; i++) { | ||
120 | if (line_pos == OUTSIDE_BUFFER || | ||
121 | line_pos == OUTSIDE_FILE) | ||
122 | break; | ||
123 | str = buffer + line_pos + 1; | ||
124 | for (j=0; j<col && *str!=0; ++j) | ||
125 | str++; | ||
126 | rb->lcd_puts(0, i, str); | ||
127 | line_pos = find_next_line(line_pos); | ||
128 | } | ||
129 | #ifdef HAVE_LCD_BITMAP | ||
130 | rb->lcd_update(); | ||
131 | #endif | ||
132 | } | ||
133 | |||
134 | static void fill_buffer(int pos) | ||
135 | { | ||
136 | int i; | ||
137 | int numread; | ||
138 | |||
139 | if (pos>=file_size-BUFFER_SIZE) | ||
140 | pos = file_size-BUFFER_SIZE; | ||
141 | if (pos<0) | ||
142 | pos = 0; | ||
143 | |||
144 | rb->lseek(fd, pos, SEEK_SET); | ||
145 | numread = rb->read(fd, buffer, BUFFER_SIZE); | ||
146 | |||
147 | begin_line_pos -= pos - buffer_pos; | ||
148 | end_line_pos -= pos - buffer_pos; | ||
149 | buffer_pos = pos; | ||
150 | |||
151 | buffer[numread] = 0; | ||
152 | for(i=0;i<numread;i++) { | ||
153 | switch(buffer[i]) { | ||
154 | case '\r': | ||
155 | buffer[i] = ' '; | ||
156 | break; | ||
157 | case '\n': | ||
158 | buffer[i] = 0; | ||
159 | break; | ||
160 | default: | ||
161 | break; | ||
162 | } | ||
163 | } | ||
164 | } | ||
165 | |||
166 | static bool viewer_init(char* file) | ||
167 | { | ||
168 | int i; | ||
169 | int ret; | ||
170 | |||
171 | fd = rb->open(file, O_RDONLY); | ||
172 | if (fd==-1) | ||
173 | return false; | ||
174 | |||
175 | file_size = rb->lseek(fd, 0, SEEK_END); | ||
176 | |||
177 | buffer_pos = 0; | ||
178 | begin_line = 0; | ||
179 | begin_line_pos = -1; | ||
180 | end_line = -1; | ||
181 | end_line_pos = -1; | ||
182 | fill_buffer(0); | ||
183 | display_line_count(); | ||
184 | for (i=0; i<display_lines; ++i) { | ||
185 | ret = find_next_line(end_line_pos); | ||
186 | if (ret!=OUTSIDE_FILE && ret!=OUTSIDE_BUFFER) { | ||
187 | end_line_pos = ret; | ||
188 | end_line++; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | return true; | ||
193 | } | ||
194 | |||
195 | static void viewer_exit(void) | ||
196 | { | ||
197 | rb->close(fd); | ||
198 | } | ||
199 | |||
200 | static void viewer_scroll_down(void) | ||
201 | { | ||
202 | int ret; | ||
203 | |||
204 | ret = find_next_line(end_line_pos); | ||
205 | switch ( ret ) { | ||
206 | case OUTSIDE_BUFFER: | ||
207 | begin_line_pos = find_next_line(begin_line_pos); | ||
208 | fill_buffer(begin_line_pos+buffer_pos); | ||
209 | end_line_pos = find_next_line(end_line_pos); | ||
210 | break; | ||
211 | |||
212 | case OUTSIDE_FILE: | ||
213 | return; | ||
214 | |||
215 | default: | ||
216 | begin_line_pos = find_next_line(begin_line_pos); | ||
217 | end_line_pos = ret; | ||
218 | break; | ||
219 | } | ||
220 | begin_line++; | ||
221 | end_line++; | ||
222 | } | ||
223 | |||
224 | static void viewer_scroll_up(void) | ||
225 | { | ||
226 | int ret; | ||
227 | |||
228 | ret = find_prev_line(begin_line_pos); | ||
229 | switch ( ret ) { | ||
230 | case OUTSIDE_BUFFER: | ||
231 | end_line_pos = find_prev_line(end_line_pos); | ||
232 | fill_buffer(buffer_pos+end_line_pos-BUFFER_SIZE); | ||
233 | begin_line_pos = find_prev_line(begin_line_pos); | ||
234 | break; | ||
235 | |||
236 | case OUTSIDE_FILE: | ||
237 | return; | ||
238 | |||
239 | default: | ||
240 | end_line_pos = find_prev_line(end_line_pos); | ||
241 | begin_line_pos = ret; | ||
242 | break; | ||
243 | } | ||
244 | begin_line--; | ||
245 | end_line--; | ||
246 | } | ||
247 | |||
248 | static int pagescroll(int col) | ||
249 | { | ||
250 | bool exit = false; | ||
251 | int i; | ||
252 | |||
253 | while (!exit) { | ||
254 | switch (rb->button_get(true)) { | ||
255 | #ifdef HAVE_RECORDER_KEYPAD | ||
256 | case BUTTON_ON | BUTTON_UP: | ||
257 | case BUTTON_ON | BUTTON_UP | BUTTON_REPEAT: | ||
258 | #else | ||
259 | case BUTTON_ON | BUTTON_LEFT: | ||
260 | case BUTTON_ON | BUTTON_LEFT | BUTTON_REPEAT: | ||
261 | #endif | ||
262 | for (i=0; i<display_lines; i++) | ||
263 | viewer_scroll_up(); | ||
264 | break; | ||
265 | |||
266 | #ifdef HAVE_RECORDER_KEYPAD | ||
267 | case BUTTON_ON | BUTTON_DOWN: | ||
268 | case BUTTON_ON | BUTTON_DOWN | BUTTON_REPEAT: | ||
269 | #else | ||
270 | case BUTTON_ON | BUTTON_RIGHT: | ||
271 | case BUTTON_ON | BUTTON_RIGHT | BUTTON_REPEAT: | ||
272 | #endif | ||
273 | for (i=0; i<display_lines; i++) | ||
274 | viewer_scroll_down(); | ||
275 | break; | ||
276 | |||
277 | #ifdef HAVE_RECORDER_KEYPAD | ||
278 | case BUTTON_ON | BUTTON_LEFT: | ||
279 | case BUTTON_ON | BUTTON_LEFT | BUTTON_REPEAT: | ||
280 | #else | ||
281 | case BUTTON_ON | BUTTON_MENU | BUTTON_LEFT: | ||
282 | case BUTTON_ON | BUTTON_MENU | BUTTON_LEFT | BUTTON_REPEAT: | ||
283 | #endif | ||
284 | col -= display_columns; | ||
285 | if (col < 0) | ||
286 | col = 0; | ||
287 | break; | ||
288 | |||
289 | #ifdef HAVE_RECORDER_KEYPAD | ||
290 | case BUTTON_ON | BUTTON_RIGHT: | ||
291 | case BUTTON_ON | BUTTON_RIGHT | BUTTON_REPEAT: | ||
292 | #else | ||
293 | case BUTTON_ON | BUTTON_MENU | BUTTON_RIGHT: | ||
294 | case BUTTON_ON | BUTTON_MENU | BUTTON_RIGHT | BUTTON_REPEAT: | ||
295 | #endif | ||
296 | col += display_columns; | ||
297 | break; | ||
298 | |||
299 | case BUTTON_ON | BUTTON_REL: | ||
300 | #ifdef HAVE_RECORDER_KEYPAD | ||
301 | case BUTTON_ON | BUTTON_DOWN | BUTTON_REL: | ||
302 | case BUTTON_ON | BUTTON_UP | BUTTON_REL: | ||
303 | #else | ||
304 | case BUTTON_ON | BUTTON_RIGHT | BUTTON_REL: | ||
305 | case BUTTON_ON | BUTTON_LEFT | BUTTON_REL: | ||
306 | case BUTTON_ON | BUTTON_MENU | BUTTON_RIGHT | BUTTON_REL: | ||
307 | case BUTTON_ON | BUTTON_MENU | BUTTON_LEFT | BUTTON_REL: | ||
308 | #endif | ||
309 | exit = true; | ||
310 | break; | ||
311 | } | ||
312 | if ( !exit ) | ||
313 | viewer_draw(col); | ||
314 | } | ||
315 | |||
316 | return col; | ||
317 | } | ||
318 | |||
319 | enum plugin_status plugin_start(struct plugin_api* api, void* file) | ||
320 | { | ||
321 | bool exit=false; | ||
322 | int button; | ||
323 | int col = 0; | ||
324 | int ok; | ||
325 | |||
326 | TEST_PLUGIN_API(api); | ||
327 | rb = api; | ||
328 | |||
329 | if (!file) | ||
330 | return PLUGIN_ERROR; | ||
331 | |||
332 | ok = viewer_init(file); | ||
333 | if (!ok) { | ||
334 | rb->splash(HZ, 0, false, "Error"); | ||
335 | viewer_exit(); | ||
336 | return PLUGIN_OK; | ||
337 | } | ||
338 | |||
339 | viewer_draw(col); | ||
340 | while (!exit) { | ||
341 | button = rb->button_get(true); | ||
342 | |||
343 | switch ( button ) { | ||
344 | |||
345 | #ifdef HAVE_RECORDER_KEYPAD | ||
346 | case BUTTON_F1: | ||
347 | case BUTTON_OFF: | ||
348 | #else | ||
349 | case BUTTON_STOP: | ||
350 | #endif | ||
351 | viewer_exit(); | ||
352 | exit = true; | ||
353 | break; | ||
354 | |||
355 | #ifdef HAVE_RECORDER_KEYPAD | ||
356 | case BUTTON_UP: | ||
357 | case BUTTON_UP | BUTTON_REPEAT: | ||
358 | #else | ||
359 | case BUTTON_LEFT: | ||
360 | case BUTTON_LEFT | BUTTON_REPEAT: | ||
361 | #endif | ||
362 | viewer_scroll_up(); | ||
363 | viewer_draw(col); | ||
364 | break; | ||
365 | |||
366 | #ifdef HAVE_RECORDER_KEYPAD | ||
367 | case BUTTON_DOWN: | ||
368 | case BUTTON_DOWN | BUTTON_REPEAT: | ||
369 | #else | ||
370 | case BUTTON_RIGHT: | ||
371 | case BUTTON_RIGHT | BUTTON_REPEAT: | ||
372 | #endif | ||
373 | viewer_scroll_down(); | ||
374 | viewer_draw(col); | ||
375 | break; | ||
376 | |||
377 | #ifdef HAVE_RECORDER_KEYPAD | ||
378 | case BUTTON_LEFT: | ||
379 | case BUTTON_LEFT | BUTTON_REPEAT: | ||
380 | #else | ||
381 | case BUTTON_MENU | BUTTON_LEFT: | ||
382 | case BUTTON_MENU | BUTTON_LEFT | BUTTON_REPEAT: | ||
383 | #endif | ||
384 | col--; | ||
385 | if (col < 0) | ||
386 | col = 0; | ||
387 | viewer_draw(col); | ||
388 | break; | ||
389 | |||
390 | #ifdef HAVE_RECORDER_KEYPAD | ||
391 | case BUTTON_RIGHT: | ||
392 | case BUTTON_RIGHT | BUTTON_REPEAT: | ||
393 | #else | ||
394 | case BUTTON_MENU | BUTTON_RIGHT: | ||
395 | case BUTTON_MENU | BUTTON_RIGHT | BUTTON_REPEAT: | ||
396 | #endif | ||
397 | col++; | ||
398 | viewer_draw(col); | ||
399 | break; | ||
400 | |||
401 | case BUTTON_ON: | ||
402 | #ifdef HAVE_PLAYER_KEYPAD | ||
403 | case BUTTON_ON | BUTTON_MENU: | ||
404 | #endif | ||
405 | col = pagescroll(col); | ||
406 | break; | ||
407 | |||
408 | case SYS_USB_CONNECTED: | ||
409 | rb->usb_screen(); | ||
410 | viewer_exit(); | ||
411 | return PLUGIN_USB_CONNECTED; | ||
412 | } | ||
413 | } | ||
414 | return PLUGIN_OK; | ||
415 | } | ||
diff --git a/apps/plugins/wormlet.c b/apps/plugins/wormlet.c new file mode 100644 index 0000000000..be089cdb7c --- /dev/null +++ b/apps/plugins/wormlet.c | |||
@@ -0,0 +1,2009 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Philipp Pertermann | ||
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 | #include "plugin.h" | ||
20 | |||
21 | #ifdef HAVE_LCD_BITMAP | ||
22 | |||
23 | /* size of the field the worm lives in */ | ||
24 | #define FIELD_RECT_X 1 | ||
25 | #define FIELD_RECT_Y 1 | ||
26 | #define FIELD_RECT_WIDTH (LCD_WIDTH - 45) | ||
27 | #define FIELD_RECT_HEIGHT (LCD_HEIGHT - 2) | ||
28 | |||
29 | /* size of the ring of the worm | ||
30 | choos a value that is a power of 2 to help | ||
31 | the compiler optimize modul operations*/ | ||
32 | #define MAX_WORM_SEGMENTS 64 | ||
33 | |||
34 | /* when the game starts */ | ||
35 | #define INITIAL_WORM_LENGTH 10 | ||
36 | |||
37 | /* num of pixel the worm grows per eaten food */ | ||
38 | #define WORM_PER_FOOD 7 | ||
39 | |||
40 | /* num of worms creeping in the FIELD */ | ||
41 | #define MAX_WORMS 3 | ||
42 | |||
43 | /* minimal distance between a worm and an argh | ||
44 | when a new argh is made */ | ||
45 | #define MIN_ARGH_DIST 5 | ||
46 | |||
47 | /** | ||
48 | * All the properties that a worm has. | ||
49 | */ | ||
50 | static struct worm { | ||
51 | /* The worm is stored in a ring of xy coordinates */ | ||
52 | char x[MAX_WORM_SEGMENTS]; | ||
53 | char y[MAX_WORM_SEGMENTS]; | ||
54 | |||
55 | int head; /* index of the head within the buffer */ | ||
56 | int tail; /* index of the tail within the buffer */ | ||
57 | int growing; /* number of cyles the worm still keeps growing */ | ||
58 | bool alive; /* the worms living state */ | ||
59 | |||
60 | /* direction vector in which the worm moves */ | ||
61 | int dirx; /* only values -1 0 1 allowed */ | ||
62 | int diry; /* only values -1 0 1 allowed */ | ||
63 | |||
64 | /* this method is used to fetch the direction the user | ||
65 | has selected. It can be one of the values | ||
66 | human_player1, human_player2, remote_player, virtual_player. | ||
67 | All these values are fuctions, that can change the direction | ||
68 | of the worm */ | ||
69 | void (*fetch_worm_direction)(struct worm *w); | ||
70 | } worms[MAX_WORMS]; | ||
71 | |||
72 | /* stores the highscore - besides it was scored by a virtual player */ | ||
73 | static int highscore; | ||
74 | |||
75 | #define MAX_FOOD 5 /* maximal number of food items */ | ||
76 | #define FOOD_SIZE 3 /* the width and height of a food */ | ||
77 | |||
78 | /* The arrays store the food coordinates */ | ||
79 | static char foodx[MAX_FOOD]; | ||
80 | static char foody[MAX_FOOD]; | ||
81 | |||
82 | #define MAX_ARGH 100 /* maximal number of argh items */ | ||
83 | #define ARGH_SIZE 4 /* the width and height of a argh */ | ||
84 | #define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */ | ||
85 | |||
86 | /* The arrays store the argh coordinates */ | ||
87 | static char arghx[MAX_ARGH]; | ||
88 | static char arghy[MAX_ARGH]; | ||
89 | |||
90 | /* the number of arghs that are currently in use */ | ||
91 | static int argh_count; | ||
92 | |||
93 | #ifdef DEBUG_WORMLET | ||
94 | /* just a buffer used for debug output */ | ||
95 | static char debugout[15]; | ||
96 | #endif | ||
97 | |||
98 | /* the number of ticks each game cycle should take */ | ||
99 | #define SPEED 14 | ||
100 | |||
101 | /* the number of active worms (dead or alive) */ | ||
102 | static int worm_count = MAX_WORMS; | ||
103 | |||
104 | /* in multiplayer mode: en- / disables the remote worm control | ||
105 | in singleplayer mode: toggles 4 / 2 button worm control */ | ||
106 | static bool use_remote = false; | ||
107 | |||
108 | /* return values of check_collision */ | ||
109 | #define COLLISION_NONE 0 | ||
110 | #define COLLISION_WORM 1 | ||
111 | #define COLLISION_FOOD 2 | ||
112 | #define COLLISION_ARGH 3 | ||
113 | #define COLLISION_FIELD 4 | ||
114 | |||
115 | /* constants for use as directions. | ||
116 | Note that the values are ordered clockwise. | ||
117 | Thus increasing / decreasing the values | ||
118 | is equivalent to right / left turns. */ | ||
119 | #define WEST 0 | ||
120 | #define NORTH 1 | ||
121 | #define EAST 2 | ||
122 | #define SOUTH 3 | ||
123 | |||
124 | /* direction of human player 1 */ | ||
125 | static int player1_dir = EAST; | ||
126 | /* direction of human player 2 */ | ||
127 | static int player2_dir = EAST; | ||
128 | /* direction of human player 3 */ | ||
129 | static int player3_dir = EAST; | ||
130 | |||
131 | /* the number of (human) players that currently | ||
132 | control a worm */ | ||
133 | static int players = 1; | ||
134 | |||
135 | /* the rockbox plugin api */ | ||
136 | static struct plugin_api* rb; | ||
137 | |||
138 | #ifdef DEBUG_WORMLET | ||
139 | static void set_debug_out(char *str){ | ||
140 | strcpy(debugout, str); | ||
141 | } | ||
142 | #endif | ||
143 | |||
144 | /** | ||
145 | * Returns the direction id in which the worm | ||
146 | * currently is creeping. | ||
147 | * @param struct worm *w The worm that is to be investigated. | ||
148 | * w Must not be null. | ||
149 | * @return int A value 0 <= value < 4 | ||
150 | * Note the predefined constants NORTH, SOUTH, EAST, WEST | ||
151 | */ | ||
152 | static int get_worm_dir(struct worm *w) { | ||
153 | int retVal ; | ||
154 | if (w->dirx == 0) { | ||
155 | if (w->diry == 1) { | ||
156 | retVal = SOUTH; | ||
157 | } else { | ||
158 | retVal = NORTH; | ||
159 | } | ||
160 | } else { | ||
161 | if (w->dirx == 1) { | ||
162 | retVal = EAST; | ||
163 | } else { | ||
164 | retVal = WEST; | ||
165 | } | ||
166 | } | ||
167 | return retVal; | ||
168 | } | ||
169 | |||
170 | /** | ||
171 | * Set the direction of the specified worm with a direction id. | ||
172 | * Increasing the value by 1 means to turn the worm direction | ||
173 | * to right by 90 degree. | ||
174 | * @param struct worm *w The worm that is to be altered. w Must not be null. | ||
175 | * @param int dir The new direction in which the worm is to creep. | ||
176 | * dir must be 0 <= dir < 4. Use predefined constants | ||
177 | * NORTH, SOUTH, EAST, WEST | ||
178 | */ | ||
179 | static void set_worm_dir(struct worm *w, int dir) { | ||
180 | switch (dir) { | ||
181 | case WEST: | ||
182 | w->dirx = -1; | ||
183 | w->diry = 0; | ||
184 | break; | ||
185 | case NORTH: | ||
186 | w->dirx = 0; | ||
187 | w->diry = - 1; | ||
188 | break; | ||
189 | case EAST: | ||
190 | w->dirx = 1; | ||
191 | w->diry = 0; | ||
192 | break; | ||
193 | case SOUTH: | ||
194 | w->dirx = 0; | ||
195 | w->diry = 1; | ||
196 | break; | ||
197 | } | ||
198 | } | ||
199 | |||
200 | /** | ||
201 | * Returns the current length of the worm array. This | ||
202 | * is also a value for the number of bends that are in the worm. | ||
203 | * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS | ||
204 | */ | ||
205 | static int get_worm_array_length(struct worm *w) { | ||
206 | /* initial simple calculation will be overwritten if wrong. */ | ||
207 | int retVal = w->head - w->tail; | ||
208 | |||
209 | /* if the worm 'crosses' the boundaries of the ringbuffer */ | ||
210 | if (retVal < 0) { | ||
211 | retVal = w->head + MAX_WORM_SEGMENTS - w->tail; | ||
212 | } | ||
213 | |||
214 | return retVal; | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * Returns the score the specified worm. The score is the length | ||
219 | * of the worm. | ||
220 | * @param struct worm *w The worm that is to be investigated. | ||
221 | * w must not be null. | ||
222 | * @return int The length of the worm (>= 0). | ||
223 | */ | ||
224 | static int get_score(struct worm *w) { | ||
225 | int retval = 0; | ||
226 | int length = get_worm_array_length(w); | ||
227 | int i; | ||
228 | for (i = 0; i < length; i++) { | ||
229 | |||
230 | /* The iteration iterates the length of the worm. | ||
231 | Here's the conversion to the true indices within the worm arrays. */ | ||
232 | int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS; | ||
233 | int lineend = (linestart + 1) % MAX_WORM_SEGMENTS; | ||
234 | int startx = w->x[linestart]; | ||
235 | int starty = w->y[linestart]; | ||
236 | int endx = w->x[lineend]; | ||
237 | int endy = w->y[lineend]; | ||
238 | |||
239 | int minimum, maximum; | ||
240 | |||
241 | if (startx == endx) { | ||
242 | minimum = MIN(starty, endy); | ||
243 | maximum = MAX(starty, endy); | ||
244 | } else { | ||
245 | minimum = MIN(startx, endx); | ||
246 | maximum = MAX(startx, endx); | ||
247 | } | ||
248 | retval += abs(maximum - minimum); | ||
249 | } | ||
250 | return retval; | ||
251 | } | ||
252 | |||
253 | /** | ||
254 | * Determines wether the line specified by startx, starty, endx, endy intersects | ||
255 | * the rectangle specified by x, y, width, height. Note that the line must be exactly | ||
256 | * horizontal or vertical (startx == endx or starty == endy). | ||
257 | * @param int startx The x coordinate of the start point of the line. | ||
258 | * @param int starty The y coordinate of the start point of the line. | ||
259 | * @param int endx The x coordinate of the end point of the line. | ||
260 | * @param int endy The y coordinate of the end point of the line. | ||
261 | * @param int x The x coordinate of the top left corner of the rectangle. | ||
262 | * @param int y The y coordinate of the top left corner of the rectangle. | ||
263 | * @param int width The width of the rectangle. | ||
264 | * @param int height The height of the rectangle. | ||
265 | * @return bool Returns true if the specified line intersects with the recangle. | ||
266 | */ | ||
267 | static bool line_in_rect(int startx, int starty, int endx, int endy, int x, int y, int width, int height) { | ||
268 | bool retval = false; | ||
269 | int simple, simplemin, simplemax; | ||
270 | int compa, compb, compmin, compmax; | ||
271 | int temp; | ||
272 | if (startx == endx) { | ||
273 | simple = startx; | ||
274 | simplemin = x; | ||
275 | simplemax = x + width; | ||
276 | |||
277 | compa = starty; | ||
278 | compb = endy; | ||
279 | compmin = y; | ||
280 | compmax = y + height; | ||
281 | } else { | ||
282 | simple = starty; | ||
283 | simplemin = y; | ||
284 | simplemax = y + height; | ||
285 | |||
286 | compa = startx; | ||
287 | compb = endx; | ||
288 | compmin = x; | ||
289 | compmax = x + width; | ||
290 | }; | ||
291 | |||
292 | temp = compa; | ||
293 | compa = MIN(compa, compb); | ||
294 | compb = MAX(temp, compb); | ||
295 | |||
296 | if (simplemin <= simple && simple <= simplemax) { | ||
297 | if ((compmin <= compa && compa <= compmax) || | ||
298 | (compmin <= compb && compb <= compmax) || | ||
299 | (compa <= compmin && compb >= compmax)) { | ||
300 | retval = true; | ||
301 | } | ||
302 | } | ||
303 | return retval; | ||
304 | } | ||
305 | |||
306 | /** | ||
307 | * Tests wether the specified worm intersects with the rect. | ||
308 | * @param struct worm *w The worm to be investigated | ||
309 | * @param int x The x coordinate of the top left corner of the rect | ||
310 | * @param int y The y coordinate of the top left corner of the rect | ||
311 | * @param int widht The width of the rect | ||
312 | * @param int height The height of the rect | ||
313 | * @return bool Returns true if the worm intersects with the rect | ||
314 | */ | ||
315 | static bool worm_in_rect(struct worm *w, int x, int y, int width, int height) { | ||
316 | bool retval = false; | ||
317 | |||
318 | |||
319 | /* get_worm_array_length is expensive -> buffer the value */ | ||
320 | int wormLength = get_worm_array_length(w); | ||
321 | int i; | ||
322 | |||
323 | /* test each entry that is part of the worm */ | ||
324 | for (i = 0; i < wormLength && retval == false; i++) { | ||
325 | |||
326 | /* The iteration iterates the length of the worm. | ||
327 | Here's the conversion to the true indices within the worm arrays. */ | ||
328 | int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS; | ||
329 | int lineend = (linestart + 1) % MAX_WORM_SEGMENTS; | ||
330 | int startx = w->x[linestart]; | ||
331 | int starty = w->y[linestart]; | ||
332 | int endx = w->x[lineend]; | ||
333 | int endy = w->y[lineend]; | ||
334 | |||
335 | retval = line_in_rect(startx, starty, endx, endy, x, y, width, height); | ||
336 | } | ||
337 | |||
338 | return retval; | ||
339 | } | ||
340 | |||
341 | /** | ||
342 | * Checks wether a specific food in the food arrays is at the | ||
343 | * specified coordinates. | ||
344 | * @param int foodIndex The index of the food in the food arrays | ||
345 | * @param int x the x coordinate. | ||
346 | * @param int y the y coordinate. | ||
347 | * @return Returns true if the coordinate hits the food specified by | ||
348 | * foodIndex. | ||
349 | */ | ||
350 | static bool specific_food_collision(int foodIndex, int x, int y) { | ||
351 | bool retVal = false; | ||
352 | if (x >= foodx[foodIndex] && | ||
353 | x < foodx[foodIndex] + FOOD_SIZE && | ||
354 | y >= foody[foodIndex] && | ||
355 | y < foody[foodIndex] + FOOD_SIZE) { | ||
356 | |||
357 | retVal = true; | ||
358 | } | ||
359 | return retVal; | ||
360 | } | ||
361 | |||
362 | /** | ||
363 | * Returns the index of the food that is at the | ||
364 | * given coordinates. If no food is at the coordinates | ||
365 | * -1 is returned. | ||
366 | * @return int -1 <= value < MAX_FOOD | ||
367 | */ | ||
368 | static int food_collision(int x, int y) { | ||
369 | int i = 0; | ||
370 | int retVal = -1; | ||
371 | for (i = 0; i < MAX_FOOD; i++) { | ||
372 | if (specific_food_collision(i, x, y)) { | ||
373 | retVal = i; | ||
374 | break; | ||
375 | } | ||
376 | } | ||
377 | return retVal; | ||
378 | } | ||
379 | |||
380 | /** | ||
381 | * Checks wether a specific argh in the argh arrays is at the | ||
382 | * specified coordinates. | ||
383 | * @param int arghIndex The index of the argh in the argh arrays | ||
384 | * @param int x the x coordinate. | ||
385 | * @param int y the y coordinate. | ||
386 | * @return Returns true if the coordinate hits the argh specified by | ||
387 | * arghIndex. | ||
388 | */ | ||
389 | static bool specific_argh_collision(int arghIndex, int x, int y) { | ||
390 | |||
391 | if ( x >= arghx[arghIndex] && | ||
392 | y >= arghy[arghIndex] && | ||
393 | x < arghx[arghIndex] + ARGH_SIZE && | ||
394 | y < arghy[arghIndex] + ARGH_SIZE ) | ||
395 | { | ||
396 | return true; | ||
397 | } | ||
398 | |||
399 | return false; | ||
400 | } | ||
401 | |||
402 | /** | ||
403 | * Returns the index of the argh that is at the | ||
404 | * given coordinates. If no argh is at the coordinates | ||
405 | * -1 is returned. | ||
406 | * @param int x The x coordinate. | ||
407 | * @param int y The y coordinate. | ||
408 | * @return int -1 <= value < argh_count <= MAX_ARGH | ||
409 | */ | ||
410 | static int argh_collision(int x, int y) { | ||
411 | int i = 0; | ||
412 | int retVal = -1; | ||
413 | |||
414 | /* search for the argh that has the specified coords */ | ||
415 | for (i = 0; i < argh_count; i++) { | ||
416 | if (specific_argh_collision(i, x, y)) { | ||
417 | retVal = i; | ||
418 | break; | ||
419 | } | ||
420 | } | ||
421 | return retVal; | ||
422 | } | ||
423 | |||
424 | /** | ||
425 | * Checks wether the worm collides with the food at the specfied food-arrays. | ||
426 | * @param int foodIndex The index of the food in the arrays. Ensure the value is | ||
427 | * 0 <= foodIndex <= MAX_FOOD | ||
428 | * @return Returns true if the worm collides with the specified food. | ||
429 | */ | ||
430 | static bool worm_food_collision(struct worm *w, int foodIndex) | ||
431 | { | ||
432 | bool retVal = false; | ||
433 | |||
434 | retVal = worm_in_rect(w, foodx[foodIndex], foody[foodIndex], | ||
435 | FOOD_SIZE - 1, FOOD_SIZE - 1); | ||
436 | |||
437 | return retVal; | ||
438 | } | ||
439 | |||
440 | /** | ||
441 | * Returns true if the worm hits the argh within the next moves (unless | ||
442 | * the worm changes it's direction). | ||
443 | * @param struct worm *w - The worm to investigate | ||
444 | * @param int argh_idx - The index of the argh | ||
445 | * @param int moves - The number of moves that are considered. | ||
446 | * @return Returns false if the specified argh is not hit within the next | ||
447 | * moves. | ||
448 | */ | ||
449 | static bool worm_argh_collision_in_moves(struct worm *w, int argh_idx, int moves){ | ||
450 | bool retVal = false; | ||
451 | int x1, y1, x2, y2; | ||
452 | x1 = w->x[w->head]; | ||
453 | y1 = w->y[w->head]; | ||
454 | |||
455 | x2 = w->x[w->head] + moves * w->dirx; | ||
456 | y2 = w->y[w->head] + moves * w->diry; | ||
457 | |||
458 | retVal = line_in_rect(x1, y1, x2, y2, arghx[argh_idx], arghy[argh_idx], | ||
459 | ARGH_SIZE, ARGH_SIZE); | ||
460 | return retVal; | ||
461 | } | ||
462 | |||
463 | /** | ||
464 | * Checks wether the worm collides with the argh at the specfied argh-arrays. | ||
465 | * @param int arghIndex The index of the argh in the arrays. | ||
466 | * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH | ||
467 | * @return Returns true if the worm collides with the specified argh. | ||
468 | */ | ||
469 | static bool worm_argh_collision(struct worm *w, int arghIndex) | ||
470 | { | ||
471 | bool retVal = false; | ||
472 | |||
473 | retVal = worm_in_rect(w, arghx[arghIndex], arghy[arghIndex], | ||
474 | ARGH_SIZE - 1, ARGH_SIZE - 1); | ||
475 | |||
476 | return retVal; | ||
477 | } | ||
478 | |||
479 | /** | ||
480 | * Find new coordinates for the food stored in foodx[index], foody[index] | ||
481 | * that don't collide with any other food or argh | ||
482 | * @param int index | ||
483 | * Ensure that 0 <= index < MAX_FOOD. | ||
484 | */ | ||
485 | static int make_food(int index) { | ||
486 | |||
487 | int x = 0; | ||
488 | int y = 0; | ||
489 | bool collisionDetected = false; | ||
490 | int tries = 0; | ||
491 | int i; | ||
492 | |||
493 | do { | ||
494 | /* make coordinates for a new food so that | ||
495 | the entire food lies within the FIELD */ | ||
496 | x = rb->rand() % (FIELD_RECT_WIDTH - FOOD_SIZE); | ||
497 | y = rb->rand() % (FIELD_RECT_HEIGHT - FOOD_SIZE); | ||
498 | tries ++; | ||
499 | |||
500 | /* Ensure that the new food doesn't collide with any | ||
501 | existing foods or arghs. | ||
502 | If one or more corners of the new food hit any existing | ||
503 | argh or food a collision is detected. | ||
504 | */ | ||
505 | collisionDetected = | ||
506 | food_collision(x , y ) >= 0 || | ||
507 | food_collision(x , y + FOOD_SIZE - 1) >= 0 || | ||
508 | food_collision(x + FOOD_SIZE - 1, y ) >= 0 || | ||
509 | food_collision(x + FOOD_SIZE - 1, y + FOOD_SIZE - 1) >= 0 || | ||
510 | argh_collision(x , y ) >= 0 || | ||
511 | argh_collision(x , y + FOOD_SIZE - 1) >= 0 || | ||
512 | argh_collision(x + FOOD_SIZE - 1, y ) >= 0 || | ||
513 | argh_collision(x + FOOD_SIZE - 1, y + FOOD_SIZE - 1) >= 0; | ||
514 | |||
515 | /* use coordinates for further testing */ | ||
516 | foodx[index] = x; | ||
517 | foody[index] = y; | ||
518 | |||
519 | /* now test wether we accidently hit the worm with food ;) */ | ||
520 | i = 0; | ||
521 | for (i = 0; i < worm_count && !collisionDetected; i++) { | ||
522 | collisionDetected |= worm_food_collision(&worms[i], index); | ||
523 | } | ||
524 | } | ||
525 | while (collisionDetected); | ||
526 | return tries; | ||
527 | } | ||
528 | |||
529 | /** | ||
530 | * Clears a food from the lcd buffer. | ||
531 | * @param int index The index of the food arrays under which | ||
532 | * the coordinates of the desired food can be found. Ensure | ||
533 | * that the value is 0 <= index <= MAX_FOOD. | ||
534 | */ | ||
535 | static void clear_food(int index) | ||
536 | { | ||
537 | /* remove the old food from the screen */ | ||
538 | rb->lcd_clearrect(foodx[index] + FIELD_RECT_X, | ||
539 | foody[index] + FIELD_RECT_Y, | ||
540 | FOOD_SIZE, FOOD_SIZE); | ||
541 | } | ||
542 | |||
543 | /** | ||
544 | * Draws a food in the lcd buffer. | ||
545 | * @param int index The index of the food arrays under which | ||
546 | * the coordinates of the desired food can be found. Ensure | ||
547 | * that the value is 0 <= index <= MAX_FOOD. | ||
548 | */ | ||
549 | static void draw_food(int index) | ||
550 | { | ||
551 | /* draw the food object */ | ||
552 | rb->lcd_fillrect(foodx[index] + FIELD_RECT_X, | ||
553 | foody[index] + FIELD_RECT_Y, | ||
554 | FOOD_SIZE, FOOD_SIZE); | ||
555 | rb->lcd_clearrect(foodx[index] + FIELD_RECT_X + 1, | ||
556 | foody[index] + FIELD_RECT_Y + 1, | ||
557 | FOOD_SIZE - 2, FOOD_SIZE - 2); | ||
558 | } | ||
559 | |||
560 | /** | ||
561 | * Find new coordinates for the argh stored in arghx[index], arghy[index] | ||
562 | * that don't collide with any other food or argh. | ||
563 | * @param int index | ||
564 | * Ensure that 0 <= index < argh_count < MAX_ARGH. | ||
565 | */ | ||
566 | static int make_argh(int index) | ||
567 | { | ||
568 | int x = -1; | ||
569 | int y = -1; | ||
570 | bool collisionDetected = false; | ||
571 | int tries = 0; | ||
572 | int i; | ||
573 | |||
574 | do { | ||
575 | /* make coordinates for a new argh so that | ||
576 | the entire food lies within the FIELD */ | ||
577 | x = rb->rand() % (FIELD_RECT_WIDTH - ARGH_SIZE); | ||
578 | y = rb->rand() % (FIELD_RECT_HEIGHT - ARGH_SIZE); | ||
579 | tries ++; | ||
580 | |||
581 | /* Ensure that the new argh doesn't intersect with any | ||
582 | existing foods or arghs. | ||
583 | If one or more corners of the new argh hit any existing | ||
584 | argh or food an intersection is detected. | ||
585 | */ | ||
586 | collisionDetected = | ||
587 | food_collision(x , y ) >= 0 || | ||
588 | food_collision(x , y + ARGH_SIZE - 1) >= 0 || | ||
589 | food_collision(x + ARGH_SIZE - 1, y ) >= 0 || | ||
590 | food_collision(x + ARGH_SIZE - 1, y + ARGH_SIZE - 1) >= 0 || | ||
591 | argh_collision(x , y ) >= 0 || | ||
592 | argh_collision(x , y + ARGH_SIZE - 1) >= 0 || | ||
593 | argh_collision(x + ARGH_SIZE - 1, y ) >= 0 || | ||
594 | argh_collision(x + ARGH_SIZE - 1, y + ARGH_SIZE - 1) >= 0; | ||
595 | |||
596 | /* use the candidate coordinates to make a real argh */ | ||
597 | arghx[index] = x; | ||
598 | arghy[index] = y; | ||
599 | |||
600 | /* now test wether we accidently hit the worm with argh ;) */ | ||
601 | for (i = 0; i < worm_count && !collisionDetected; i++) { | ||
602 | collisionDetected |= worm_argh_collision(&worms[i], index); | ||
603 | collisionDetected |= worm_argh_collision_in_moves(&worms[i], index, | ||
604 | MIN_ARGH_DIST); | ||
605 | } | ||
606 | } | ||
607 | while (collisionDetected); | ||
608 | return tries; | ||
609 | } | ||
610 | |||
611 | /** | ||
612 | * Draws an argh in the lcd buffer. | ||
613 | * @param int index The index of the argh arrays under which | ||
614 | * the coordinates of the desired argh can be found. Ensure | ||
615 | * that the value is 0 <= index < argh_count <= MAX_ARGH. | ||
616 | */ | ||
617 | static void draw_argh(int index) | ||
618 | { | ||
619 | /* draw the new argh */ | ||
620 | rb->lcd_fillrect(arghx[index] + FIELD_RECT_X, | ||
621 | arghy[index] + FIELD_RECT_Y, | ||
622 | ARGH_SIZE, ARGH_SIZE); | ||
623 | } | ||
624 | |||
625 | static void virtual_player(struct worm *w); | ||
626 | /** | ||
627 | * Initialzes the specified worm with INITIAL_WORM_LENGTH | ||
628 | * and the tail at the specified position. The worm will | ||
629 | * be initialized alive and creeping EAST. | ||
630 | * @param struct worm *w The worm that is to be initialized | ||
631 | * @param int x The x coordinate at which the tail of the worm starts. | ||
632 | * x must be 0 <= x < FIELD_RECT_WIDTH. | ||
633 | * @param int y The y coordinate at which the tail of the worm starts | ||
634 | * y must be 0 <= y < FIELD_RECT_WIDTH. | ||
635 | */ | ||
636 | static void init_worm(struct worm *w, int x, int y){ | ||
637 | /* initialize the worm size */ | ||
638 | w->head = 1; | ||
639 | w->tail = 0; | ||
640 | |||
641 | w->x[w->head] = x + 1; | ||
642 | w->y[w->head] = y; | ||
643 | |||
644 | w->x[w->tail] = x; | ||
645 | w->y[w->tail] = y; | ||
646 | |||
647 | /* set the initial direction the worm creeps to */ | ||
648 | w->dirx = 1; | ||
649 | w->diry = 0; | ||
650 | |||
651 | w->growing = INITIAL_WORM_LENGTH - 1; | ||
652 | w->alive = true; | ||
653 | w->fetch_worm_direction = virtual_player; | ||
654 | } | ||
655 | |||
656 | /** | ||
657 | * Writes the direction that was stored for | ||
658 | * human player 1 into the specified worm. This function | ||
659 | * may be used to be stored in worm.fetch_worm_direction. | ||
660 | * The value of | ||
661 | * the direction is read from player1_dir. | ||
662 | * @param struct worm *w - The worm of which the direction | ||
663 | * is altered. | ||
664 | */ | ||
665 | static void human_player1(struct worm *w) { | ||
666 | set_worm_dir(w, player1_dir); | ||
667 | } | ||
668 | |||
669 | /** | ||
670 | * Writes the direction that was stored for | ||
671 | * human player 2 into the specified worm. This function | ||
672 | * may be used to be stored in worm.fetch_worm_direction. | ||
673 | * The value of | ||
674 | * the direction is read from player2_dir. | ||
675 | * @param struct worm *w - The worm of which the direction | ||
676 | * is altered. | ||
677 | */ | ||
678 | static void human_player2(struct worm *w) { | ||
679 | set_worm_dir(w, player2_dir); | ||
680 | } | ||
681 | |||
682 | /** | ||
683 | * Writes the direction that was stored for | ||
684 | * human player using a remote control | ||
685 | * into the specified worm. This function | ||
686 | * may be used to be stored in worm.fetch_worm_direction. | ||
687 | * The value of | ||
688 | * the direction is read from player3_dir. | ||
689 | * @param struct worm *w - The worm of which the direction | ||
690 | * is altered. | ||
691 | */ | ||
692 | static void remote_player(struct worm *w) { | ||
693 | set_worm_dir(w, player3_dir); | ||
694 | } | ||
695 | |||
696 | /** | ||
697 | * Initializes the worm-, food- and argh-arrays, draws a frame, | ||
698 | * makes some food and argh and display all that stuff. | ||
699 | */ | ||
700 | static void init_wormlet(void) | ||
701 | { | ||
702 | int i; | ||
703 | |||
704 | for (i = 0; i< worm_count; i++) { | ||
705 | /* Initialize all the worm coordinates to center. */ | ||
706 | int x = (int)(FIELD_RECT_WIDTH / 2); | ||
707 | int y = (int)((FIELD_RECT_HEIGHT - 20)/ 2) + i * 10; | ||
708 | |||
709 | init_worm(&worms[i], x, y); | ||
710 | } | ||
711 | |||
712 | player1_dir = EAST; | ||
713 | player2_dir = EAST; | ||
714 | player3_dir = EAST; | ||
715 | |||
716 | if (players > 0) { | ||
717 | worms[0].fetch_worm_direction = human_player1; | ||
718 | } | ||
719 | |||
720 | if (players > 1) { | ||
721 | if (use_remote) { | ||
722 | worms[1].fetch_worm_direction = remote_player; | ||
723 | } else { | ||
724 | worms[1].fetch_worm_direction = human_player2; | ||
725 | } | ||
726 | } | ||
727 | |||
728 | if (players > 2) { | ||
729 | worms[2].fetch_worm_direction = human_player2; | ||
730 | } | ||
731 | |||
732 | /* Needed when the game is restarted using BUTTON_ON */ | ||
733 | rb->lcd_clear_display(); | ||
734 | |||
735 | /* make and display some food and argh */ | ||
736 | argh_count = MAX_FOOD; | ||
737 | for (i = 0; i < MAX_FOOD; i++) { | ||
738 | make_food(i); | ||
739 | draw_food(i); | ||
740 | make_argh(i); | ||
741 | draw_argh(i); | ||
742 | } | ||
743 | |||
744 | /* draw the game field */ | ||
745 | rb->lcd_invertrect(0, 0, FIELD_RECT_WIDTH + 2, FIELD_RECT_HEIGHT + 2); | ||
746 | rb->lcd_invertrect(1, 1, FIELD_RECT_WIDTH, FIELD_RECT_HEIGHT); | ||
747 | |||
748 | /* make everything visible */ | ||
749 | rb->lcd_update(); | ||
750 | } | ||
751 | |||
752 | |||
753 | /** | ||
754 | * Move the worm one step further if it is alive. | ||
755 | * The direction in which the worm moves is taken from dirx and diry. | ||
756 | * move_worm decreases growing if > 0. While the worm is growing the tail | ||
757 | * is left untouched. | ||
758 | * @param struct worm *w The worm to move. w must not be NULL. | ||
759 | */ | ||
760 | static void move_worm(struct worm *w) | ||
761 | { | ||
762 | if (w->alive) { | ||
763 | /* determine the head point and its precessor */ | ||
764 | int headx = w->x[w->head]; | ||
765 | int heady = w->y[w->head]; | ||
766 | int prehead = (w->head + MAX_WORM_SEGMENTS - 1) % MAX_WORM_SEGMENTS; | ||
767 | int preheadx = w->x[prehead]; | ||
768 | int preheady = w->y[prehead]; | ||
769 | |||
770 | /* determine the old direction */ | ||
771 | int olddirx; | ||
772 | int olddiry; | ||
773 | if (headx == preheadx) { | ||
774 | olddirx = 0; | ||
775 | olddiry = (heady > preheady) ? 1 : -1; | ||
776 | } else { | ||
777 | olddiry = 0; | ||
778 | olddirx = (headx > preheadx) ? 1 : -1; | ||
779 | } | ||
780 | |||
781 | /* olddir == dir? | ||
782 | a change of direction means a new segment | ||
783 | has been opened */ | ||
784 | if (olddirx != w->dirx || | ||
785 | olddiry != w->diry) { | ||
786 | w->head = (w->head + 1) % MAX_WORM_SEGMENTS; | ||
787 | } | ||
788 | |||
789 | /* new head position */ | ||
790 | w->x[w->head] = headx + w->dirx; | ||
791 | w->y[w->head] = heady + w->diry; | ||
792 | |||
793 | |||
794 | /* while the worm is growing no tail procession is necessary */ | ||
795 | if (w->growing > 0) { | ||
796 | /* update the worms grow state */ | ||
797 | w->growing--; | ||
798 | } | ||
799 | |||
800 | /* if the worm isn't growing the tail has to be dragged */ | ||
801 | else { | ||
802 | /* index of the end of the tail segment */ | ||
803 | int tail_segment_end = (w->tail + 1) % MAX_WORM_SEGMENTS; | ||
804 | |||
805 | /* drag the end of the tail */ | ||
806 | /* only one coordinate has to be altered. Here it is | ||
807 | determined which one */ | ||
808 | int dir = 0; /* specifies wether the coord has to be in- or decreased */ | ||
809 | if (w->x[w->tail] == w->x[tail_segment_end]) { | ||
810 | dir = (w->y[w->tail] - w->y[tail_segment_end] < 0) ? 1 : -1; | ||
811 | w->y[w->tail] += dir; | ||
812 | } else { | ||
813 | dir = (w->x[w->tail] - w->x[tail_segment_end] < 0) ? 1 : -1; | ||
814 | w->x[w->tail] += dir; | ||
815 | } | ||
816 | |||
817 | /* when the tail has been dragged so far that it meets | ||
818 | the next segment start the tail segment is obsolete and | ||
819 | must be freed */ | ||
820 | if (w->x[w->tail] == w->x[tail_segment_end] && | ||
821 | w->y[w->tail] == w->y[tail_segment_end]){ | ||
822 | |||
823 | /* drop the last tail point */ | ||
824 | w->tail = tail_segment_end; | ||
825 | } | ||
826 | } | ||
827 | } | ||
828 | } | ||
829 | |||
830 | /** | ||
831 | * Draws the head and clears the tail of the worm in | ||
832 | * the display buffer. lcd_update() is NOT called thus | ||
833 | * the caller has to take care that the buffer is displayed. | ||
834 | */ | ||
835 | static void draw_worm(struct worm *w) | ||
836 | { | ||
837 | /* draw the new head */ | ||
838 | int x = w->x[w->head]; | ||
839 | int y = w->y[w->head]; | ||
840 | if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) { | ||
841 | rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y); | ||
842 | } | ||
843 | |||
844 | /* clear the space behind the worm */ | ||
845 | x = w->x[w->tail] ; | ||
846 | y = w->y[w->tail] ; | ||
847 | if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) { | ||
848 | rb->lcd_clearpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y); | ||
849 | } | ||
850 | } | ||
851 | |||
852 | /** | ||
853 | * Checks wether the coordinate is part of the worm. Returns | ||
854 | * true if any part of the worm was hit - including the head. | ||
855 | * @param x int The x coordinate | ||
856 | * @param y int The y coordinate | ||
857 | * @return int The index of the worm arrays that contain x, y. | ||
858 | * Returns -1 if the coordinates are not part of the worm. | ||
859 | */ | ||
860 | static int specific_worm_collision(struct worm *w, int x, int y) | ||
861 | { | ||
862 | int retVal = -1; | ||
863 | |||
864 | /* get_worm_array_length is expensive -> buffer the value */ | ||
865 | int wormLength = get_worm_array_length(w); | ||
866 | int i; | ||
867 | |||
868 | /* test each entry that is part of the worm */ | ||
869 | for (i = 0; i < wormLength && retVal == -1; i++) { | ||
870 | |||
871 | /* The iteration iterates the length of the worm. | ||
872 | Here's the conversion to the true indices within the worm arrays. */ | ||
873 | int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS; | ||
874 | int lineend = (linestart + 1) % MAX_WORM_SEGMENTS; | ||
875 | bool samex = (w->x[linestart] == x) && (w->x[lineend] == x); | ||
876 | bool samey = (w->y[linestart] == y) && (w->y[lineend] == y); | ||
877 | if (samex || samey){ | ||
878 | int test, min, max, tmp; | ||
879 | |||
880 | if (samey) { | ||
881 | min = w->x[linestart]; | ||
882 | max = w->x[lineend]; | ||
883 | test = x; | ||
884 | } else { | ||
885 | min = w->y[linestart]; | ||
886 | max = w->y[lineend]; | ||
887 | test = y; | ||
888 | } | ||
889 | |||
890 | tmp = min; | ||
891 | min = MIN(min, max); | ||
892 | max = MAX(tmp, max); | ||
893 | |||
894 | if (min <= test && test <= max) { | ||
895 | retVal = lineend; | ||
896 | } | ||
897 | } | ||
898 | } | ||
899 | return retVal; | ||
900 | } | ||
901 | |||
902 | /** | ||
903 | * Increases the length of the specified worm by marking | ||
904 | * that it may grow by len pixels. Note that the worm has | ||
905 | * to move to make the growing happen. | ||
906 | * @param worm *w The worm that is to be altered. | ||
907 | * @param int len A positive value specifying the amount of | ||
908 | * pixels the worm may grow. | ||
909 | */ | ||
910 | static void add_growing(struct worm *w, int len) { | ||
911 | w->growing += len; | ||
912 | } | ||
913 | |||
914 | /** | ||
915 | * Determins the worm that is at the coordinates x, y. The parameter | ||
916 | * w is a switch parameter that changes the functionality of worm_collision. | ||
917 | * If w is specified and x,y hits the head of w NULL is returned. | ||
918 | * This is a useful way to determine wether the head of w hits | ||
919 | * any worm but including itself but excluding its own head. | ||
920 | * (It hits always its own head ;)) | ||
921 | * If w is set to NULL worm_collision returns any worm including all heads | ||
922 | * that is at position of x,y. | ||
923 | * @param struct worm *w The worm of which the head should be excluded in | ||
924 | * the test. w may be set to NULL. | ||
925 | * @param int x The x coordinate that is checked | ||
926 | * @param int y The y coordinate that is checkec | ||
927 | * @return struct worm* The worm that has been hit by x,y. If no worm | ||
928 | * was at the position NULL is returned. | ||
929 | */ | ||
930 | static struct worm* worm_collision(struct worm *w, int x, int y){ | ||
931 | struct worm *retVal = NULL; | ||
932 | int i; | ||
933 | for (i = 0; (i < worm_count) && (retVal == NULL); i++) { | ||
934 | int collision_at = specific_worm_collision(&worms[i], x, y); | ||
935 | if (collision_at != -1) { | ||
936 | if (!(w == &worms[i] && collision_at == w->head)){ | ||
937 | retVal = &worms[i]; | ||
938 | } | ||
939 | } | ||
940 | } | ||
941 | return retVal; | ||
942 | } | ||
943 | |||
944 | /** | ||
945 | * Returns true if the head of the worm just has | ||
946 | * crossed the field boundaries. | ||
947 | * @return bool true if the worm just has wrapped. | ||
948 | */ | ||
949 | static bool field_collision(struct worm *w) | ||
950 | { | ||
951 | bool retVal = false; | ||
952 | if ((w->x[w->head] >= FIELD_RECT_WIDTH) || | ||
953 | (w->y[w->head] >= FIELD_RECT_HEIGHT) || | ||
954 | (w->x[w->head] < 0) || | ||
955 | (w->y[w->head] < 0)) | ||
956 | { | ||
957 | retVal = true; | ||
958 | } | ||
959 | return retVal; | ||
960 | } | ||
961 | |||
962 | |||
963 | /** | ||
964 | * Returns true if the specified coordinates are within the | ||
965 | * field specified by the FIELD_RECT_XXX constants. | ||
966 | * @param int x The x coordinate of the point that is investigated | ||
967 | * @param int y The y coordinate of the point that is investigated | ||
968 | * @return bool Returns false if x,y specifies a point outside the | ||
969 | * field of worms. | ||
970 | */ | ||
971 | static bool is_in_field_rect(int x, int y) { | ||
972 | bool retVal = false; | ||
973 | retVal = (x >= 0 && x < FIELD_RECT_WIDTH && | ||
974 | y >= 0 && y < FIELD_RECT_HEIGHT); | ||
975 | return retVal; | ||
976 | } | ||
977 | |||
978 | /** | ||
979 | * Checks and returns wether the head of the w | ||
980 | * is colliding with something currently. | ||
981 | * @return int One of the values: | ||
982 | * COLLISION_NONE | ||
983 | * COLLISION_w | ||
984 | * COLLISION_FOOD | ||
985 | * COLLISION_ARGH | ||
986 | * COLLISION_FIELD | ||
987 | */ | ||
988 | static int check_collision(struct worm *w) | ||
989 | { | ||
990 | int retVal = COLLISION_NONE; | ||
991 | |||
992 | if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) | ||
993 | retVal = COLLISION_WORM; | ||
994 | |||
995 | if (food_collision(w->x[w->head], w->y[w->head]) >= 0) | ||
996 | retVal = COLLISION_FOOD; | ||
997 | |||
998 | if (argh_collision(w->x[w->head], w->y[w->head]) >= 0) | ||
999 | retVal = COLLISION_ARGH; | ||
1000 | |||
1001 | if (field_collision(w)) | ||
1002 | retVal = COLLISION_FIELD; | ||
1003 | |||
1004 | return retVal; | ||
1005 | } | ||
1006 | |||
1007 | /** | ||
1008 | * Returns the index of the food that is closest to the point | ||
1009 | * specified by x, y. This index may be used in the foodx and | ||
1010 | * foody arrays. | ||
1011 | * @param int x The x coordinate of the point | ||
1012 | * @param int y The y coordinate of the point | ||
1013 | * @return int A value usable as index in foodx and foody. | ||
1014 | */ | ||
1015 | static int get_nearest_food(int x, int y){ | ||
1016 | int nearestfood = 0; | ||
1017 | int olddistance = FIELD_RECT_WIDTH + FIELD_RECT_HEIGHT; | ||
1018 | int deltax = 0; | ||
1019 | int deltay = 0; | ||
1020 | int foodindex; | ||
1021 | for (foodindex = 0; foodindex < MAX_FOOD; foodindex++) { | ||
1022 | int distance; | ||
1023 | deltax = foodx[foodindex] - x; | ||
1024 | deltay = foody[foodindex] - y; | ||
1025 | deltax = deltax > 0 ? deltax : deltax * (-1); | ||
1026 | deltay = deltay > 0 ? deltay : deltay * (-1); | ||
1027 | distance = deltax + deltay; | ||
1028 | |||
1029 | if (distance < olddistance) { | ||
1030 | olddistance = distance; | ||
1031 | nearestfood = foodindex; | ||
1032 | } | ||
1033 | } | ||
1034 | return nearestfood; | ||
1035 | } | ||
1036 | |||
1037 | /** | ||
1038 | * Returns wether the specified position is next to the worm | ||
1039 | * and in the direction the worm looks. Use this method to | ||
1040 | * test wether this position would be hit with the next move of | ||
1041 | * the worm unless the worm changes its direction. | ||
1042 | * @param struct worm *w - The worm to be investigated | ||
1043 | * @param int x - The x coordinate of the position to test. | ||
1044 | * @param int y - The y coordinate of the position to test. | ||
1045 | * @return Returns true if the worm will hit the position unless | ||
1046 | * it change its direction before the next move. | ||
1047 | */ | ||
1048 | static bool is_in_front_of_worm(struct worm *w, int x, int y) { | ||
1049 | bool infront = false; | ||
1050 | int deltax = x - w->x[w->head]; | ||
1051 | int deltay = y - w->y[w->head]; | ||
1052 | |||
1053 | if (w->dirx == 0) { | ||
1054 | infront = (w->diry * deltay) > 0; | ||
1055 | } else { | ||
1056 | infront = (w->dirx * deltax) > 0; | ||
1057 | } | ||
1058 | return infront; | ||
1059 | } | ||
1060 | |||
1061 | /** | ||
1062 | * Returns true if the worm will collide with the next move unless | ||
1063 | * it changes its direction. | ||
1064 | * @param struct worm *w - The worm to be investigated. | ||
1065 | * @return Returns true if the worm will collide with the next move | ||
1066 | * unless it changes its direction. | ||
1067 | */ | ||
1068 | static bool will_worm_collide(struct worm *w) { | ||
1069 | int x = w->x[w->head] + w->dirx; | ||
1070 | int y = w->y[w->head] + w->diry; | ||
1071 | bool retVal = !is_in_field_rect(x, y); | ||
1072 | if (!retVal) { | ||
1073 | retVal = (argh_collision(x, y) != -1); | ||
1074 | } | ||
1075 | |||
1076 | if (!retVal) { | ||
1077 | retVal = (worm_collision(w, x, y) != NULL); | ||
1078 | } | ||
1079 | return retVal; | ||
1080 | } | ||
1081 | |||
1082 | /** | ||
1083 | * This function | ||
1084 | * may be used to be stored in worm.fetch_worm_direction for | ||
1085 | * worms that are not controlled by humans but by artificial stupidity. | ||
1086 | * A direction is searched that doesn't lead to collision but to the nearest | ||
1087 | * food - but not very intelligent. The direction is written to the specified | ||
1088 | * worm. | ||
1089 | * @param struct worm *w - The worm of which the direction | ||
1090 | * is altered. | ||
1091 | */ | ||
1092 | static void virtual_player(struct worm *w) { | ||
1093 | bool isright; | ||
1094 | int plana, planb, planc; | ||
1095 | /* find the next lunch */ | ||
1096 | int nearestfood = get_nearest_food(w->x[w->head], w->y[w->head]); | ||
1097 | |||
1098 | /* determine in which direction it is */ | ||
1099 | |||
1100 | /* in front of me? */ | ||
1101 | bool infront = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]); | ||
1102 | |||
1103 | /* left right of me? */ | ||
1104 | int olddir = get_worm_dir(w); | ||
1105 | set_worm_dir(w, (olddir + 1) % 4); | ||
1106 | isright = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]); | ||
1107 | set_worm_dir(w, olddir); | ||
1108 | |||
1109 | /* detect situation, set strategy */ | ||
1110 | if (infront) { | ||
1111 | if (isright) { | ||
1112 | plana = olddir; | ||
1113 | planb = (olddir + 1) % 4; | ||
1114 | planc = (olddir + 3) % 4; | ||
1115 | } else { | ||
1116 | plana = olddir; | ||
1117 | planb = (olddir + 3) % 4; | ||
1118 | planc = (olddir + 1) % 4; | ||
1119 | } | ||
1120 | } else { | ||
1121 | if (isright) { | ||
1122 | plana = (olddir + 1) % 4; | ||
1123 | planb = olddir; | ||
1124 | planc = (olddir + 3) % 4; | ||
1125 | } else { | ||
1126 | plana = (olddir + 3) % 4; | ||
1127 | planb = olddir; | ||
1128 | planc = (olddir + 1) % 4; | ||
1129 | } | ||
1130 | } | ||
1131 | |||
1132 | /* test for collision */ | ||
1133 | set_worm_dir(w, plana); | ||
1134 | if (will_worm_collide(w)){ | ||
1135 | |||
1136 | /* plan b */ | ||
1137 | set_worm_dir(w, planb); | ||
1138 | |||
1139 | /* test for collision */ | ||
1140 | if (will_worm_collide(w)) { | ||
1141 | |||
1142 | /* plan c */ | ||
1143 | set_worm_dir(w, planc); | ||
1144 | } | ||
1145 | } | ||
1146 | } | ||
1147 | |||
1148 | /** | ||
1149 | * prints out the score board with all the status information | ||
1150 | * about the game. | ||
1151 | */ | ||
1152 | static void score_board(void) | ||
1153 | { | ||
1154 | char buf[15]; | ||
1155 | char* buf2 = NULL; | ||
1156 | int i; | ||
1157 | int y = 0; | ||
1158 | rb->lcd_clearrect(FIELD_RECT_WIDTH + 2, 0, LCD_WIDTH - FIELD_RECT_WIDTH - 2, LCD_HEIGHT); | ||
1159 | for (i = 0; i < worm_count; i++) { | ||
1160 | int score = get_score(&worms[i]); | ||
1161 | |||
1162 | /* high score */ | ||
1163 | if (worms[i].fetch_worm_direction != virtual_player){ | ||
1164 | if (highscore < score) { | ||
1165 | highscore = score; | ||
1166 | } | ||
1167 | } | ||
1168 | |||
1169 | /* length */ | ||
1170 | rb->snprintf(buf, sizeof (buf),"Len:%d", score); | ||
1171 | |||
1172 | /* worm state */ | ||
1173 | switch (check_collision(&worms[i])) { | ||
1174 | case COLLISION_NONE: | ||
1175 | if (worms[i].growing > 0) | ||
1176 | buf2 = "Growing"; | ||
1177 | else { | ||
1178 | if (worms[i].alive) | ||
1179 | buf2 = "Hungry"; | ||
1180 | else | ||
1181 | buf2 = "Wormed"; | ||
1182 | } | ||
1183 | break; | ||
1184 | |||
1185 | case COLLISION_WORM: | ||
1186 | buf2 = "Wormed"; | ||
1187 | break; | ||
1188 | |||
1189 | case COLLISION_FOOD: | ||
1190 | buf2 = "Growing"; | ||
1191 | break; | ||
1192 | |||
1193 | case COLLISION_ARGH: | ||
1194 | buf2 = "Argh"; | ||
1195 | break; | ||
1196 | |||
1197 | case COLLISION_FIELD: | ||
1198 | buf2 = "Crashed"; | ||
1199 | break; | ||
1200 | } | ||
1201 | rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y , buf); | ||
1202 | rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y+8, buf2); | ||
1203 | |||
1204 | if (!worms[i].alive){ | ||
1205 | rb->lcd_invertrect(FIELD_RECT_WIDTH + 2, y, | ||
1206 | LCD_WIDTH - FIELD_RECT_WIDTH - 2, 17); | ||
1207 | } | ||
1208 | y += 19; | ||
1209 | } | ||
1210 | rb->snprintf(buf , sizeof(buf), "Hs: %d", highscore); | ||
1211 | #ifndef DEBUG_WORMLET | ||
1212 | rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, buf); | ||
1213 | #else | ||
1214 | rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, debugout); | ||
1215 | #endif | ||
1216 | } | ||
1217 | |||
1218 | /** | ||
1219 | * Checks for collisions of the worm and its environment and | ||
1220 | * takes appropriate actions like growing the worm or killing it. | ||
1221 | * @return bool Returns true if the worm is dead. Returns | ||
1222 | * false if the worm is healthy, up and creeping. | ||
1223 | */ | ||
1224 | static bool process_collisions(struct worm *w) | ||
1225 | { | ||
1226 | int index = -1; | ||
1227 | |||
1228 | w->alive &= !field_collision(w); | ||
1229 | |||
1230 | if (w->alive) { | ||
1231 | |||
1232 | /* check if food was eaten */ | ||
1233 | index = food_collision(w->x[w->head], w->y[w->head]); | ||
1234 | if (index != -1){ | ||
1235 | int i; | ||
1236 | |||
1237 | clear_food(index); | ||
1238 | make_food(index); | ||
1239 | draw_food(index); | ||
1240 | |||
1241 | for (i = 0; i < ARGHS_PER_FOOD; i++) { | ||
1242 | argh_count++; | ||
1243 | if (argh_count > MAX_ARGH) | ||
1244 | argh_count = MAX_ARGH; | ||
1245 | make_argh(argh_count - 1); | ||
1246 | draw_argh(argh_count - 1); | ||
1247 | } | ||
1248 | |||
1249 | add_growing(w, WORM_PER_FOOD); | ||
1250 | |||
1251 | draw_worm(w); | ||
1252 | } | ||
1253 | |||
1254 | /* check if argh was eaten */ | ||
1255 | else { | ||
1256 | index = argh_collision(w->x[w->head], w->y[w->head]); | ||
1257 | if (index != -1) { | ||
1258 | w->alive = false; | ||
1259 | } | ||
1260 | else { | ||
1261 | if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) { | ||
1262 | w->alive = false; | ||
1263 | } | ||
1264 | } | ||
1265 | } | ||
1266 | } | ||
1267 | return !w->alive; | ||
1268 | } | ||
1269 | |||
1270 | /** | ||
1271 | * The main loop of the game. | ||
1272 | * @return bool Returns true if the game ended | ||
1273 | * with a dead worm. Returns false if the user | ||
1274 | * aborted the game manually. | ||
1275 | */ | ||
1276 | static bool run(void) | ||
1277 | { | ||
1278 | int button = 0; | ||
1279 | int wormDead = false; | ||
1280 | |||
1281 | /* ticks are counted to compensate speed variations */ | ||
1282 | long cycle_start = 0, cycle_end = 0; | ||
1283 | #ifdef DEBUG_WORMLET | ||
1284 | int ticks_to_max_cycle_reset = 20; | ||
1285 | long max_cycle = 0; | ||
1286 | char buf[20]; | ||
1287 | #endif | ||
1288 | |||
1289 | /* initialize the board and so on */ | ||
1290 | init_wormlet(); | ||
1291 | |||
1292 | cycle_start = *rb->current_tick; | ||
1293 | /* change the direction of the worm */ | ||
1294 | while (button != BUTTON_OFF && ! wormDead) | ||
1295 | { | ||
1296 | int i; | ||
1297 | long cycle_duration ; | ||
1298 | switch (button) { | ||
1299 | case BUTTON_UP: | ||
1300 | if (players == 1 && !use_remote) { | ||
1301 | player1_dir = NORTH; | ||
1302 | } | ||
1303 | break; | ||
1304 | |||
1305 | case BUTTON_DOWN: | ||
1306 | if (players == 1 && !use_remote) { | ||
1307 | player1_dir = SOUTH; | ||
1308 | } | ||
1309 | break; | ||
1310 | |||
1311 | case BUTTON_LEFT: | ||
1312 | if (players != 1 || use_remote) { | ||
1313 | player1_dir = (player1_dir + 3) % 4; | ||
1314 | } else { | ||
1315 | player1_dir = WEST; | ||
1316 | } | ||
1317 | break; | ||
1318 | |||
1319 | case BUTTON_RIGHT: | ||
1320 | if (players != 1 || use_remote) { | ||
1321 | player1_dir = (player1_dir + 1) % 4; | ||
1322 | } else { | ||
1323 | player1_dir = EAST; | ||
1324 | } | ||
1325 | break; | ||
1326 | |||
1327 | case BUTTON_F2: | ||
1328 | player2_dir = (player2_dir + 3) % 4; | ||
1329 | break; | ||
1330 | |||
1331 | case BUTTON_F3: | ||
1332 | player2_dir = (player2_dir + 1) % 4; | ||
1333 | break; | ||
1334 | |||
1335 | case BUTTON_RC_VOL_UP: | ||
1336 | player3_dir = (player3_dir + 1) % 4; | ||
1337 | break; | ||
1338 | |||
1339 | case BUTTON_RC_VOL_DOWN: | ||
1340 | player3_dir = (player3_dir + 3) % 4; | ||
1341 | break; | ||
1342 | |||
1343 | case BUTTON_PLAY: | ||
1344 | do { | ||
1345 | button = rb->button_get(true); | ||
1346 | } while (button != BUTTON_PLAY && | ||
1347 | button != BUTTON_OFF && | ||
1348 | button != BUTTON_ON); | ||
1349 | break; | ||
1350 | } | ||
1351 | |||
1352 | for (i = 0; i < worm_count; i++) { | ||
1353 | worms[i].fetch_worm_direction(&worms[i]); | ||
1354 | } | ||
1355 | |||
1356 | wormDead = true; | ||
1357 | for (i = 0; i < worm_count; i++){ | ||
1358 | struct worm *w = &worms[i]; | ||
1359 | move_worm(w); | ||
1360 | wormDead &= process_collisions(w); | ||
1361 | draw_worm(w); | ||
1362 | } | ||
1363 | score_board(); | ||
1364 | rb->lcd_update(); | ||
1365 | if (button == BUTTON_ON) { | ||
1366 | wormDead = true; | ||
1367 | } | ||
1368 | |||
1369 | /* here the wormlet game cycle ends | ||
1370 | thus the current tick is stored | ||
1371 | as end time */ | ||
1372 | cycle_end = *rb->current_tick; | ||
1373 | |||
1374 | /* The duration of the game cycle */ | ||
1375 | cycle_duration = cycle_end - cycle_start; | ||
1376 | cycle_duration = MAX(0, cycle_duration); | ||
1377 | cycle_duration = MIN(SPEED -1, cycle_duration); | ||
1378 | |||
1379 | |||
1380 | #ifdef DEBUG_WORMLET | ||
1381 | ticks_to_max_cycle_reset--; | ||
1382 | if (ticks_to_max_cycle_reset <= 0) { | ||
1383 | max_cycle = 0; | ||
1384 | } | ||
1385 | |||
1386 | if (max_cycle < cycle_duration) { | ||
1387 | max_cycle = cycle_duration; | ||
1388 | ticks_to_max_cycle_reset = 20; | ||
1389 | } | ||
1390 | rb->snprintf(buf, sizeof buf, "ticks %d", max_cycle); | ||
1391 | set_debug_out(buf); | ||
1392 | #endif | ||
1393 | /* adjust the number of ticks to wait for a button. | ||
1394 | This ensures that a complete game cycle including | ||
1395 | user input runs in constant time */ | ||
1396 | button = rb->button_get_w_tmo(SPEED - cycle_duration); | ||
1397 | cycle_start = *rb->current_tick; | ||
1398 | } | ||
1399 | return wormDead; | ||
1400 | } | ||
1401 | |||
1402 | #ifdef DEBUG_WORMLET | ||
1403 | |||
1404 | /** | ||
1405 | * Just a test routine that checks that worm_food_collision works | ||
1406 | * in some typical situations. | ||
1407 | */ | ||
1408 | static void test_worm_food_collision(void) { | ||
1409 | int collision_count = 0; | ||
1410 | int i; | ||
1411 | rb->lcd_clear_display(); | ||
1412 | init_worm(&worms[0], 10, 10); | ||
1413 | add_growing(&worms[0], 10); | ||
1414 | set_worm_dir(&worms[0], EAST); | ||
1415 | for (i = 0; i < 10; i++) { | ||
1416 | move_worm(&worms[0]); | ||
1417 | draw_worm(&worms[0]); | ||
1418 | } | ||
1419 | |||
1420 | set_worm_dir(&worms[0], SOUTH); | ||
1421 | for (i = 0; i < 10; i++) { | ||
1422 | move_worm(&worms[0]); | ||
1423 | draw_worm(&worms[0]); | ||
1424 | } | ||
1425 | |||
1426 | foodx[0] = 15; | ||
1427 | foody[0] = 12; | ||
1428 | for (foody[0] = 20; foody[0] > 0; foody[0] --) { | ||
1429 | char buf[20]; | ||
1430 | bool collision; | ||
1431 | draw_worm(&worms[0]); | ||
1432 | draw_food(0); | ||
1433 | collision = worm_food_collision(&worms[0], 0); | ||
1434 | if (collision) { | ||
1435 | collision_count++; | ||
1436 | } | ||
1437 | rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count); | ||
1438 | rb->lcd_putsxy(0, LCD_HEIGHT -8, buf); | ||
1439 | rb->lcd_update(); | ||
1440 | } | ||
1441 | if (collision_count != FOOD_SIZE) { | ||
1442 | rb->button_get(true); | ||
1443 | } | ||
1444 | |||
1445 | |||
1446 | foody[0] = 15; | ||
1447 | for (foodx[0] = 30; foodx[0] > 0; foodx[0] --) { | ||
1448 | char buf[20]; | ||
1449 | bool collision; | ||
1450 | draw_worm(&worms[0]); | ||
1451 | draw_food(0); | ||
1452 | collision = worm_food_collision(&worms[0], 0); | ||
1453 | if (collision) { | ||
1454 | collision_count ++; | ||
1455 | } | ||
1456 | rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count); | ||
1457 | rb->lcd_putsxy(0, LCD_HEIGHT -8, buf); | ||
1458 | rb->lcd_update(); | ||
1459 | } | ||
1460 | if (collision_count != FOOD_SIZE * 2) { | ||
1461 | rb->button_get(true); | ||
1462 | } | ||
1463 | |||
1464 | } | ||
1465 | |||
1466 | |||
1467 | static bool expensive_worm_in_rect(struct worm *w, int rx, int ry, int rw, int rh){ | ||
1468 | int x, y; | ||
1469 | bool retVal = false; | ||
1470 | for (x = rx; x < rx + rw; x++){ | ||
1471 | for (y = ry; y < ry + rh; y++) { | ||
1472 | if (specific_worm_collision(w, x, y) != -1) { | ||
1473 | retVal = true; | ||
1474 | } | ||
1475 | } | ||
1476 | } | ||
1477 | return retVal; | ||
1478 | } | ||
1479 | |||
1480 | static void test_worm_argh_collision(void) { | ||
1481 | int i; | ||
1482 | int dir; | ||
1483 | int collision_count = 0; | ||
1484 | rb->lcd_clear_display(); | ||
1485 | init_worm(&worms[0], 10, 10); | ||
1486 | add_growing(&worms[0], 40); | ||
1487 | for (dir = 0; dir < 4; dir++) { | ||
1488 | set_worm_dir(&worms[0], (EAST + dir) % 4); | ||
1489 | for (i = 0; i < 10; i++) { | ||
1490 | move_worm(&worms[0]); | ||
1491 | draw_worm(&worms[0]); | ||
1492 | } | ||
1493 | } | ||
1494 | |||
1495 | arghx[0] = 12; | ||
1496 | for (arghy[0] = 0; arghy[0] < FIELD_RECT_HEIGHT - ARGH_SIZE; arghy[0]++){ | ||
1497 | char buf[20]; | ||
1498 | bool collision; | ||
1499 | draw_argh(0); | ||
1500 | collision = worm_argh_collision(&worms[0], 0); | ||
1501 | if (collision) { | ||
1502 | collision_count ++; | ||
1503 | } | ||
1504 | rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count); | ||
1505 | rb->lcd_putsxy(0, LCD_HEIGHT -8, buf); | ||
1506 | rb->lcd_update(); | ||
1507 | } | ||
1508 | if (collision_count != ARGH_SIZE * 2) { | ||
1509 | rb->button_get(true); | ||
1510 | } | ||
1511 | |||
1512 | arghy[0] = 12; | ||
1513 | for (arghx[0] = 0; arghx[0] < FIELD_RECT_HEIGHT - ARGH_SIZE; arghx[0]++){ | ||
1514 | char buf[20]; | ||
1515 | bool collision; | ||
1516 | draw_argh(0); | ||
1517 | collision = worm_argh_collision(&worms[0], 0); | ||
1518 | if (collision) { | ||
1519 | collision_count ++; | ||
1520 | } | ||
1521 | rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count); | ||
1522 | rb->lcd_putsxy(0, LCD_HEIGHT -8, buf); | ||
1523 | rb->lcd_update(); | ||
1524 | } | ||
1525 | if (collision_count != ARGH_SIZE * 4) { | ||
1526 | rb->button_get(true); | ||
1527 | } | ||
1528 | } | ||
1529 | |||
1530 | static int testline_in_rect(void) { | ||
1531 | int testfailed = -1; | ||
1532 | |||
1533 | int rx = 10; | ||
1534 | int ry = 15; | ||
1535 | int rw = 20; | ||
1536 | int rh = 25; | ||
1537 | |||
1538 | /* Test 1 */ | ||
1539 | int x1 = 12; | ||
1540 | int y1 = 8; | ||
1541 | int x2 = 12; | ||
1542 | int y2 = 42; | ||
1543 | |||
1544 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1545 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1546 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1547 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1548 | rb->lcd_update(); | ||
1549 | rb->lcd_putsxy(0, 0, "failed 1"); | ||
1550 | rb->button_get(true); | ||
1551 | testfailed = 1; | ||
1552 | } | ||
1553 | |||
1554 | /* test 2 */ | ||
1555 | y2 = 20; | ||
1556 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1557 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1558 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1559 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1560 | rb->lcd_putsxy(0, 0, "failed 2"); | ||
1561 | rb->lcd_update(); | ||
1562 | rb->button_get(true); | ||
1563 | testfailed = 2; | ||
1564 | } | ||
1565 | |||
1566 | /* test 3 */ | ||
1567 | y1 = 30; | ||
1568 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1569 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1570 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1571 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1572 | rb->lcd_putsxy(0, 0, "failed 3"); | ||
1573 | rb->lcd_update(); | ||
1574 | rb->button_get(true); | ||
1575 | testfailed = 3; | ||
1576 | } | ||
1577 | |||
1578 | /* test 4 */ | ||
1579 | y2 = 45; | ||
1580 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1581 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1582 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1583 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1584 | rb->lcd_putsxy(0, 0, "failed 4"); | ||
1585 | rb->lcd_update(); | ||
1586 | rb->button_get(true); | ||
1587 | testfailed = 4; | ||
1588 | } | ||
1589 | |||
1590 | /* test 5 */ | ||
1591 | y1 = 50; | ||
1592 | if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) || | ||
1593 | line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1594 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1595 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1596 | rb->lcd_putsxy(0, 0, "failed 5"); | ||
1597 | rb->lcd_update(); | ||
1598 | rb->button_get(true); | ||
1599 | testfailed = 5; | ||
1600 | } | ||
1601 | |||
1602 | /* test 6 */ | ||
1603 | y1 = 5; | ||
1604 | y2 = 7; | ||
1605 | if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) || | ||
1606 | line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1607 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1608 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1609 | rb->lcd_putsxy(0, 0, "failed 6"); | ||
1610 | rb->lcd_update(); | ||
1611 | rb->button_get(true); | ||
1612 | testfailed = 6; | ||
1613 | } | ||
1614 | |||
1615 | /* test 7 */ | ||
1616 | x1 = 8; | ||
1617 | y1 = 20; | ||
1618 | x2 = 35; | ||
1619 | y2 = 20; | ||
1620 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1621 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1622 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1623 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1624 | rb->lcd_putsxy(0, 0, "failed 7"); | ||
1625 | rb->lcd_update(); | ||
1626 | rb->button_get(true); | ||
1627 | testfailed = 7; | ||
1628 | } | ||
1629 | |||
1630 | /* test 8 */ | ||
1631 | x2 = 12; | ||
1632 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1633 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1634 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1635 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1636 | rb->lcd_putsxy(0, 0, "failed 8"); | ||
1637 | rb->lcd_update(); | ||
1638 | rb->button_get(true); | ||
1639 | testfailed = 8; | ||
1640 | } | ||
1641 | |||
1642 | /* test 9 */ | ||
1643 | x1 = 25; | ||
1644 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1645 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1646 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1647 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1648 | rb->lcd_putsxy(0, 0, "failed 9"); | ||
1649 | rb->lcd_update(); | ||
1650 | rb->button_get(true); | ||
1651 | testfailed = 9; | ||
1652 | } | ||
1653 | |||
1654 | /* test 10 */ | ||
1655 | x2 = 37; | ||
1656 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1657 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1658 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1659 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1660 | rb->lcd_putsxy(0, 0, "failed 10"); | ||
1661 | rb->lcd_update(); | ||
1662 | rb->button_get(true); | ||
1663 | testfailed = 10; | ||
1664 | } | ||
1665 | |||
1666 | /* test 11 */ | ||
1667 | x1 = 42; | ||
1668 | if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) || | ||
1669 | line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1670 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1671 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1672 | rb->lcd_putsxy(0, 0, "failed 11"); | ||
1673 | rb->lcd_update(); | ||
1674 | rb->button_get(true); | ||
1675 | testfailed = 11; | ||
1676 | } | ||
1677 | |||
1678 | /* test 12 */ | ||
1679 | x1 = 5; | ||
1680 | x2 = 7; | ||
1681 | if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) || | ||
1682 | line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1683 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1684 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1685 | rb->lcd_putsxy(0, 0, "failed 12"); | ||
1686 | rb->lcd_update(); | ||
1687 | rb->button_get(true); | ||
1688 | testfailed = 12; | ||
1689 | } | ||
1690 | |||
1691 | /* test 13 */ | ||
1692 | rx = 9; | ||
1693 | ry = 15; | ||
1694 | rw = FOOD_SIZE; | ||
1695 | rh = FOOD_SIZE; | ||
1696 | |||
1697 | x1 = 10; | ||
1698 | y1 = 10; | ||
1699 | x2 = 10; | ||
1700 | y2 = 20; | ||
1701 | if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1702 | line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) { | ||
1703 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1704 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1705 | rb->lcd_putsxy(0, 0, "failed 13"); | ||
1706 | rb->lcd_update(); | ||
1707 | rb->button_get(true); | ||
1708 | testfailed = 13; | ||
1709 | } | ||
1710 | |||
1711 | /* test 14 */ | ||
1712 | rx = 9; | ||
1713 | ry = 15; | ||
1714 | rw = 4; | ||
1715 | rh = 4; | ||
1716 | |||
1717 | x1 = 10; | ||
1718 | y1 = 10; | ||
1719 | x2 = 10; | ||
1720 | y2 = 19; | ||
1721 | if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1722 | line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) { | ||
1723 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1724 | rb->lcd_invertrect(rx, ry, rw, rh); | ||
1725 | rb->lcd_putsxy(0, 0, "failed 14"); | ||
1726 | rb->lcd_update(); | ||
1727 | rb->button_get(true); | ||
1728 | testfailed = 14; | ||
1729 | } | ||
1730 | |||
1731 | rb->lcd_clear_display(); | ||
1732 | |||
1733 | return testfailed; | ||
1734 | } | ||
1735 | |||
1736 | /** | ||
1737 | * Just a test routine to test wether specific_worm_collision might work properly | ||
1738 | */ | ||
1739 | static int test_specific_worm_collision(void) { | ||
1740 | int collisions = 0; | ||
1741 | int dir; | ||
1742 | int x = 0; | ||
1743 | int y = 0; | ||
1744 | char buf[20]; | ||
1745 | rb->lcd_clear_display(); | ||
1746 | init_worm(&worms[0], 10, 20); | ||
1747 | add_growing(&worms[0], 20 - INITIAL_WORM_LENGTH); | ||
1748 | |||
1749 | for (dir = EAST; dir < EAST + 4; dir++) { | ||
1750 | int i; | ||
1751 | set_worm_dir(&worms[0], dir % 4); | ||
1752 | for (i = 0; i < 5; i++) { | ||
1753 | if (!(dir % 4 == NORTH && i == 9)) { | ||
1754 | move_worm(&worms[0]); | ||
1755 | draw_worm(&worms[0]); | ||
1756 | } | ||
1757 | } | ||
1758 | } | ||
1759 | |||
1760 | for (y = 15; y < 30; y ++){ | ||
1761 | for (x = 5; x < 20; x++) { | ||
1762 | if (specific_worm_collision(&worms[0], x, y) != -1) { | ||
1763 | collisions ++; | ||
1764 | } | ||
1765 | rb->lcd_invertpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y); | ||
1766 | rb->snprintf(buf, sizeof buf, "collisions %d", collisions); | ||
1767 | rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf); | ||
1768 | rb->lcd_update(); | ||
1769 | } | ||
1770 | } | ||
1771 | if (collisions != 21) { | ||
1772 | rb->button_get(true); | ||
1773 | } | ||
1774 | return collisions; | ||
1775 | } | ||
1776 | |||
1777 | static void test_make_argh(void){ | ||
1778 | int dir; | ||
1779 | int seed = 0; | ||
1780 | int hit = 0; | ||
1781 | int failures = 0; | ||
1782 | int last_failures = 0; | ||
1783 | int i, worm_idx; | ||
1784 | rb->lcd_clear_display(); | ||
1785 | worm_count = 3; | ||
1786 | |||
1787 | for (worm_idx = 0; worm_idx < worm_count; worm_idx++) { | ||
1788 | init_worm(&worms[worm_idx], 10 + worm_idx * 20, 20); | ||
1789 | add_growing(&worms[worm_idx], 40 - INITIAL_WORM_LENGTH); | ||
1790 | } | ||
1791 | |||
1792 | for (dir = EAST; dir < EAST + 4; dir++) { | ||
1793 | for (worm_idx = 0; worm_idx < worm_count; worm_idx++) { | ||
1794 | set_worm_dir(&worms[worm_idx], dir % 4); | ||
1795 | for (i = 0; i < 10; i++) { | ||
1796 | if (!(dir % 4 == NORTH && i == 9)) { | ||
1797 | move_worm(&worms[worm_idx]); | ||
1798 | draw_worm(&worms[worm_idx]); | ||
1799 | } | ||
1800 | } | ||
1801 | } | ||
1802 | } | ||
1803 | |||
1804 | rb->lcd_update(); | ||
1805 | |||
1806 | for (seed = 0; hit < 20; seed += 2) { | ||
1807 | char buf[20]; | ||
1808 | int x, y; | ||
1809 | rb->srand(seed); | ||
1810 | x = rb->rand() % (FIELD_RECT_WIDTH - ARGH_SIZE); | ||
1811 | y = rb->rand() % (FIELD_RECT_HEIGHT - ARGH_SIZE); | ||
1812 | |||
1813 | for (worm_idx = 0; worm_idx < worm_count; worm_idx++){ | ||
1814 | if (expensive_worm_in_rect(&worms[worm_idx], x, y, ARGH_SIZE, ARGH_SIZE)) { | ||
1815 | int tries = 0; | ||
1816 | rb->srand(seed); | ||
1817 | |||
1818 | tries = make_argh(0); | ||
1819 | if ((x == arghx[0] && y == arghy[0]) || tries < 2) { | ||
1820 | failures ++; | ||
1821 | } | ||
1822 | |||
1823 | rb->snprintf(buf, sizeof buf, "(%d;%d) fail%d try%d", x, y, failures, tries); | ||
1824 | rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf); | ||
1825 | rb->lcd_update(); | ||
1826 | rb->lcd_invertrect(x + FIELD_RECT_X, y+ FIELD_RECT_Y, ARGH_SIZE, ARGH_SIZE); | ||
1827 | rb->lcd_update(); | ||
1828 | draw_argh(0); | ||
1829 | rb->lcd_update(); | ||
1830 | rb->lcd_invertrect(x + FIELD_RECT_X, y + FIELD_RECT_Y, ARGH_SIZE, ARGH_SIZE); | ||
1831 | rb->lcd_clearrect(arghx[0] + FIELD_RECT_X, arghy[0] + FIELD_RECT_Y, ARGH_SIZE, ARGH_SIZE); | ||
1832 | |||
1833 | if (failures > last_failures) { | ||
1834 | rb->button_get(true); | ||
1835 | } | ||
1836 | last_failures = failures; | ||
1837 | hit ++; | ||
1838 | } | ||
1839 | } | ||
1840 | } | ||
1841 | } | ||
1842 | |||
1843 | static void test_worm_argh_collision_in_moves(void) { | ||
1844 | int hit_count = 0; | ||
1845 | int i; | ||
1846 | rb->lcd_clear_display(); | ||
1847 | init_worm(&worms[0], 10, 20); | ||
1848 | |||
1849 | arghx[0] = 20; | ||
1850 | arghy[0] = 18; | ||
1851 | draw_argh(0); | ||
1852 | |||
1853 | set_worm_dir(&worms[0], EAST); | ||
1854 | for (i = 0; i < 20; i++) { | ||
1855 | char buf[20]; | ||
1856 | move_worm(&worms[0]); | ||
1857 | draw_worm(&worms[0]); | ||
1858 | if (worm_argh_collision_in_moves(&worms[0], 0, 5)){ | ||
1859 | hit_count ++; | ||
1860 | } | ||
1861 | rb->snprintf(buf, sizeof buf, "in 5 moves hits: %d", hit_count); | ||
1862 | rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf); | ||
1863 | rb->lcd_update(); | ||
1864 | } | ||
1865 | if (hit_count != ARGH_SIZE + 5) { | ||
1866 | rb->button_get(true); | ||
1867 | } | ||
1868 | } | ||
1869 | #endif /* DEBUG_WORMLET */ | ||
1870 | |||
1871 | extern bool use_old_rect; | ||
1872 | |||
1873 | /** | ||
1874 | * Main entry point | ||
1875 | */ | ||
1876 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | ||
1877 | { | ||
1878 | bool worm_dead = false; | ||
1879 | int button; | ||
1880 | |||
1881 | TEST_PLUGIN_API(api); | ||
1882 | (void)(parameter); | ||
1883 | |||
1884 | rb = api; | ||
1885 | rb->lcd_setfont(FONT_SYSFIXED); | ||
1886 | |||
1887 | #ifdef DEBUG_WORMLET | ||
1888 | testline_in_rect(); | ||
1889 | test_worm_argh_collision_in_moves(); | ||
1890 | test_make_argh(); | ||
1891 | test_worm_food_collision(); | ||
1892 | test_worm_argh_collision(); | ||
1893 | test_specific_worm_collision(); | ||
1894 | #endif | ||
1895 | |||
1896 | /* Setup screen */ | ||
1897 | do { | ||
1898 | char buf[20]; | ||
1899 | char* ptr; | ||
1900 | rb->lcd_clear_display(); | ||
1901 | |||
1902 | /* first line players */ | ||
1903 | rb->snprintf(buf, sizeof buf, "%d Players UP/DN", players); | ||
1904 | rb->lcd_puts(0, 0, buf); | ||
1905 | |||
1906 | /* second line worms */ | ||
1907 | rb->snprintf(buf, sizeof buf, "%d Worms L/R", worm_count); | ||
1908 | rb->lcd_puts(0, 1, buf); | ||
1909 | |||
1910 | /* third line control */ | ||
1911 | if (players > 1) { | ||
1912 | if (use_remote) { | ||
1913 | ptr = "Remote Control F1"; | ||
1914 | } else { | ||
1915 | ptr = "No Rem. Control F1"; | ||
1916 | } | ||
1917 | } else { | ||
1918 | if (players > 0) { | ||
1919 | if (use_remote) { | ||
1920 | ptr = "2 Key Control F1"; | ||
1921 | } else { | ||
1922 | ptr = "4 Key Control F1"; | ||
1923 | } | ||
1924 | } else { | ||
1925 | ptr = "Out Of Control"; | ||
1926 | } | ||
1927 | } | ||
1928 | rb->lcd_puts(0, 2, ptr); | ||
1929 | rb->lcd_update(); | ||
1930 | |||
1931 | /* user selection */ | ||
1932 | button = rb->button_get(true); | ||
1933 | switch (button) { | ||
1934 | case BUTTON_UP: | ||
1935 | if (players < 3) { | ||
1936 | players ++; | ||
1937 | if (players > worm_count) { | ||
1938 | worm_count = players; | ||
1939 | } | ||
1940 | if (players > 2) { | ||
1941 | use_remote = true; | ||
1942 | } | ||
1943 | } | ||
1944 | break; | ||
1945 | case BUTTON_DOWN: | ||
1946 | if (players > 0) { | ||
1947 | players --; | ||
1948 | } | ||
1949 | break; | ||
1950 | case BUTTON_LEFT: | ||
1951 | if (worm_count > 1) { | ||
1952 | worm_count--; | ||
1953 | if (worm_count < players) { | ||
1954 | players = worm_count; | ||
1955 | } | ||
1956 | } | ||
1957 | break; | ||
1958 | case BUTTON_RIGHT: | ||
1959 | if (worm_count < MAX_WORMS) { | ||
1960 | worm_count ++; | ||
1961 | } | ||
1962 | break; | ||
1963 | case BUTTON_F1: | ||
1964 | use_remote = !use_remote; | ||
1965 | if (players > 2) { | ||
1966 | use_remote = true; | ||
1967 | } | ||
1968 | break; | ||
1969 | |||
1970 | case SYS_USB_CONNECTED: | ||
1971 | rb->usb_screen(); | ||
1972 | return PLUGIN_USB_CONNECTED; | ||
1973 | } | ||
1974 | } while (button != BUTTON_PLAY && | ||
1975 | button != BUTTON_OFF && button != BUTTON_ON); | ||
1976 | |||
1977 | rb->lcd_clear_display(); | ||
1978 | /* end of setup */ | ||
1979 | |||
1980 | do { | ||
1981 | |||
1982 | /* button state will be overridden if | ||
1983 | the game quits with the death of the worm. | ||
1984 | Initializing button to BUTTON_OFF ensures | ||
1985 | that the user can hit BUTTON_OFF during the | ||
1986 | game to return to the menu. | ||
1987 | */ | ||
1988 | button = BUTTON_OFF; | ||
1989 | |||
1990 | /* start the game */ | ||
1991 | worm_dead = run(); | ||
1992 | |||
1993 | /* if worm isn't dead the game was quit | ||
1994 | via BUTTON_OFF -> no need to wait for buttons. */ | ||
1995 | if (worm_dead) { | ||
1996 | do { | ||
1997 | button = rb->button_get(true); | ||
1998 | } | ||
1999 | /* BUTTON_ON -> start new game */ | ||
2000 | /* BUTTON_OFF -> back to game menu */ | ||
2001 | while (button != BUTTON_OFF && button != BUTTON_ON); | ||
2002 | } | ||
2003 | } | ||
2004 | while (button != BUTTON_OFF); | ||
2005 | |||
2006 | return PLUGIN_OK; | ||
2007 | } | ||
2008 | |||
2009 | #endif | ||