summaryrefslogtreecommitdiff
path: root/apps/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/Makefile45
-rw-r--r--apps/plugins/bounce.c409
-rw-r--r--apps/plugins/cube.c338
-rw-r--r--apps/plugins/helloworld.c48
-rw-r--r--apps/plugins/oscillograph.c207
-rw-r--r--apps/plugins/plugin.lds26
-rw-r--r--apps/plugins/snow.c111
-rw-r--r--apps/plugins/sokoban.c868
-rw-r--r--apps/plugins/viewer.c415
-rw-r--r--apps/plugins/wormlet.c2009
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
10CC = sh-elf-gcc
11OC = sh-elf-objcopy
12
13FIRMWARE = ../../firmware
14
15INCLUDES = -I$(FIRMWARE)/include -I$(FIRMWARE)/export -I$(FIRMWARE)/common -I$(FIRMWARE)/drivers -I..
16CFLAGS = -O -W -Wall -m1 -nostdlib -ffreestanding -Wstrict-prototypes $(INCLUDES) $(TARGET) $(EXTRA_DEFINES)
17
18LINKFILE = plugin.lds
19
20SRC := $(wildcard *.c)
21ROCKS := $(SRC:%.c=$(OBJDIR)/%.rock)
22
23ifndef OBJDIR
24no_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"
30endif
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
41all: $(ROCKS)
42 @echo done
43
44clean:
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
32static struct plugin_api* rb;
33
34static unsigned char table[]={
3526,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
38static unsigned char xtable[]={
3954,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
42static signed char speed[]={
43 1,2,3,3,3,2,1,0,-1,-2,-2,-2,-1,0,0,1,
44};
45
46const 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
149enum {
150 NUM_XSANKE,
151 NUM_YSANKE,
152 NUM_XADD,
153 NUM_YADD,
154 NUM_XDIST,
155 NUM_YDIST,
156
157 NUM_LAST
158};
159
160struct counter {
161 char *what;
162 int num;
163};
164
165struct 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
175static unsigned char yminute[]={
17653,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};
178static unsigned char yhour[]={
17942,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
182static unsigned char xminute[]={
18356,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};
185static unsigned char xhour[]={
18656,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
189static 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
224static 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
274static 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
350enum 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
27struct point_3D {
28 long x, y, z;
29};
30
31struct point_2D {
32 long x, y;
33};
34
35static struct point_3D sommet[8];
36static struct point_3D point3D[8];
37static struct point_2D point2D[8];
38
39static long matrice[3][3];
40
41static int nb_points = 8;
42
43static int x_off = 56;
44static int y_off = 95;
45static int z_off = 600;
46
47/* Precalculated sine and cosine * 10000 (four digit fixed point math) */
48static 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
71static struct plugin_api* rb;
72
73static 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
105static 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
138static 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
178static 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
192static 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
205static 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
210static 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
228enum 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 */
26static struct plugin_api* rb;
27
28/* this is the plugin entry point */
29enum 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 */
33static int speed = 1;
34/* roll == true -> lcd rolls */
35static bool roll = true;
36/* see DRAW_MODE_XXX constants for valid values */
37static 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 */
44enum 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 @@
1OUTPUT_FORMAT(elf32-sh)
2
3MEMORY
4{
5 PLUGIN_RAM : ORIGIN = 0x091f8000, LENGTH = 0x8000
6}
7
8SECTIONS
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
25static short particles[NUM_PARTICLES][2];
26static struct plugin_api* rb;
27
28static 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
37static 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
51static 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
83static 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
94enum 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
36static void init_undo(void);
37static void undo(void);
38static void add_undo(int button);
39
40static int get_level(char *level, int level_size);
41static int get_level_count(void);
42static int load_level(void);
43static void draw_level(void);
44
45static void init_boards(void);
46static void update_screen(void);
47static 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. */
56struct 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 */
63struct 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 */
71struct Undo {
72 struct LevelInfo level;
73 struct Location location[3];
74};
75
76/* Our full undo history */
77static 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 */
84static 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
93static struct plugin_api* rb;
94
95static void init_undo(void)
96{
97 undo_info.count = 0;
98 undo_info.current = 0;
99}
100
101static 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(&current_info.level, &undo->level, sizeof(undo->level));
114 rb->memcpy(&current_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
143static 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, &current_info.level, sizeof(undo->level));
165
166 /* Store our player info */
167 rb->memcpy(&undo->location[0], &current_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
222static 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
237static 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
264static 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 */
330static 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
364static 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
435static void draw_level(void)
436{
437 load_level();
438 rb->lcd_clear_display();
439 update_screen();
440}
441
442static 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
817enum 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
26static int fd;
27static int file_size;
28
29static char buffer[BUFFER_SIZE+1];
30static int buffer_pos; /* Position of the buffer in the file */
31
32static char display_lines; /* number of lines on the display */
33static char display_columns; /* number of columns on the display */
34static int begin_line; /* Index of the first line displayed on the lcd */
35static int end_line; /* Index of the last line displayed on the lcd */
36static int begin_line_pos; /* Position of the first_line in the bufffer */
37static int end_line_pos; /* Position of the last_line in the buffer */
38static 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
46static 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
59static 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
84static 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
109static 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
134static 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
166static 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
195static void viewer_exit(void)
196{
197 rb->close(fd);
198}
199
200static 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
224static 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
248static 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
319enum 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 */
50static 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 */
73static 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 */
79static char foodx[MAX_FOOD];
80static 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 */
87static char arghx[MAX_ARGH];
88static char arghy[MAX_ARGH];
89
90/* the number of arghs that are currently in use */
91static int argh_count;
92
93#ifdef DEBUG_WORMLET
94/* just a buffer used for debug output */
95static 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) */
102static 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 */
106static 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 */
125static int player1_dir = EAST;
126/* direction of human player 2 */
127static int player2_dir = EAST;
128/* direction of human player 3 */
129static int player3_dir = EAST;
130
131/* the number of (human) players that currently
132 control a worm */
133static int players = 1;
134
135/* the rockbox plugin api */
136static struct plugin_api* rb;
137
138#ifdef DEBUG_WORMLET
139static 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 */
152static 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 */
179static 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 */
205static 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 */
224static 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 */
267static 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 */
315static 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 */
350static 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 */
368static 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 */
389static 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 */
410static 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 */
430static 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 */
449static 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 */
469static 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 */
485static 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 */
535static 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 */
549static 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 */
566static 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 */
617static 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
625static 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 */
636static 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 */
665static 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 */
678static 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 */
692static 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 */
700static 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 */
760static 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 */
835static 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 */
860static 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 */
910static 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 */
930static 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 */
949static 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 */
971static 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 */
988static 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 */
1015static 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 */
1048static 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 */
1068static 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 */
1092static 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 */
1152static 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 */
1224static 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 */
1276static 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 */
1408static 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
1467static 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
1480static 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
1530static 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 */
1739static 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
1777static 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
1843static 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
1871extern bool use_old_rect;
1872
1873/**
1874 * Main entry point
1875 */
1876enum 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