mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-10-13 10:07:38 -04:00
- 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
1620 lines
41 KiB
C
1620 lines
41 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* 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;
|
|
}
|