summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFranklin Wei <frankhwei536@gmail.com>2014-09-03 19:16:37 -0400
committerSolomon Peachy <pizza@shaftnet.org>2024-04-24 17:37:58 -0400
commit6efd7f8f3e4a9de578646c77c7527c54b3c8e557 (patch)
tree97bff19881dfc7db5b87801ccf74d07cc24d12cb
parent2dbf26f11aff960ddac01fb625d857e2d1e42263 (diff)
downloadrockbox-6efd7f8f3e.tar.gz
rockbox-6efd7f8f3e.zip
FS#8647: Amaze - 3D maze game plugin
- update to build against latest Git - cleanup (whitespace, indentation) - fixed old PLA handling - update indentation/curly brace style - improve load/save mechanism - enable marking ground with "select" button - add compass view - improve display on 1-bit-targets (floor pattern) - graphics update: add 3x3 and 5x5 tiles, rework 7x7 and 9x9 tiles, load tiles dependant of screen size - fix: on some targets (Fuze+) division by 0 could occur. Fix by calculating the exact view depth on all targets. - fix: duplicate error checks when saving prefs - Fully translate it - Add a simple manual entry (including screenshots for some platforms) Change-Id: Ic84d98650c1152ab0ad268b51bd060f714ace288
-rw-r--r--apps/lang/english.lang224
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/amaze.c1620
-rw-r--r--apps/plugins/bitmaps/native/SOURCES38
-rw-r--r--apps/plugins/bitmaps/native/amaze_tiles_3.3x3x1.bmpbin0 -> 182 bytes
-rw-r--r--apps/plugins/bitmaps/native/amaze_tiles_3.3x3x16.bmpbin0 -> 414 bytes
-rw-r--r--apps/plugins/bitmaps/native/amaze_tiles_3.3x3x2.bmpbin0 -> 1198 bytes
-rw-r--r--apps/plugins/bitmaps/native/amaze_tiles_5.5x5x1.bmpbin0 -> 262 bytes
-rw-r--r--apps/plugins/bitmaps/native/amaze_tiles_5.5x5x16.bmpbin0 -> 854 bytes
-rw-r--r--apps/plugins/bitmaps/native/amaze_tiles_5.5x5x2.bmpbin0 -> 270 bytes
-rw-r--r--apps/plugins/bitmaps/native/amaze_tiles_7.7x7x1.bmpbin0 -> 342 bytes
-rw-r--r--apps/plugins/bitmaps/native/amaze_tiles_7.7x7x16.bmpbin0 -> 1734 bytes
-rw-r--r--apps/plugins/bitmaps/native/amaze_tiles_7.7x7x2.bmpbin0 -> 1638 bytes
-rw-r--r--apps/plugins/bitmaps/native/amaze_tiles_9.9x9x1.bmpbin0 -> 422 bytes
-rw-r--r--apps/plugins/bitmaps/native/amaze_tiles_9.9x9x16.bmpbin0 -> 2574 bytes
-rw-r--r--apps/plugins/bitmaps/native/amaze_tiles_9.9x9x2.bmpbin0 -> 2158 bytes
-rw-r--r--docs/CREDITS1
-rw-r--r--manual/plugins/amaze.tex6
-rw-r--r--manual/plugins/images/ss-amaze-128x160x24.pngbin0 -> 803 bytes
-rw-r--r--manual/plugins/images/ss-amaze-128x64x1.pngbin0 -> 961 bytes
-rw-r--r--manual/plugins/images/ss-amaze-138x110x2.pngbin0 -> 687 bytes
-rw-r--r--manual/plugins/images/ss-amaze-176x132x16.pngbin0 -> 883 bytes
-rw-r--r--manual/plugins/images/ss-amaze-240x320x24.pngbin0 -> 1215 bytes
-rw-r--r--manual/plugins/images/ss-amaze-320x240x16.pngbin0 -> 1057 bytes
-rw-r--r--manual/plugins/main.tex2
26 files changed, 1893 insertions, 0 deletions
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index b060c8230e..3a1f52c9b1 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -16711,3 +16711,227 @@
*: "Default Browser"
</voice>
</phrase>
+<phrase>
+ id: LANG_AMAZE_MENU
+ desc: Amaze game
+ user: core
+ <source>
+ *: "Amaze Main Menu"
+ </source>
+ <dest>
+ *: "Amaze Main Menu"
+ </dest>
+ <voice>
+ *: "Amaze Main Menu"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SET_MAZE_SIZE
+ desc: Maze size in Amaze game
+ user: core
+ <source>
+ *: "Set Maze Size"
+ </source>
+ <dest>
+ *: "Set Maze Size"
+ </dest>
+ <voice>
+ *: "Set Maze Size"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_VIEW_MAP
+ desc: Map in Amaze game
+ user: core
+ <source>
+ *: "View Map"
+ </source>
+ <dest>
+ *: "View Map"
+ </dest>
+ <voice>
+ *: "View Map"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_COMPASS
+ desc: Compass in Amaze game
+ user: core
+ <source>
+ *: "Show Compass"
+ </source>
+ <dest>
+ *: "Show Compass"
+ </dest>
+ <voice>
+ *: "Show Compass"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_MAP
+ desc: Map in Amaze game
+ user: core
+ <source>
+ *: "Show Map"
+ </source>
+ <dest>
+ *: "Show Map"
+ </dest>
+ <voice>
+ *: "Show Map"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_REMEMBER_PATH
+ desc: Map in Amaze game
+ user: core
+ <source>
+ *: "Remember Path"
+ </source>
+ <dest>
+ *: "Remember Path"
+ </dest>
+ <voice>
+ *: "Remember Path"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_USE_LARGE_TILES
+ desc: Map in Amaze game
+ user: core
+ <source>
+ *: "Use Large Tiles"
+ </source>
+ <dest>
+ *: "Use Large Tiles"
+ </dest>
+ <voice>
+ *: "Use Large Tiles"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_SOLUTION
+ desc: Map in Amaze game
+ user: core
+ <source>
+ *: "Show Solution"
+ </source>
+ <dest>
+ *: "Show Solution"
+ </dest>
+ <voice>
+ *: "Show Solution"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_QUIT_WITHOUT_SAVING
+ desc:
+ user: core
+ <source>
+ *: "Quit without saving"
+ </source>
+ <dest>
+ *: "Quit without saving"
+ </dest>
+ <voice>
+ *: "Quit without saving"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_GENERATING_MAZE
+ desc: Amaze game
+ user: core
+ <source>
+ *: "Generating maze..."
+ </source>
+ <dest>
+ *: "Generating maze..."
+ </dest>
+ <voice>
+ *: "Generating maze..."
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_YOU_WIN
+ desc: Success in game
+ user: core
+ <source>
+ *: "You win!"
+ </source>
+ <dest>
+ *: "You win!"
+ </dest>
+ <voice>
+ *: "You win!"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_YOU_CHEATED
+ desc: Cheated in game
+ user: core
+ <source>
+ *: "You cheated!"
+ </source>
+ <dest>
+ *: "You cheated!"
+ </dest>
+ <voice>
+ *: "You cheated!"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DIFFICULTY_EASY
+ desc: Game difficulty
+ user: core
+ <source>
+ *: "Easy"
+ </source>
+ <dest>
+ *: "Easy"
+ </dest>
+ <voice>
+ *: "Easy"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DIFFICULTY_MEDIUM
+ desc: Game difficulty
+ user: core
+ <source>
+ *: "Medium"
+ </source>
+ <dest>
+ *: "Medium"
+ </dest>
+ <voice>
+ *: "Medium"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DIFFICULTY_HARD
+ desc: Game difficulty
+ user: core
+ <source>
+ *: "Hard"
+ </source>
+ <dest>
+ *: "Hard"
+ </dest>
+ <voice>
+ *: "Hard"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_DIFFICULTY_EXPERT
+ desc: Game difficulty
+ user: core
+ <source>
+ *: "Expert"
+ </source>
+ <dest>
+ *: "Expert"
+ </dest>
+ <voice>
+ *: "Expert"
+ </voice>
+</phrase>
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index 5320015772..28b8b67c68 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -2,6 +2,7 @@
alpine_cdc,apps
alarmclock,apps
announce_status,demos
+amaze,games
autostart,apps
battery_bench,apps
bench_scaler,apps
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index bf36d50a40..3d520dea39 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -112,6 +112,7 @@ pictureflow.c
metronome.c
2048.c
+amaze.c
/* Lua needs at least 160 KB to work in */
#if PLUGIN_BUFFER_SIZE >= 0x80000
diff --git a/apps/plugins/amaze.c b/apps/plugins/amaze.c
new file mode 100644
index 0000000000..2af9463a16
--- /dev/null
+++ b/apps/plugins/amaze.c
@@ -0,0 +1,1620 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2014 Franklin Wei, (c) 2018 Sebastian Leonhardt
+ *
+ * Original work (C) 2000 David Leonard, public domain according to
+ * http://www.adaptive-enterprises.com.au/~d/software/amaze/
+ *
+ * Original Rockbox port by Jerry Chapman, 2007
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "plugin.h"
+#include "lib/pluginlib_actions.h"
+
+const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
+
+static const struct opt_items noyes_text[] = {
+ { STR(LANG_SET_BOOL_NO) },
+ { STR(LANG_SET_BOOL_YES) }
+};
+
+static const struct opt_items mazesize_text[] = {
+ { STR(LANG_DIFFICULTY_EASY) },
+ { STR(LANG_DIFFICULTY_MEDIUM) },
+ { STR(LANG_DIFFICULTY_HARD) },
+ { STR(LANG_DIFFICULTY_EXPERT) }
+};
+
+bool show_map;
+bool remember_visited;
+bool use_large_tiles;
+int maze_size;
+
+#if LCD_DEPTH >= 16
+#define COLOR_GROUND LCD_RGBPACK(51,51,51)
+#define COLOR_SKY LCD_RGBPACK(51,51,102)
+#define COLOR_VISITED LCD_RGBPACK(102,77,51)
+#define COLOR_MARK LCD_RGBPACK(255,100,61)
+#define COLOR_GOAL LCD_RGBPACK(128,0,0) /* red */
+#define COLOR_COMPASS LCD_RGBPACK(255,255,0) /* yellow */
+#define COLOR_PARA LCD_RGBPACK(153,153,102) /* color side wall */
+/* #define COLOR_PERP LCD_RGBPACK(102,51,0) */
+#define COLOR_PERP LCD_RGBPACK(204,153,102) /* color wall perp */
+#elif LCD_DEPTH == 2
+#define COLOR_GROUND LCD_BLACK
+#define COLOR_SKY LCD_WHITE
+#define COLOR_VISITED LCD_DARKGRAY
+#define COLOR_GOAL LCD_WHITE
+#define COLOR_COMPASS LCD_BLACK
+#define COLOR_MARK LCD_WHITE
+#define COLOR_PARA LCD_DARKGRAY /* color side wall */
+#define COLOR_PERP LCD_LIGHTGRAY /* color wall perp */
+#else /* mono */
+#define COLOR_GROUND LCD_BLACK
+#define COLOR_SKY LCD_BLACK
+#define COLOR_VISITED LCD_BLACK
+#define COLOR_GOAL LCD_BLACK
+#define COLOR_COMPASS LCD_WHITE
+#define COLOR_PARA LCD_BLACK /* color side wall */
+#define COLOR_PERP LCD_WHITE /* color wall perp */
+#endif
+
+#define A_DOWN 'v'
+#define A_RIGHT '>'
+#define A_UP '^'
+#define A_LEFT '<'
+#define SPACE ' ' /* Space you can walk through */
+#define BLOCK 'B' /* A block that you can't walk through */
+#define OBSPACE '#' /* Obscured space */
+#define VISITED '.' /* Visited space */
+#define GOAL '%' /* Exit from the maze */
+#define START '+' /* Starting point in the maze */
+
+enum dir { DIR_DOWN=0, DIR_RIGHT=1, DIR_UP=2, DIR_LEFT=3 };
+#define _TOD(d) (enum dir)((d) % 4)
+#define _TOI(d) (int)(d)
+#define LEFT_OF(d) _TOD(_TOI(d) + 1)
+#define REVERSE_OF(d) _TOD(_TOI(d) + 2)
+#define RIGHT_OF(d) _TOD(_TOI(d) + 3)
+
+static struct { int y, x; } dirtab[4] =
+ { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } };
+static char ptab[4] = { A_DOWN, A_RIGHT, A_UP, A_LEFT };
+
+int px, py; /* Player position */
+enum dir pdir; /* Player direction */
+char punder; /* character under player, if any */
+int sx, sy; /* Start position */
+int gx, gy; /* Goal position */
+int gdist; /* Distance from start to goal */
+int won = 0; /* Reached goal */
+int cheated = 0; /* Cheated somehow */
+bool compass = false; /* show compass */
+int button; /* Button data */
+bool loaded=false; /* Loaded? */
+
+#define FIELD_SIZE 80
+#define MAP_CONST 20
+
+static char map[FIELD_SIZE * FIELD_SIZE]; /* map storage */
+static char umap[FIELD_SIZE * FIELD_SIZE];
+
+/* visible depth */
+#define MAX_DEPTH 20
+int depth;
+
+/* CX,CY = center of screen */
+#define CX LCD_WIDTH / 2
+#define CY LCD_HEIGHT / 2
+
+int crd_x[MAX_DEPTH], crd_y[MAX_DEPTH]; /* screen vertices */
+
+#if LCD_WIDTH >= 220 || LCD_HEIGHT >= 220
+#include "pluginbitmaps/amaze_tiles_9.h"
+#define amaze_tiles_large amaze_tiles_9
+#define TILESIZE_LARGE BMPWIDTH_amaze_tiles_9
+#include "pluginbitmaps/amaze_tiles_7.h"
+#define amaze_tiles_small amaze_tiles_7
+#define TILESIZE_SMALL BMPWIDTH_amaze_tiles_7
+#elif LCD_WIDTH >= 160 || LCD_HEIGHT >= 160
+#include "pluginbitmaps/amaze_tiles_7.h"
+#define amaze_tiles_large amaze_tiles_7
+#define TILESIZE_LARGE BMPWIDTH_amaze_tiles_7
+#include "pluginbitmaps/amaze_tiles_5.h"
+#define amaze_tiles_small amaze_tiles_5
+#define TILESIZE_SMALL BMPWIDTH_amaze_tiles_5
+#else
+#include "pluginbitmaps/amaze_tiles_5.h"
+#define amaze_tiles_large amaze_tiles_5
+#define TILESIZE_LARGE BMPWIDTH_amaze_tiles_5
+#include "pluginbitmaps/amaze_tiles_3.h"
+#define amaze_tiles_small amaze_tiles_3
+#define TILESIZE_SMALL BMPWIDTH_amaze_tiles_3
+#endif
+
+/* save file names */
+#define UMAP_FILE PLUGIN_GAMES_DIR "/amaze_umap.sav"
+#define MAP_FILE PLUGIN_GAMES_DIR "/amaze_map.sav"
+#define PREF_FILE PLUGIN_GAMES_DIR "/amaze_prefs.sav"
+
+void clearscreen (void)
+{
+#if LCD_DEPTH > 1
+ rb->lcd_set_background(LCD_BLACK);
+ rb->lcd_set_foreground(LCD_WHITE);
+#endif
+ rb->lcd_clear_display();
+ rb->lcd_update();
+}
+
+void getmaxyx(int *y, int *x)
+{
+ *y = (maze_size + 1) * MAP_CONST;
+ *x = (maze_size + 1) * MAP_CONST;
+}
+
+void map_write (char *pane, int y, int x, char c)
+{
+ int maxy, maxx;
+
+ getmaxyx(&maxy, &maxx);
+
+ if (x<0 || x>=maxx || y<0 || y>=maxy) return;
+
+ pane[x * FIELD_SIZE + y] = c;
+
+}
+char map_read (char *pane, int y, int x)
+{
+ int maxy, maxx;
+
+ getmaxyx(&maxy, &maxx);
+
+ if (x<0 || x>=maxx || y<0 || y>=maxy) {
+ return SPACE;
+ }
+
+ return pane[x * FIELD_SIZE + y];
+}
+
+/* redefine ncurses werase */
+void werase (char *pane)
+{
+ int y, x;
+ int maxy, maxx;
+
+ getmaxyx(&maxy, &maxx);
+
+ for (y = 0; y < maxy; y++)
+ for (x = 0; x < maxx; x++)
+ map_write(pane, y, x, SPACE);
+}
+
+/* start of David Leonard's code */
+
+/* Look at position (y,x) in the maze map */
+char at(int y, int x)
+{
+ int maxy, maxx;
+
+ getmaxyx(&maxy, &maxx);
+
+ if (y == py && x == px)
+ return punder;
+
+ if (y < 0 || y >= maxy || x < 0 || x >= maxx)
+ return SPACE;
+ else {
+ return map_read(map, y, x);
+ }
+}
+
+void copyumap(int y, int x, int fullvis)
+{
+ char c;
+
+ c = at(y, x);
+ if (!fullvis && c == SPACE && map_read(umap, y, x) != SPACE)
+ c = OBSPACE;
+ map_write(umap, y, x, c);
+}
+
+struct path {
+ int y, x;
+ int ttl; /* Time until this path stops */
+ int spawns; /* Max number of forks this path can do */
+ int distance; /* Distance from start */
+ struct path *next;
+};
+
+/*
+ * A better maze-digging algorithm.
+ * Simultaneously advance multiple digging paths through the map.
+ * This is done by having a work queue of advancing paths.
+ * Occasionally a path can fork; thus adding more to the work
+ * queue and diversifying the maze.
+ */
+void eatmaze(int starty, int startx)
+{
+ struct path {
+ int y, x;
+ int ttl; /* Time until this path stops */
+ int spawns; /* Max number of forks this path can do */
+ int distance; /* Distance from start */
+ struct path *next;
+ };
+ struct path *path_free, *path_head, *path_tail;
+ struct path *p, *s;
+ /* static -- per <hcs> in #rockbox -- was having stack issues */
+ static struct path path_storage[2000];
+ int try;
+ unsigned i;
+ int y, x, dy, dx;
+ int sdir;
+
+ /* Set up the free list of path cells */
+ for (i = 2; i < sizeof path_storage / sizeof path_storage[0]; i++)
+ path_storage[i].next = &path_storage[i-1];
+ path_storage[1].next = NULL;
+ path_free = &path_storage[sizeof path_storage / sizeof path_storage[0] - 1];
+
+ /* Set up the initial path cell */
+ path_storage[0].y = starty;
+ path_storage[0].x = startx;
+ map_write(map, starty, startx, SPACE);
+
+ /* Set up the initial goal. It will move later. */
+ gy = starty;
+ gx = startx;
+ gdist = 0;
+
+ /* Initial properties of the root path */
+ path_storage[0].ttl = 50;
+ path_storage[0].spawns = 20;
+ path_storage[0].distance = 0;
+
+ /* Put the initial path into the queue */
+ path_storage[0].next = NULL;
+ path_head = path_tail = &path_storage[0];
+
+ while (path_head != NULL) {
+ /* Dequeue */
+ p = path_head;
+ path_head = p->next;
+ if (path_head == NULL)
+ path_tail = NULL;
+
+ /* There's a large chance that some paths miss a turn */
+ if (rb->rand() % 100 < 60)
+ goto requeue;
+
+ /* First thing we do is advance the path. */
+ y = p->y;
+ x = p->x;
+
+ sdir = rb->rand() % 4;
+ for (try = 0; try < 4; try ++) {
+ dx = dirtab[(sdir + try) % 4].x;
+ dy = dirtab[(sdir + try) % 4].y;
+
+ /* Going back on ourselves? */
+ if (at(y + dy, x + dx) != BLOCK)
+ continue;
+
+ /* Connecting to another path? */
+ if (at(y + dy * 2, x + dx * 2) != BLOCK)
+ continue;
+ if (at(y + dy + dx, x + dx - dy) != BLOCK)
+ continue;
+ if (at(y + dy - dx, x + dx + dy) != BLOCK)
+ continue;
+
+ break;
+ }
+ if (try == 4 || p->ttl <= 0) {
+ /* Failed: the path is placed on the free list. */
+ p->next = path_free;
+ path_free = p;
+ continue;
+ }
+
+ /* Dig the path a bit */
+ p->y = y + dy;
+ p->x = x + dx;
+ map_write(map, p->y, p->x, SPACE);
+ p->ttl--;
+ p->distance++;
+
+ if (p->distance > gdist) {
+ gx = p->x;
+ gy = p->y;
+ gdist = p->distance;
+ }
+
+ /* Decide if we should spawn */
+ if (/* rb->rand() % (p->ttl + 1) < p->spawns && */ path_free) {
+ /* Take a new path element off the free list */
+ s = path_free;
+ path_free = s->next;
+
+ /* Insert it at the tail of the queue */
+ s->next = NULL;
+ if (path_tail) path_tail->next = s;
+ else path_head = s;
+ path_tail = s;
+
+ /* Newly spawned path s will inherit most properties from p */
+ s->y = p->y;
+ s->x = p->x;
+ s->ttl = p->ttl + rb->rand() % 10;
+ s->spawns = p->spawns;
+ s->distance = p->distance;
+
+ /* p->spawns--; */
+ }
+
+ requeue:
+ /* Put p onto the tail of the queue */
+ p->next = NULL;
+ if (path_tail) path_tail->next = p;
+ else path_head = p;
+ path_tail = p;
+ }
+}
+
+/* Move the player to a new position/direction in the maze map */
+void mappmove(int newpy, int newpx, enum dir newpdir)
+{
+ map_write(map, py, px, punder);
+ copyumap(py, px, 1);
+ punder = at(newpy, newpx);
+ py = newpy;
+ px = newpx;
+ pdir = newpdir;
+ copyumap(py, px, 1);
+ map_write(umap, py, px, ptab[_TOI(pdir)]);
+ rb->lcd_update();
+}
+
+void clearmap (char *amap)
+{
+ int maxy, maxx;
+ int y, x;
+
+ getmaxyx(&maxy, &maxx);
+
+
+ for (y = 0; y < maxy; y++)
+ for (x = 0; x < maxx; x++)
+ map_write(amap, y, x, BLOCK);
+}
+
+/* Reveal the solution to the user */
+void showmap(void)
+{
+ int maxy, maxx, y, x;
+ char ch, och;
+
+ getmaxyx(&maxy, &maxx);
+ for (y = 0; y < maxy; y++)
+ for (x = 0; x < maxx; x++) {
+ ch = at(y, x);
+ if (ch == SPACE) {
+ och = map_read(umap, y, x);
+ if (och == BLOCK || och == OBSPACE)
+ ch = OBSPACE;
+ }
+ map_write(umap, y, x, ch);
+ rb->yield();
+ }
+ map_write(umap, py, px, ptab[_TOI(pdir)]);
+ rb->lcd_update();
+}
+
+/*
+ * Create a new maze map
+ * The algorithm here is quite inferior to that presented in the
+ * magazine. I could only remember the gist of it: recursively dig a
+ * trail that doesn't touch any other part of the maze, keeping track
+ * of all possible points where the path could fork. Later on try those
+ * possible branches; put limits on the segment lengths etc.
+ */
+void makemaze(void)
+{
+ int maxy, maxx;
+ int i;
+
+ /* Get the window dimensions */
+ getmaxyx(&maxy, &maxx);
+
+ clearmap(map);
+
+ py = rb->rand() % (maxy - 2) + 1; /* maxy/2 */
+ px = rb->rand() % (maxx - 2) + 1; /* maxx/2 */
+
+ eatmaze(py, px);
+
+ sx = px; /* starting position */
+ sy = py;
+
+ /* Face in an interesting direction: */
+ pdir = DIR_UP;
+ for (i = 0;
+ i < 4 && at(py + dirtab[pdir].y,
+ px + dirtab[pdir].x) == BLOCK;
+ i++)
+ pdir = LEFT_OF(pdir);
+
+ map_write(map, py, px, START);
+ map_write(map, gy, gx, GOAL);
+ punder = START;
+ mappmove(py, px, pdir);
+}
+
+/* new drawing routines */
+
+void draw_arrow(int dir, int sx, int sy, int pass)
+{
+ if (pass > 2) return;
+
+ rb->lcd_fillrect(sx, sy, 1, 1);
+ switch(dir) {
+ case 0: /* down */
+ rb->lcd_fillrect(sx + 2*pass, sy, 1, 1);
+ draw_arrow(dir, sx - 1, sy - 1, pass + 1);
+ break;
+ case 2: /* up */
+ rb->lcd_fillrect(sx + 2*pass, sy, 1, 1);
+ draw_arrow(dir, sx - 1, sy + 1, pass + 1);
+ break;
+ case 1: /* left */
+ rb->lcd_fillrect(sx, sy + 2*pass, 1, 1);
+ draw_arrow(dir, sx + 1, sy - 1, pass + 1);
+ break;
+ case 3: /* right */
+ rb->lcd_fillrect(sx, sy + 2*pass, 1, 1);
+ draw_arrow(dir, sx - 1, sy - 1, pass +1);
+ break;
+ }
+}
+
+/* Provide a compass pointing 'north' or draw arrow mark on the floor */
+void draw_pointer(int dir, bool is_compass)
+{
+ int offset;
+
+ if(is_compass)
+ offset = -crd_y[1]*2/3; /* draw compass at the top */
+ else
+ offset = crd_y[1]/2; /* draw mark on the floor */
+
+#if LCD_DEPTH > 1
+ if(is_compass)
+ rb->lcd_set_foreground(COLOR_COMPASS);
+ else
+ rb->lcd_set_foreground(COLOR_MARK);
+#else
+ rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+#endif
+ switch(dir) {
+ case 0: /* point down */
+ draw_arrow(dir, CX - 1, CY + offset + 6, 0);
+ rb->lcd_fillrect(CX - 1, CY + offset + 1, 1, 3);
+ break;
+ case 2: /* point up */
+ draw_arrow(dir, CX - 1, CY + offset, 0);
+ rb->lcd_fillrect(CX - 1, CY + offset + 3, 1, 3);
+ break;
+ case 1: /* point left */
+ draw_arrow(dir, CX - 6, CY + offset + 6, 0);
+ rb->lcd_fillrect(CX - 3, CY + offset + 6, 3, 1);
+ break;
+ case 3: /* point right */
+ draw_arrow(dir, CX + 1, CY + offset + 6, 0);
+ rb->lcd_fillrect(CX - 4, CY + offset + 6, 3, 1);
+ break;
+ }
+#if LCD_DEPTH == 1
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+#endif
+ rb->lcd_update();
+}
+
+void draw_end_wall(int bx, int by)
+{
+#if LCD_DEPTH > 1
+ rb->lcd_set_foreground(COLOR_PERP);
+ rb->lcd_fillrect(CX - bx/2, CY - by/2, bx + 1, by);
+#else
+ rb->lcd_drawrect(CX - bx/2, CY - by/2, bx + 1, by);
+#endif
+}
+
+void draw_side(int fx, int bx, int by, int tan_n, int tan_d, bool isleft)
+{
+ int signx;
+
+ if(isleft)
+ signx = -1;
+ else
+ signx = 1;
+
+#if LCD_DEPTH > 1
+ for(int i = bx; i < fx + 1; i++)
+ {
+ /* add some stripes */
+ if(i % 3 == 0)
+ rb->lcd_set_foreground(COLOR_PERP);
+ else
+ rb->lcd_set_foreground(COLOR_PARA);
+ rb->lcd_vline(CX + signx * i/2,
+ CY - tan_n * (i-bx)/2 / tan_d - by/2,
+ CY + tan_n * (i-bx)/2 / tan_d + by/2);
+ }
+#else
+ rb->lcd_vline(CX + signx * bx/2,
+ CY - by/2,
+ CY + by/2);
+ rb->lcd_vline(CX + signx * (fx + 1)/2,
+ CY - tan_n * (fx - bx + 1)/2 / tan_d - by/2,
+ CY + tan_n * (fx - bx + 1)/2 / tan_d + by/2);
+ rb->lcd_drawline(CX + signx * bx/2,
+ CY - by/2,
+ CX + signx * (fx + 1)/2,
+ CY - tan_n * (fx - bx + 1)/2 / tan_d - by/2);
+ rb->lcd_drawline(CX + signx * bx/2,
+ CY + by/2,
+ CX + signx * (fx + 1)/2,
+ CY + tan_n * (fx - bx + 1)/2 / tan_d + by/2);
+#endif
+}
+
+void draw_hall(int fx, int bx, int by, bool isleft)
+{
+#if LCD_DEPTH > 1
+ rb->lcd_set_foreground(COLOR_PERP);
+
+ if(isleft)
+ rb->lcd_fillrect(CX - fx/2, CY - by/2, (fx - bx)/2 + 1, by);
+ else
+ rb->lcd_fillrect(CX + bx/2, CY - by/2, (fx - bx)/2 + 1, by);
+#else
+ if(isleft)
+ rb->lcd_drawrect(CX - fx/2, CY - by/2, (fx - bx)/2 + 1, by);
+ else
+ rb->lcd_drawrect(CX + bx/2, CY - by/2, (fx - bx)/2 + 1, by);
+#endif
+}
+
+void draw_side_tri(int fx, int fy, int bx, int tan_n, int tan_d,
+ bool isvisited, bool isgoal)
+{
+ int i;
+ int signx, signy;
+
+ signy = 1;
+
+ while(signy >= -1)
+ {
+#if LCD_DEPTH > 1
+ if(signy == 1)
+ if(isgoal)
+ rb->lcd_set_foreground(COLOR_GOAL);
+ else if(isvisited)
+ rb->lcd_set_foreground(COLOR_VISITED);
+ else
+ rb->lcd_set_foreground(COLOR_GROUND);
+ else
+ rb->lcd_set_foreground(COLOR_SKY);
+#endif
+
+ signx = 1;
+
+ while(signx >= -1)
+ {
+ for(i = fx; i > bx; i--) {
+#if LCD_DEPTH == 1 /* if unvisited floor draw pattern, otherwise solid */
+ if (signy!=1 || isgoal || isvisited || (CX + signx * i/2) & 1)
+#endif
+ rb->lcd_vline(CX + signx * i/2,
+ CY + signy * fy/2,
+ CY + signy * fy/2
+ + signy * tan_n
+ * (i - fx)/2 / tan_d);
+ }
+ signx-=2;
+ }
+ signy-=2;
+ }
+}
+
+void draw_hall_crnr(int fx, int fy, int bx, int by,
+ bool isleft, bool isvisited, bool isgoal)
+{
+#if LCD_DEPTH > 1
+ rb->lcd_set_foreground(COLOR_SKY);
+#endif
+ if(isleft)
+ rb->lcd_fillrect(CX - fx/2, CY - fy/2,
+ (fx - bx)/2 + 1, (fy - by)/2);
+ else
+ rb->lcd_fillrect(CX + bx/2, CY - fy/2,
+ (fx - bx)/2 + 1, (fy - by)/2);
+
+#if LCD_DEPTH > 1
+ if(isgoal)
+ rb->lcd_set_foreground(COLOR_GOAL);
+ else if(isvisited)
+ rb->lcd_set_foreground(COLOR_VISITED);
+ else
+ rb->lcd_set_foreground(COLOR_GROUND);
+
+ if(isleft)
+ rb->lcd_fillrect(CX - fx/2, CY + by/2,
+ (fx - bx)/2 + 1, (fy - by)/2);
+ else
+ rb->lcd_fillrect(CX + bx/2, CY + by/2,
+ (fx - bx)/2 + 1, (fy - by)/2);
+#else /* LCD_DEPTH == 1 */
+ /* if unvisited floor draw pattern, otherwise solid */
+ if(isleft)
+ for (int x = CX-fx/2; x <= CX-bx/2; x++) {
+ if (isgoal || isvisited || x & 1)
+ rb->lcd_vline(x, CY + by/2, CY + fy/2);
+ }
+ else
+ for (int x = CX+bx/2; x <= CX+fx/2; x++) {
+ if (isgoal || isvisited || x & 1)
+ rb->lcd_vline(x, CY + by/2, CY + fy/2);
+ }
+#endif
+}
+
+void draw_center_sq(int fy, int bx, int by, bool isvisited, bool isgoal,
+ bool isfront, int chr)
+{
+ chr = chr - '0'; /* get the integer value */
+
+#if LCD_DEPTH > 1
+ rb->lcd_set_foreground(COLOR_SKY);
+#endif
+ rb->lcd_fillrect(CX - bx/2, CY - fy/2, bx, (fy - by)/2);
+
+#if LCD_DEPTH > 1
+ if(isgoal)
+ rb->lcd_set_foreground(COLOR_GOAL);
+ else if(isvisited)
+ rb->lcd_set_foreground(COLOR_VISITED);
+ else
+ rb->lcd_set_foreground(COLOR_GROUND);
+ rb->lcd_fillrect(CX - bx/2, CY + by/2, bx, (fy - by)/2 + 1);
+#else
+ /* if unvisited floor draw pattern, otherwise solid */
+ for (int x = CX-bx/2; x <= CX+bx/2; x++) {
+ if (isgoal || isvisited || x & 1)
+ rb->lcd_vline(x, CY + by/2, CY + fy/2 + 1);
+ }
+#endif
+
+ if(isvisited && chr >= 0 && chr <= 3)
+ {
+#if LCD_DEPTH > 1
+ rb->lcd_set_foreground(COLOR_MARK);
+#else
+ rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+#endif
+ if(isfront)
+ { /* cell is marked in front, draw arrow */
+ if (chr == ((int)(pdir) + 3) % 4)
+ draw_pointer(DIR_LEFT, false);
+ else if (chr == ((int)(pdir) + 1) % 4)
+ draw_pointer(DIR_RIGHT, false);
+ else if (chr == ((int)(pdir) + 2) % 4)
+ draw_pointer(DIR_DOWN, false);
+ else /* same direction */
+ draw_pointer(DIR_UP, false);
+ }
+ else /* cell is marked but is in distance */
+ rb->lcd_fillrect(CX, CY + (fy + by)/4, 2, 2);
+#if LCD_DEPTH == 1
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+#endif
+ }
+}
+
+bool is_visited(char cell)
+{
+ if (cell - '0' >= 0 && cell - '0' <= 3)
+ return true;
+ else if (cell == VISITED)
+ return true;
+ else
+ return false;
+}
+
+void graphic_view(void)
+{
+ int dist;
+ int x, y, dx, dy;
+ int a, l, r; /* is block? ahead/left/right */
+ bool g, gl, gr; /* ground visted? under/left/right */
+ bool e, el, er; /* is goal? under/left/right */
+ int tan_n, tan_d; /* tangent numerator/denominator */
+
+ dx = dirtab[(int)pdir].x;
+ dy = dirtab[(int)pdir].y;
+
+ for (dist = 1; dist < depth; dist++)
+ if (at(py + dy * dist, px + dx * dist) == BLOCK)
+ break;
+
+ if (!show_map)
+ {
+ clearmap(umap);
+ copyumap(gy, gx, 1);
+ }
+
+#if LCD_DEPTH == 1
+ clearscreen();
+#endif
+
+ while (--dist >= 0)
+ {
+ x = px + dx * dist;
+ y = py + dy * dist;
+
+ /* ground */
+ g = is_visited(at(y, x));
+ gl = is_visited(at(y - dx, x + dy));
+ gr = is_visited(at(y + dx, x - dy));
+ /* goal/end */
+ e = at(y, x) == GOAL;
+ el = at(y - dx, x + dy) == GOAL;
+ er = at(y + dx, x - dy) == GOAL;
+ /* ahead */
+ a = at(y + dy, x + dx) == BLOCK;
+ /* to the left */
+ l = at(y - dx, x + dy) == BLOCK;
+ /* to the right */
+ r = at(y + dx, x - dy) == BLOCK;
+
+ tan_n = crd_y[dist] - crd_y[dist+1];
+ tan_d = crd_x[dist] - crd_x[dist+1];
+
+ if (a)
+ draw_end_wall(crd_x[dist+1], crd_y[dist+1]);
+ if (l)
+ {
+ draw_side(crd_x[dist], crd_x[dist+1],
+ crd_y[dist+1], tan_n, tan_d, true);
+ }
+ else
+ {
+ draw_hall(crd_x[dist], crd_x[dist+1],
+ crd_y[dist+1], true);
+ draw_hall_crnr(crd_x[dist], crd_y[dist], crd_x[dist+1],
+ crd_y[dist+1], true, gl, el);
+ }
+ if (r)
+ {
+ draw_side(crd_x[dist], crd_x[dist+1],
+ crd_y[dist+1], tan_n, tan_d, false);
+ }
+ else
+ {
+ draw_hall(crd_x[dist], crd_x[dist+1],
+ crd_y[dist+1], false);
+ draw_hall_crnr(crd_x[dist], crd_y[dist],
+ crd_x[dist+1], crd_y[dist+1], false, gr, er);
+ }
+
+ draw_center_sq(crd_y[dist], crd_x[dist+1], crd_y[dist+1],
+ g, e, dist==0, (int)(at(y, x)));
+ draw_side_tri(crd_x[dist], crd_y[dist],
+ crd_x[dist+1], tan_n, tan_d, g, e);
+
+ copyumap(y + dy, x + dx, 0); /* ahead */
+ copyumap(y, x, 1); /* here */
+ copyumap(y - dx, x + dy, 0); /* left */
+ copyumap(y + dx, x - dy, 0); /* right */
+ if (!l)
+ copyumap(y - dx + dy, x + dy + dx, 0); /* lft ahead */
+ if (!r)
+ copyumap(y + dx + dy, x - dy + dx, 0); /* rt ahead */
+ }
+ if (compass)
+ draw_pointer(pdir, true);
+}
+
+void win(void)
+{
+ /*
+ int i;
+ char amazed[8] = "amazing!";
+ char newton;
+
+ for (i=0; i <= 8; i++)
+ {
+ newton = amazed[i];
+ map_write(msg, 0, i + 31, newton);
+ }
+ */
+ won++;
+ showmap();
+ show_map = 1;
+}
+
+
+/* Try to move the player in the direction given */
+void trymove(enum dir dir)
+{
+ int nx, ny;
+
+ ny = py + dirtab[(int)dir].y;
+ nx = px + dirtab[(int)dir].x;
+
+ if (at(ny, nx) == BLOCK)
+ {
+ graphic_view();
+ return;
+ }
+
+ if (at(ny, nx) == GOAL)
+ win();
+
+ mappmove(ny, nx, pdir);
+ if (remember_visited && punder == SPACE)
+ punder = VISITED;
+ graphic_view();
+}
+
+void walkleft(void)
+{
+ int a, l;
+ int dx, dy;
+ int owon = won;
+
+ while (1)
+ {
+ int input = pluginlib_getaction(TIMEOUT_NOBLOCK, plugin_contexts,
+ ARRAYLEN(plugin_contexts));
+ if(input==PLA_CANCEL || input==PLA_EXIT)
+ {
+ return;
+ }
+ rb->lcd_update();
+ if (won != owon)
+ {
+ break;
+ }
+
+ dx = dirtab[(int)pdir].x;
+ dy = dirtab[(int)pdir].y;
+
+ /* ahead */
+ a = at(py + dy, px + dx) == BLOCK;
+ /* to the left */
+ l = at(py - dx, px + dy) == BLOCK;
+
+ if (!l)
+ {
+ mappmove(py, px, LEFT_OF(pdir));
+ graphic_view();
+ rb->sleep(2);
+ trymove(pdir);
+ continue;
+ }
+ if (a)
+ {
+ mappmove(py, px, RIGHT_OF(pdir));
+ graphic_view();
+ continue;
+ }
+ trymove(pdir);
+ rb->yield();
+ }
+}
+
+void draw_tile(int index, int x, int y)
+{
+ if (use_large_tiles == 1)
+ rb->lcd_bitmap_part (amaze_tiles_large, 0, index * TILESIZE_LARGE,
+ TILESIZE_LARGE, x * TILESIZE_LARGE, y * TILESIZE_LARGE,
+ TILESIZE_LARGE, TILESIZE_LARGE);
+ else
+ rb->lcd_bitmap_part (amaze_tiles_small, 0, index * TILESIZE_SMALL,
+ TILESIZE_SMALL, x * TILESIZE_SMALL, y * TILESIZE_SMALL,
+ TILESIZE_SMALL, TILESIZE_SMALL);
+}
+
+void draw_tile_map(int xmin, int xmax, int ymin, int ymax)
+{
+ int x,y;
+ char map_unit;
+ int tdex = 7; /* tile index */
+
+ enum tile_index
+ { t_down=0, t_right=1, t_up=2, t_left=3, t_visited=4,
+ t_obspace=5, t_goal=6, t_block=7, t_space=8, t_start=9 };
+
+ for(y = ymin; y <= ymax; y++)
+ for(x = xmin; x <= xmax; x++)
+ {
+
+ map_unit = map_read(umap, y, x);
+
+ switch (map_unit)
+ {
+ case VISITED:
+ case '0': case '1': case '2': case '3':
+ tdex = t_visited;
+ break;
+ case OBSPACE:
+ tdex = t_obspace;
+ break;
+ case START:
+ tdex = t_start;
+ break;
+ case GOAL:
+ tdex = t_goal;
+ break;
+ case SPACE:
+ tdex = t_space;
+ break;
+ case BLOCK:
+ tdex = t_block;
+ break;
+ }
+ draw_tile(tdex, x - xmin, y - ymin);
+ }
+ if(sx>=xmin && sx<=xmax && sy>=ymin && sy<=ymax)
+ {
+ x = sx;
+ y = sy;
+ draw_tile(t_start, x - xmin, y - ymin);
+ }
+
+ if(px>=xmin && px<=xmax && py>=ymin && py<=ymax)
+ {
+ x = px;
+ y = py;
+ draw_tile(pdir, x - xmin, y - ymin);
+ }
+
+ rb->lcd_update();
+}
+
+void check_map_bounds(int *xmin, int *xmax, int *ymin, int *ymax)
+{
+ int maxx, maxy;
+
+ getmaxyx(&maxy, &maxx);
+
+ /* bounds check x */
+ if(*xmin < 0)
+ {
+ *xmax = *xmax - *xmin;
+ *xmin = 0;
+ }
+ if(*xmax >= maxx)
+ {
+ *xmin = *xmin - *xmax + maxx - 1;
+ *xmax = maxx - 1;
+ }
+
+ /* bounds check y */
+ if(*ymin < 0)
+ {
+ *ymax = *ymax - *ymin;
+ *ymin = 0;
+ }
+ if(*ymax >= maxy)
+ {
+ *ymin = *ymin - *ymax + maxy - 1;
+ *ymax = maxy - 1;
+ }
+}
+
+void calc_map_size(int *xmin, int *xmax, int *ymin, int *ymax)
+{
+ int tile_size; /* runtime option */
+ int vx, vy; /* maxx, maxy of view */
+ int midx, midy; /* midpoint x, y of view */
+ int maxx, maxy; /* maxx, maxy of map */
+
+ getmaxyx(&maxy, &maxx);
+
+ if (use_large_tiles == 1)
+ tile_size = TILESIZE_LARGE;
+ else
+ tile_size = TILESIZE_SMALL;
+
+ vx = LCD_WIDTH / tile_size;
+ if (vx > maxx)
+ vx = maxx;
+ vy = LCD_HEIGHT / tile_size;
+ if (vy > maxy)
+ vy = maxy;
+
+ midx = vx / 2;
+ midy = vy / 2;
+
+ *xmin = px - midx;
+ if(vx % 2 == 0)
+ *xmax = px + midx - 1;
+ else
+ *xmax = px + midx;
+
+ *ymin = py - midy;
+ if(vy % 2 == 0)
+ *ymax = py + midy - 1;
+ else
+ *ymax = py + midy;
+
+}
+
+
+void draw_portion_map(void)
+{
+ int xmin, xmax, ymin, ymax; /* coords of map corners */
+ bool quit_map;
+ int input;
+
+ clearscreen();
+
+ calc_map_size(&xmin, &xmax, &ymin, &ymax);
+
+ quit_map = false;
+
+ while (!quit_map)
+ {
+ check_map_bounds(&xmin, &xmax, &ymin, &ymax);
+ draw_tile_map(xmin, xmax, ymin, ymax);
+
+ input = pluginlib_getaction(TIMEOUT_BLOCK, plugin_contexts,
+ ARRAYLEN(plugin_contexts));
+
+ switch(input)
+ {
+ case PLA_CANCEL:
+ case PLA_EXIT:
+ quit_map = true;
+ break;
+ case PLA_UP:
+ case PLA_UP_REPEAT:
+ ymin--;
+ ymax--;
+ break;
+ case PLA_DOWN:
+ case PLA_DOWN_REPEAT:
+ ymin++;
+ ymax++;
+ break;
+ case PLA_LEFT:
+ case PLA_LEFT_REPEAT:
+ xmin--;
+ xmax--;
+ break;
+ case PLA_RIGHT:
+ case PLA_RIGHT_REPEAT:
+ xmin++;
+ xmax++;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+bool load_map(char *filename, char *amap)
+{
+ int fd;
+ int x,y;
+ int maxxy;
+ size_t n;
+ char newton = BLOCK;
+ char map_size[2];
+
+ /* load a map */
+ fd = rb->open(filename, O_RDONLY);
+ if (fd < 0)
+ {
+ LOGF("Invalid map file: %s\n", filename);
+ return false;
+ }
+
+ n = rb->read(fd, map_size, sizeof(map_size));
+ if (n <= 0)
+ {
+ LOGF("Invalid map size.");
+ return false;
+ }
+
+ maze_size = (int)map_size[0] - 48;
+ maxxy = MAP_CONST * (maze_size + 1);
+ char line[maxxy + 1];
+
+ for(y=0; y < maxxy ; y++)
+ {
+ n = rb->read(fd, line, sizeof(line));
+ if (n <= 0)
+ {
+ return false;
+ }
+ for(x=0; x < maxxy+1; x++)
+ {
+ switch(line[x])
+ {
+ case '\n':
+ break;
+ case '0': case '1': case '2': case '3':
+ newton = line[x];
+ break;
+ case START:
+ sy = y;
+ sx = x;
+ newton = START;
+ break;
+ case SPACE:
+ case BLOCK:
+ case OBSPACE:
+ newton = line[x];
+ break;
+ case GOAL:
+ newton = GOAL;
+ gy = y;
+ gx = x;
+ break;
+ case A_DOWN: case A_LEFT: case A_UP: case A_RIGHT:
+ py = y;
+ px = x;
+ switch(line[x])
+ {
+ case A_DOWN:
+ pdir = DIR_DOWN;
+ break;
+ case A_LEFT:
+ pdir = DIR_LEFT;
+ break;
+ case A_UP:
+ pdir = DIR_UP;
+ break;
+ case A_RIGHT:
+ pdir = DIR_RIGHT;
+ break;
+ }
+ /* FALLTHROUGH */
+ case VISITED:
+ newton = VISITED;
+ break;
+ }
+ if (line[x] != '\n')
+ map_write(amap, y, x, newton);
+ }
+ }
+ rb->close(fd);
+ rb->remove(filename);
+ return true;
+}
+
+bool load_game(void)
+{
+ if (load_map(UMAP_FILE, umap) && load_map(MAP_FILE, map))
+ return true;
+ else
+ return false;
+}
+
+
+bool save_map(char *filename, char *amap)
+{
+ int x,y;
+ int maxy, maxx;
+ char map_unit;
+ int fd;
+ int line_len = (maze_size + 1) * MAP_CONST + 1;
+ char line[line_len];
+ char map_size[2] =
+ {'0','\n'};
+
+ line[line_len - 1] = '\n'; /* last cell is a linefeed */
+ map_size[0] = (char)(maze_size + 48);
+
+ if ((fd = rb->open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
+ return false;
+
+ rb->write(fd, map_size, 2);
+
+ getmaxyx(&maxy, &maxx);
+
+ for(y=0; y < maxy; y++)
+ {
+ for (x=0; x < maxx; x++)
+ {
+ map_unit = map_read(amap, y, x);
+
+ if(y == py && x == px)
+ line[x] = ptab[_TOI(pdir)];
+ else if(y == sy && x == sx)
+ line[x] = START;
+ else
+ line[x] = map_unit;
+ }
+ rb->write(fd, line, line_len);
+ }
+ rb->close(fd);
+
+ return true;
+}
+
+bool save_game(void)
+{
+ if (save_map(UMAP_FILE, umap) && save_map(MAP_FILE, map))
+ return true;
+ else
+ return false;
+}
+
+
+bool ingame;
+bool save_prefs(char *filename);
+
+static int menu_cb(int action, const struct menu_item_ex *this_item,
+ struct gui_synclist *this_list)
+{
+ (void)this_list;
+
+ int idx=((intptr_t)this_item);
+ if (action==ACTION_REQUEST_MENUITEM) {
+ if (ingame) {
+ if (idx==2)
+ return ACTION_EXIT_MENUITEM;
+ }
+ else { /* !ingame */
+ if (idx==3 || idx==8)
+ return ACTION_EXIT_MENUITEM;
+ if (!loaded && (idx==0 || idx==9))
+ return ACTION_EXIT_MENUITEM;
+ }
+ }
+ return action;
+}
+
+int menu(void)
+{
+ bool exit_menu = false;
+ int selection = 0, result = 0, status = 1;
+
+ MENUITEM_STRINGLIST(menu, ID2P(LANG_AMAZE_MENU), menu_cb,
+ ID2P(LANG_CHESSBOX_MENU_RESUME_GAME),
+ ID2P(LANG_CHESSBOX_MENU_NEW_GAME),
+ ID2P(LANG_SET_MAZE_SIZE),
+ ID2P(LANG_VIEW_MAP),
+ ID2P(LANG_SHOW_COMPASS),
+ ID2P(LANG_SHOW_MAP),
+ ID2P(LANG_REMEMBER_PATH),
+ ID2P(LANG_USE_LARGE_TILES),
+ ID2P(LANG_SHOW_SOLUTION),
+ ID2P(LANG_QUIT_WITHOUT_SAVING),
+ ID2P(LANG_MENU_QUIT)
+ );
+
+ clearscreen();
+
+ while(!exit_menu)
+ {
+ result = rb->do_menu(&menu, &selection, NULL, false);
+ switch(result)
+ {
+ case 0: /* resume */
+ exit_menu = true;
+ if (!ingame) {
+ save_prefs(PREF_FILE);
+ }
+ break;
+ case 1: /* new game */
+ exit_menu = true;
+ if (ingame)
+ {
+ rb->splash(0, ID2P(LANG_GENERATING_MAZE));
+ clearmap(umap);
+ makemaze();
+
+ /* Show where the goal is */
+ copyumap(gy, gx, 1);
+ rb->lcd_update();
+
+ mappmove(py, px, pdir);
+
+ if (remember_visited)
+ punder = VISITED;
+ else
+ punder = SPACE;
+ }
+ else { /* !ingame */
+ loaded=false;
+ save_prefs(PREF_FILE);
+ }
+ break;
+ case 2: /* Set maze size */
+ {
+ int old_size = maze_size;
+ rb->set_option(rb->str(LANG_SET_MAZE_SIZE), &maze_size, RB_INT,
+ mazesize_text, 4, NULL);
+ if (maze_size != old_size)
+ loaded = false;
+ }
+ break;
+ case 3: /* View map */
+ exit_menu = true;
+ draw_portion_map();
+ break;
+ case 4: /* Show compass option */
+ rb->set_option(rb->str(LANG_SHOW_COMPASS), &compass, RB_BOOL,
+ noyes_text, 2, NULL);
+ break;
+ case 5: /* Show Map option */
+ rb->set_option(rb->str(LANG_SHOW_MAP), &show_map, RB_BOOL,
+ noyes_text, 2, NULL);
+ break;
+ case 6: /* Remember Path option */
+ rb->set_option(rb->str(LANG_REMEMBER_PATH), &remember_visited, RB_BOOL,
+ noyes_text, 2, NULL);
+ break;
+ case 7: /* Tilesize option */
+ rb->set_option(rb->str(LANG_USE_LARGE_TILES), &use_large_tiles, RB_BOOL,
+ noyes_text, 2, NULL);
+ break;
+ case 8: /* solver */
+ exit_menu = true;
+ cheated++;
+ walkleft();
+ break;
+ case 9: /* quit w/o saving */
+ exit_menu = true;
+ status = 0;
+ break;
+ case 10: /* save+quit */
+ exit_menu = true;
+ if (ingame) {
+ if (save_game())
+ status = 0;
+ else
+ rb->splash(HZ*3, ID2P(LANG_ERROR_WRITING_CONFIG));
+ }
+ else {
+ if(loaded)
+ save_game();
+ status = 0;
+ }
+ break;
+ }
+ }
+ return status;
+}
+
+int amaze(void)
+{
+ int quitting;
+ int i;
+ int input;
+
+ clearscreen();
+ rb->lcd_setfont(FONT_SYSFIXED);
+ if(!loaded)
+ rb->splash(0, ID2P(LANG_GENERATING_MAZE));
+
+ crd_x[0] = LCD_WIDTH + 1;
+ crd_y[0] = LCD_HEIGHT + 1;
+ for (depth=1; depth < MAX_DEPTH + 1; depth++)
+ {
+ crd_x[depth] = crd_x[depth-1]*2/3;
+ if(crd_x[depth] % 2 != 0) crd_x[depth]++;
+ crd_y[depth] = crd_y[depth-1]*2/3;
+ if(crd_y[depth] % 2 != 0) crd_y[depth]++;
+ if (crd_x[depth]==crd_x[depth-1] || crd_y[depth]==crd_y[depth-1])
+ break;
+ }
+ --depth;
+
+ if (!loaded)
+ {
+ clearmap(umap);
+ makemaze();
+ }
+
+ /* Show where the goal is */
+
+ copyumap(gy, gx, 1);
+
+ rb->lcd_update();
+
+ quitting = 0;
+
+ mappmove(py, px, pdir);
+
+ if (remember_visited)
+ punder = VISITED;
+ else
+ punder = SPACE;
+
+ clearscreen();
+ graphic_view();
+
+ while (!quitting && !won)
+ {
+
+ rb->lcd_update();
+
+ input = pluginlib_getaction(TIMEOUT_BLOCK, plugin_contexts,
+ ARRAYLEN(plugin_contexts));
+
+ switch (input)
+ {
+ case PLA_CANCEL:
+ case PLA_EXIT:
+ i = menu();
+ rb->lcd_setfont(FONT_SYSFIXED);
+ clearscreen();
+ graphic_view();
+
+ switch (i)
+ {
+ case 0:
+ quitting = 1;
+ break;
+ case 1:
+ break;
+ case 2:
+ return 0;
+ break;
+ }
+ break;
+ case PLA_UP:
+ case PLA_UP_REPEAT:
+ trymove(pdir);
+ break;
+ case PLA_DOWN:
+ case PLA_DOWN_REPEAT:
+ trymove(REVERSE_OF(pdir));
+ break;
+ case PLA_LEFT:
+ mappmove(py, px, LEFT_OF(pdir));
+ graphic_view();
+ break;
+ case PLA_RIGHT:
+ mappmove(py, px, RIGHT_OF(pdir));
+ graphic_view();
+ break;
+ case PLA_SELECT:
+ if (punder==SPACE || punder==VISITED)
+ { /* mark ground */
+ punder = pdir + '0';
+ }
+ else
+ { /* clear mark */
+ if (remember_visited)
+ punder = VISITED;
+ else
+ punder = SPACE;
+ }
+ graphic_view();
+ break;
+ }
+ }
+
+ rb->lcd_update();
+ //graphic_view();
+ if (won)
+ {
+ won = false; /* reset boolean */
+ if (cheated)
+ {
+ rb->splash(HZ*2, ID2P(LANG_YOU_CHEATED));
+ return 0;
+ }
+ rb->splash(HZ*2, ID2P(LANG_YOU_WIN));
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+bool save_prefs(char *filename)
+{
+ int fd;
+ char ms[2] = { (char)(maze_size) + '0', '\n' };
+ char sm[2] = { (char)(show_map) + '0', '\n' };
+ char rv[2] = { (char)(remember_visited) + '0', '\n' };
+ char lt[2] = { (char)(use_large_tiles) + '0', '\n' };
+
+ fd = rb->open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if(fd >= 0)
+ {
+ rb->write(fd, ms, 2);
+ rb->write(fd, sm, 2);
+ rb->write(fd, rv, 2);
+ rb->write(fd, lt, 2);
+ }
+ else
+ {
+ rb->splash(HZ, ID2P(LANG_ERROR_WRITING_CONFIG));
+ return false;
+ }
+ rb->close(fd);
+ return true;
+}
+
+bool load_prefs(char *filename)
+{
+ int fd;
+ char instr[2];
+
+ fd = rb->open(filename, O_RDONLY);
+ if (fd < 0)
+ {
+ LOGF("Invalid preferences file: %s\n", filename);
+ return false;
+ }
+
+ rb->read(fd, instr, sizeof(instr));
+ maze_size = (int)(instr[0] - '0');
+ rb->read(fd, instr, sizeof(instr));
+ show_map = (bool)(instr[0] - '0');
+ rb->read(fd, instr, sizeof(instr));
+ remember_visited = (bool)(instr[0] - '0');
+ rb->read(fd, instr, sizeof(instr));
+ use_large_tiles = (bool)(instr[0] - '0');
+ rb->close(fd);
+
+ return true;
+}
+
+enum plugin_status plugin_start(const void *parameter)
+{
+ (void) parameter;
+ int ret;
+
+ rb->srand(*rb->current_tick);
+
+ /* hard-code in program default options */
+ show_map=1;
+ remember_visited=1;
+ use_large_tiles=1;
+ maze_size=1;
+
+ loaded=load_game();
+
+ /* let's go, gentlemen, we have some work to do */
+#if LCD_DEPTH > 1
+ rb->lcd_set_backdrop(NULL);
+#endif
+ ingame = false;
+ ret = menu();
+ if (ret) {
+ ingame = true;
+ amaze();
+ }
+
+ rb->lcd_setfont(FONT_UI);
+
+ return PLUGIN_OK;
+}
diff --git a/apps/plugins/bitmaps/native/SOURCES b/apps/plugins/bitmaps/native/SOURCES
index 814845dc5b..cfc9ebcf6a 100644
--- a/apps/plugins/bitmaps/native/SOURCES
+++ b/apps/plugins/bitmaps/native/SOURCES
@@ -27,6 +27,44 @@ _2048_background.56x56x24.bmp
#endif
#undef MIN
+/* amaze */
+#if defined(HAVE_LCD_COLOR)
+#if LCD_WIDTH >= 220 || LCD_HEIGHT >= 220
+amaze_tiles_9.9x9x16.bmp
+amaze_tiles_7.7x7x16.bmp
+#elif LCD_WIDTH >= 160 || LCD_HEIGHT >= 160
+amaze_tiles_7.7x7x16.bmp
+amaze_tiles_5.5x5x16.bmp
+#else
+amaze_tiles_5.5x5x16.bmp
+amaze_tiles_3.3x3x16.bmp
+#endif
+
+#elif LCD_DEPTH > 1
+#if LCD_WIDTH >= 220 || LCD_HEIGHT >= 220
+amaze_tiles_9.9x9x2.bmp
+amaze_tiles_7.7x7x2.bmp
+#elif LCD_WIDTH >= 160 || LCD_HEIGHT >= 160
+amaze_tiles_7.7x7x2.bmp
+amaze_tiles_5.5x5x2.bmp
+#else
+amaze_tiles_5.5x5x2.bmp
+amaze_tiles_3.3x3x2.bmp
+#endif
+
+#else /* mono */
+#if LCD_WIDTH >= 220 || LCD_HEIGHT >= 220
+amaze_tiles_9.9x9x1.bmp
+amaze_tiles_7.7x7x1.bmp
+#elif LCD_WIDTH >= 160 || LCD_HEIGHT >= 160
+amaze_tiles_7.7x7x1.bmp
+amaze_tiles_5.5x5x1.bmp
+#else
+amaze_tiles_5.5x5x1.bmp
+amaze_tiles_3.3x3x1.bmp
+#endif
+#endif /* amaze */
+
/* Brickmania */
#ifdef HAVE_LCD_COLOR
#if LCD_WIDTH >= 112
diff --git a/apps/plugins/bitmaps/native/amaze_tiles_3.3x3x1.bmp b/apps/plugins/bitmaps/native/amaze_tiles_3.3x3x1.bmp
new file mode 100644
index 0000000000..66418779de
--- /dev/null
+++ b/apps/plugins/bitmaps/native/amaze_tiles_3.3x3x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/amaze_tiles_3.3x3x16.bmp b/apps/plugins/bitmaps/native/amaze_tiles_3.3x3x16.bmp
new file mode 100644
index 0000000000..930d4c62fc
--- /dev/null
+++ b/apps/plugins/bitmaps/native/amaze_tiles_3.3x3x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/amaze_tiles_3.3x3x2.bmp b/apps/plugins/bitmaps/native/amaze_tiles_3.3x3x2.bmp
new file mode 100644
index 0000000000..504525b7f4
--- /dev/null
+++ b/apps/plugins/bitmaps/native/amaze_tiles_3.3x3x2.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/amaze_tiles_5.5x5x1.bmp b/apps/plugins/bitmaps/native/amaze_tiles_5.5x5x1.bmp
new file mode 100644
index 0000000000..d5973cfc5e
--- /dev/null
+++ b/apps/plugins/bitmaps/native/amaze_tiles_5.5x5x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/amaze_tiles_5.5x5x16.bmp b/apps/plugins/bitmaps/native/amaze_tiles_5.5x5x16.bmp
new file mode 100644
index 0000000000..7ec316df6a
--- /dev/null
+++ b/apps/plugins/bitmaps/native/amaze_tiles_5.5x5x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/amaze_tiles_5.5x5x2.bmp b/apps/plugins/bitmaps/native/amaze_tiles_5.5x5x2.bmp
new file mode 100644
index 0000000000..e06075310e
--- /dev/null
+++ b/apps/plugins/bitmaps/native/amaze_tiles_5.5x5x2.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/amaze_tiles_7.7x7x1.bmp b/apps/plugins/bitmaps/native/amaze_tiles_7.7x7x1.bmp
new file mode 100644
index 0000000000..820f5a873b
--- /dev/null
+++ b/apps/plugins/bitmaps/native/amaze_tiles_7.7x7x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/amaze_tiles_7.7x7x16.bmp b/apps/plugins/bitmaps/native/amaze_tiles_7.7x7x16.bmp
new file mode 100644
index 0000000000..e9d6bc5086
--- /dev/null
+++ b/apps/plugins/bitmaps/native/amaze_tiles_7.7x7x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/amaze_tiles_7.7x7x2.bmp b/apps/plugins/bitmaps/native/amaze_tiles_7.7x7x2.bmp
new file mode 100644
index 0000000000..2e8e76593b
--- /dev/null
+++ b/apps/plugins/bitmaps/native/amaze_tiles_7.7x7x2.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/amaze_tiles_9.9x9x1.bmp b/apps/plugins/bitmaps/native/amaze_tiles_9.9x9x1.bmp
new file mode 100644
index 0000000000..0f2212e7e5
--- /dev/null
+++ b/apps/plugins/bitmaps/native/amaze_tiles_9.9x9x1.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/amaze_tiles_9.9x9x16.bmp b/apps/plugins/bitmaps/native/amaze_tiles_9.9x9x16.bmp
new file mode 100644
index 0000000000..0188dd9726
--- /dev/null
+++ b/apps/plugins/bitmaps/native/amaze_tiles_9.9x9x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/amaze_tiles_9.9x9x2.bmp b/apps/plugins/bitmaps/native/amaze_tiles_9.9x9x2.bmp
new file mode 100644
index 0000000000..f095368d30
--- /dev/null
+++ b/apps/plugins/bitmaps/native/amaze_tiles_9.9x9x2.bmp
Binary files differ
diff --git a/docs/CREDITS b/docs/CREDITS
index 4783033466..363c7b7dfb 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -717,6 +717,7 @@ Richard Goedeken
Mihaly 'Hermit' Horvath
Uwe Kleine-König
JJ Style
+Jerry Chapman
The libmad team
The wavpack team
diff --git a/manual/plugins/amaze.tex b/manual/plugins/amaze.tex
new file mode 100644
index 0000000000..66b3abeb4b
--- /dev/null
+++ b/manual/plugins/amaze.tex
@@ -0,0 +1,6 @@
+\subsection{Amaze}
+\screenshot{plugins/images/ss-amaze}{Amaze}{img:amaze}
+Amaze is a simple 3D maze game.
+
+Based upon the curses-based amaze by David Leonard, graciously placed
+into the public domain. More information can be found here: \url{https://www.adaptive-enterprises.com.au/~d/software/amaze/}
diff --git a/manual/plugins/images/ss-amaze-128x160x24.png b/manual/plugins/images/ss-amaze-128x160x24.png
new file mode 100644
index 0000000000..71305d47d9
--- /dev/null
+++ b/manual/plugins/images/ss-amaze-128x160x24.png
Binary files differ
diff --git a/manual/plugins/images/ss-amaze-128x64x1.png b/manual/plugins/images/ss-amaze-128x64x1.png
new file mode 100644
index 0000000000..f0a9680fda
--- /dev/null
+++ b/manual/plugins/images/ss-amaze-128x64x1.png
Binary files differ
diff --git a/manual/plugins/images/ss-amaze-138x110x2.png b/manual/plugins/images/ss-amaze-138x110x2.png
new file mode 100644
index 0000000000..6a2014ecae
--- /dev/null
+++ b/manual/plugins/images/ss-amaze-138x110x2.png
Binary files differ
diff --git a/manual/plugins/images/ss-amaze-176x132x16.png b/manual/plugins/images/ss-amaze-176x132x16.png
new file mode 100644
index 0000000000..9d581393a6
--- /dev/null
+++ b/manual/plugins/images/ss-amaze-176x132x16.png
Binary files differ
diff --git a/manual/plugins/images/ss-amaze-240x320x24.png b/manual/plugins/images/ss-amaze-240x320x24.png
new file mode 100644
index 0000000000..2fce9b7865
--- /dev/null
+++ b/manual/plugins/images/ss-amaze-240x320x24.png
Binary files differ
diff --git a/manual/plugins/images/ss-amaze-320x240x16.png b/manual/plugins/images/ss-amaze-320x240x16.png
new file mode 100644
index 0000000000..77e9ca8bb8
--- /dev/null
+++ b/manual/plugins/images/ss-amaze-320x240x16.png
Binary files differ
diff --git a/manual/plugins/main.tex b/manual/plugins/main.tex
index 4725818029..19636292ad 100644
--- a/manual/plugins/main.tex
+++ b/manual/plugins/main.tex
@@ -25,6 +25,8 @@ text files%
\input{plugins/2048.tex}
+\input{plugins/amaze.tex}
+
\input{plugins/blackjack.tex}
\opt{large_plugin_buffer}{\input{plugins/boomshine.tex}}