46b7028f91
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15033 a1c6a512-1295-4272-9138-f99709370657
634 lines
16 KiB
C
634 lines
16 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* robotfindskitten: A Zen simulation
|
|
*
|
|
* Copyright (C) 1997,2000 Leonard Richardson
|
|
* leonardr@segfault.org
|
|
* http://www.crummy.com/devel/
|
|
*
|
|
* 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 program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or EXISTANCE OF KITTEN. See the GNU General
|
|
* Public License for more details.
|
|
*
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*
|
|
* Ported to Rockbox 2007 by Jonas Häggqvist
|
|
*/
|
|
|
|
#include "plugin.h"
|
|
#include "pluginlib_actions.h"
|
|
|
|
/* This macros must always be included. Should be placed at the top by
|
|
convention, although the actual position doesn't matter */
|
|
PLUGIN_HEADER
|
|
|
|
/*The messages go in a separate file because they are collectively
|
|
huge, and you might want to modify them. It would be nice to load
|
|
the messages from a text file at run time.*/
|
|
#include "robotfindskitten_messages.h"
|
|
|
|
#define TRUE true
|
|
#define FALSE false
|
|
|
|
#define RFK_VERSION "v1.4142135.406"
|
|
|
|
/* Button definitions stolen from maze.c */
|
|
#if (CONFIG_KEYPAD == IPOD_4G_PAD) || \
|
|
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
|
|
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
|
|
# undef __PLUGINLIB_ACTIONS_H__
|
|
# define RFK_QUIT (BUTTON_SELECT | BUTTON_MENU)
|
|
# define RFK_RIGHT BUTTON_RIGHT
|
|
# define RFK_LEFT BUTTON_LEFT
|
|
# define RFK_UP BUTTON_MENU
|
|
# define RFK_DOWN BUTTON_PLAY
|
|
# define RFK_RRIGHT (BUTTON_RIGHT | BUTTON_REPEAT)
|
|
# define RFK_RLEFT (BUTTON_LEFT | BUTTON_REPEAT)
|
|
# define RFK_RUP (BUTTON_MENU | BUTTON_REPEAT)
|
|
# define RFK_RDOWN (BUTTON_PLAY | BUTTON_REPEAT)
|
|
|
|
#else
|
|
# define RFK_QUIT PLA_QUIT
|
|
# define RFK_RIGHT PLA_RIGHT
|
|
# define RFK_LEFT PLA_LEFT
|
|
# define RFK_UP PLA_UP
|
|
# define RFK_DOWN PLA_DOWN
|
|
# define RFK_RRIGHT PLA_RIGHT_REPEAT
|
|
# define RFK_RLEFT PLA_LEFT_REPEAT
|
|
# define RFK_RUP PLA_UP_REPEAT
|
|
# define RFK_RDOWN PLA_DOWN_REPEAT
|
|
|
|
#endif
|
|
/*Constants for our internal representation of the screen.*/
|
|
#define EMPTY -1
|
|
#define ROBOT 0
|
|
#define KITTEN 1
|
|
|
|
/*Screen dimensions.*/
|
|
#define X_MIN 0
|
|
#define X_MAX ((LCD_WIDTH/SYSFONT_WIDTH) - 1)
|
|
#define Y_MIN 3
|
|
#define Y_MAX ((LCD_HEIGHT/SYSFONT_HEIGHT) - 1)
|
|
|
|
/* Colours used */
|
|
#if LCD_DEPTH >= 16
|
|
#define NUM_COLORS 6
|
|
#define ROBOT_COLOR LCD_DARKGRAY
|
|
const unsigned colors[NUM_COLORS] = {
|
|
LCD_RGBPACK(255, 255, 0), /* Yellow */
|
|
LCD_RGBPACK(0, 255, 255), /* Cyan */
|
|
LCD_RGBPACK(255, 0, 255), /* Purple */
|
|
LCD_RGBPACK(0, 0, 255), /* Blue */
|
|
LCD_RGBPACK(255, 0, 0), /* Red */
|
|
LCD_RGBPACK(0, 255, 0), /* Green */
|
|
};
|
|
#elif LCD_DEPTH == 2
|
|
#define NUM_COLORS 3
|
|
#define ROBOT_COLOR LCD_DARKGRAY
|
|
const unsigned colors[NUM_COLORS] = {
|
|
LCD_LIGHTGRAY,
|
|
LCD_DARKGRAY,
|
|
LCD_BLACK,
|
|
};
|
|
#elif LCD_DEPTH == 1
|
|
#define NUM_COLORS 1
|
|
#define ROBOT_COLOR 0
|
|
const unsigned colors[NUM_COLORS] = {
|
|
0,
|
|
};
|
|
#endif /* HAVE_LCD_COLOR */
|
|
|
|
/*Macros for generating numbers in different ranges*/
|
|
#define randx() (rb->rand() % X_MAX) + 1
|
|
#define randy() (rb->rand() % (Y_MAX-Y_MIN+1))+Y_MIN /*I'm feeling randy()!*/
|
|
#define randchar() rb->rand() % (126-'!'+1)+'!';
|
|
#define randcolor() rb->rand() % NUM_COLORS
|
|
#define randbold() (rb->rand() % 2 ? TRUE:FALSE)
|
|
|
|
/*Row constants for the animation*/
|
|
#define ADV_ROW 1
|
|
#define ANIMATION_MEET (X_MAX/3)*2
|
|
#define ANIMATION_LENGTH 4
|
|
|
|
/*This struct contains all the information we need to display an object
|
|
on the screen*/
|
|
typedef struct
|
|
{
|
|
short x;
|
|
short y;
|
|
int color;
|
|
bool bold;
|
|
char character;
|
|
} screen_object;
|
|
|
|
/*
|
|
*Function definitions
|
|
*/
|
|
|
|
/*Initialization and setup functions*/
|
|
static void initialize_arrays(void);
|
|
static void initialize_robot(void);
|
|
static void initialize_kitten(void);
|
|
static void initialize_bogus(void);
|
|
static void initialize_screen(void);
|
|
static void instructions(void);
|
|
static void finish(int sig);
|
|
|
|
/*Game functions*/
|
|
static void play_game(void);
|
|
static void process_input(int);
|
|
|
|
/*Helper functions*/
|
|
static void pause(void);
|
|
static int validchar(char);
|
|
static void play_animation(int);
|
|
|
|
/*Global variables. Bite me, it's fun.*/
|
|
screen_object robot;
|
|
screen_object kitten;
|
|
|
|
#if X_MAX*Y_MAX < 200
|
|
#define NUM_BOGUS 15
|
|
#else
|
|
#define NUM_BOGUS 20
|
|
#endif
|
|
screen_object bogus[NUM_BOGUS];
|
|
unsigned short bogus_messages[NUM_BOGUS];
|
|
bool used_messages[MESSAGES];
|
|
|
|
bool exit_rfk;
|
|
|
|
/* This array contains our internal representation of the screen. The
|
|
array is bigger than it needs to be, as we don't need to keep track
|
|
of the first few rows of the screen. But that requires making an
|
|
offset function and using that everywhere. So not right now. */
|
|
int screen[X_MAX + 1][Y_MAX + 1];
|
|
|
|
/* here is a global api struct pointer. while not strictly necessary,
|
|
it's nice not to have to pass the api pointer in all function calls
|
|
in the plugin */
|
|
static struct plugin_api* rb;
|
|
|
|
/******************************************************************************
|
|
*
|
|
* Begin meaty routines that do the dirty work.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
MEM_FUNCTION_WRAPPERS(rb)
|
|
|
|
static void drawchar(int x, int y, char c)
|
|
{
|
|
char str[2];
|
|
rb->snprintf(str, sizeof(str), "%c", c);
|
|
rb->lcd_putsxy(x*SYSFONT_WIDTH, y*SYSFONT_HEIGHT, str);
|
|
}
|
|
|
|
static void draw(screen_object o)
|
|
{
|
|
#if LCD_DEPTH > 1
|
|
unsigned oldforeground;
|
|
oldforeground = rb->lcd_get_foreground();
|
|
rb->lcd_set_foreground(o.color);
|
|
drawchar(o.x, o.y, o.character);
|
|
rb->lcd_set_foreground(oldforeground);
|
|
#else
|
|
drawchar(o.x, o.y, o.character);
|
|
#endif
|
|
}
|
|
|
|
static void message(char * str)
|
|
{
|
|
rb->lcd_puts_scroll(0, ADV_ROW, str);
|
|
}
|
|
|
|
static void refresh(void)
|
|
{
|
|
rb->lcd_update();
|
|
}
|
|
|
|
/*
|
|
*play_game waits in a loop getting input and sending it to process_input
|
|
*/
|
|
static void play_game()
|
|
{
|
|
int old_x = robot.x;
|
|
int old_y = robot.y;
|
|
int input = 0; /* Not sure what a reasonable initial value is */
|
|
#ifdef __PLUGINLIB_ACTIONS_H__
|
|
const struct button_mapping *plugin_contexts[] = {generic_directions, generic_actions};
|
|
#endif
|
|
|
|
while (input != RFK_QUIT && exit_rfk == false)
|
|
{
|
|
process_input(input);
|
|
|
|
/*Redraw robot, where applicable. We're your station, robot.*/
|
|
if (!(old_x == robot.x && old_y == robot.y))
|
|
{
|
|
/*Get rid of the old robot*/
|
|
drawchar(old_x, old_y, ' ');
|
|
screen[old_x][old_y] = EMPTY;
|
|
|
|
/*Meet the new robot, same as the old robot.*/
|
|
draw(robot);
|
|
refresh();
|
|
screen[robot.x][robot.y] = ROBOT;
|
|
|
|
old_x = robot.x;
|
|
old_y = robot.y;
|
|
}
|
|
#ifdef __PLUGINLIB_ACTIONS_H__
|
|
input = pluginlib_getaction(rb, TIMEOUT_BLOCK, plugin_contexts, 2);
|
|
#else
|
|
input = rb->button_get(true);
|
|
#endif
|
|
}
|
|
message("Bye!");
|
|
refresh();
|
|
}
|
|
|
|
/*
|
|
*Given the keyboard input, process_input interprets it in terms of moving,
|
|
*touching objects, etc.
|
|
*/
|
|
static void process_input(int input)
|
|
{
|
|
int check_x = robot.x;
|
|
int check_y = robot.y;
|
|
|
|
switch (input)
|
|
{
|
|
case RFK_UP:
|
|
case RFK_RUP:
|
|
check_y--;
|
|
break;
|
|
case RFK_DOWN:
|
|
case RFK_RDOWN:
|
|
check_y++;
|
|
break;
|
|
case RFK_LEFT:
|
|
case RFK_RLEFT:
|
|
check_x--;
|
|
break;
|
|
case RFK_RIGHT:
|
|
case RFK_RRIGHT:
|
|
check_x++;
|
|
break;
|
|
}
|
|
|
|
/*Check for going off the edge of the screen.*/
|
|
if (check_y < Y_MIN || check_y > Y_MAX || check_x < X_MIN || check_x > X_MAX)
|
|
{
|
|
return; /*Do nothing.*/
|
|
}
|
|
|
|
/*
|
|
* Clear textline
|
|
* disabled because it breaks the scrolling for some reason
|
|
*/
|
|
/* rb->lcd_puts_scroll(0, ADV_ROW, " "); */
|
|
|
|
/*Check for collision*/
|
|
if (screen[check_x][check_y] != EMPTY)
|
|
{
|
|
switch (screen[check_x][check_y])
|
|
{
|
|
case ROBOT:
|
|
/*We didn't move, or we're stuck in a
|
|
time warp or something.*/
|
|
break;
|
|
case KITTEN: /*Found it!*/
|
|
play_animation(input);
|
|
/* Wait for the user to click something */
|
|
pause();
|
|
break;
|
|
default: /*We hit a bogus object; print its message.*/
|
|
message(messages[bogus_messages[screen[check_x][check_y]-2]]);
|
|
refresh();
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*Otherwise, move the robot.*/
|
|
robot.x = check_x;
|
|
robot.y = check_y;
|
|
}
|
|
|
|
/*finish is called upon signal or progam exit*/
|
|
static void finish(int sig)
|
|
{
|
|
(void)sig;
|
|
exit_rfk = true;
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* Begin helper routines
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static void pause()
|
|
{
|
|
int button;
|
|
rb->lcd_update();
|
|
do
|
|
button = rb->button_get(true);
|
|
while( ( button == BUTTON_NONE )
|
|
|| ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
|
|
}
|
|
|
|
static int validchar(char a)
|
|
{
|
|
switch(a)
|
|
{
|
|
case '#':
|
|
case ' ':
|
|
case 127:
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void play_animation(int input)
|
|
{
|
|
int counter;
|
|
screen_object left;
|
|
screen_object right;
|
|
/*The grand cinema scene.*/
|
|
rb->lcd_puts_scroll(0, ADV_ROW, " ");
|
|
|
|
if (input == RFK_RIGHT || input == RFK_DOWN || input == RFK_RRIGHT || input == RFK_RDOWN) {
|
|
left = robot;
|
|
right = kitten;
|
|
}
|
|
else {
|
|
left = kitten;
|
|
right = robot;
|
|
}
|
|
left.y = ADV_ROW;
|
|
right.y = ADV_ROW;
|
|
left.x = ANIMATION_MEET - ANIMATION_LENGTH - 1;
|
|
right.x = ANIMATION_MEET + ANIMATION_LENGTH;
|
|
|
|
for (counter = ANIMATION_LENGTH; counter > 0; counter--)
|
|
{
|
|
left.x++;
|
|
right.x--;
|
|
/* Clear the previous position (empty the first time) */
|
|
drawchar(left.x - 1, left.y, ' ');
|
|
drawchar(right.x + 1, right.y, ' ');
|
|
draw(left);
|
|
draw(right);
|
|
refresh();
|
|
rb->sleep(HZ);
|
|
}
|
|
|
|
message("You found kitten! Way to go, robot!");
|
|
refresh();
|
|
finish(0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* Begin initialization routines (called before play begins).
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static void instructions()
|
|
{
|
|
#define MARGIN 2
|
|
int y = MARGIN, space_w, width, height;
|
|
unsigned short x = MARGIN, i = 0;
|
|
#define WORDS (sizeof instructions / sizeof (char*))
|
|
static char* instructions[] = {
|
|
#if 0
|
|
/* Not sure if we want to include this? */
|
|
"robotfindskitten", RFK_VERSION, "", "",
|
|
"By", "the", "illustrious", "Leonard", "Richardson", "(C)", "1997,", "2000", "",
|
|
"Written", "originally", "for", "the", "Nerth", "Pork", "robotfindskitten", "contest", "", "",
|
|
#endif
|
|
"In", "this", "game", "you", "are", "robot", "(#).", "Your", "job", "is", "to", "find", "kitten.", "This", "task", "is", "complicated", "by", "the", "existence", "of", "various", "things", "which", "are", "not", "kitten.", "Robot", "must", "touch", "items", "to", "determine", "if", "they", "are", "kitten", "or", "not.", "",
|
|
"The", "game", "ends", "when", "robotfindskitten.", "", "",
|
|
"Press", "any", "key", "to", "start",
|
|
};
|
|
rb->lcd_clear_display();
|
|
rb->lcd_getstringsize(" ", &space_w, &height);
|
|
for (i = 0; i < WORDS; i++) {
|
|
rb->lcd_getstringsize(instructions[i], &width, NULL);
|
|
/* Skip to next line if the current one can't fit the word */
|
|
if (x + width > LCD_WIDTH - MARGIN) {
|
|
x = MARGIN;
|
|
y += height;
|
|
}
|
|
/* .. or if the word is the empty string */
|
|
if (rb->strcmp(instructions[i], "") == 0) {
|
|
x = MARGIN;
|
|
y += height;
|
|
continue;
|
|
}
|
|
/* We filled the screen */
|
|
if (y + height > LCD_HEIGHT - MARGIN) {
|
|
y = MARGIN;
|
|
pause();
|
|
rb->lcd_clear_display();
|
|
}
|
|
rb->lcd_putsxy(x, y, instructions[i]);
|
|
x += width + space_w;
|
|
}
|
|
pause();
|
|
}
|
|
|
|
static void initialize_arrays()
|
|
{
|
|
unsigned int counter, counter2;
|
|
screen_object empty;
|
|
|
|
/*Initialize the empty object.*/
|
|
empty.x = -1;
|
|
empty.y = -1;
|
|
#if LCD_DEPTH > 1
|
|
empty.color = LCD_BLACK;
|
|
#else
|
|
empty.color = 0;
|
|
#endif
|
|
empty.bold = FALSE;
|
|
empty.character = ' ';
|
|
|
|
for (counter = 0; counter <= X_MAX; counter++)
|
|
{
|
|
for (counter2 = 0; counter2 <= Y_MAX; counter2++)
|
|
{
|
|
screen[counter][counter2] = EMPTY;
|
|
}
|
|
}
|
|
|
|
/*Initialize the other arrays.*/
|
|
for (counter = 0; counter < MESSAGES; counter++)
|
|
{
|
|
used_messages[counter] = false;
|
|
}
|
|
for (counter = 0; counter < NUM_BOGUS; counter++)
|
|
{
|
|
bogus_messages[counter] = 0;
|
|
bogus[counter] = empty;
|
|
}
|
|
}
|
|
|
|
/*initialize_robot initializes robot.*/
|
|
static void initialize_robot()
|
|
{
|
|
/*Assign a position to the player.*/
|
|
robot.x = randx();
|
|
robot.y = randy();
|
|
|
|
robot.character = '#';
|
|
robot.color = ROBOT_COLOR;
|
|
robot.bold = FALSE;
|
|
screen[robot.x][robot.y] = ROBOT;
|
|
}
|
|
|
|
/*initialize kitten, well, initializes kitten.*/
|
|
static void initialize_kitten()
|
|
{
|
|
/*Assign the kitten a unique position.*/
|
|
do
|
|
{
|
|
kitten.x = randx();
|
|
kitten.y = randy();
|
|
} while (screen[kitten.x][kitten.y] != EMPTY);
|
|
|
|
/*Assign the kitten a character and a color.*/
|
|
do {
|
|
kitten.character = randchar();
|
|
} while (!(validchar(kitten.character)));
|
|
screen[kitten.x][kitten.y] = KITTEN;
|
|
|
|
kitten.color = colors[randcolor()];
|
|
kitten.bold = randbold();
|
|
}
|
|
|
|
/*initialize_bogus initializes all non-kitten objects to be used in this run.*/
|
|
static void initialize_bogus()
|
|
{
|
|
int counter, index;
|
|
for (counter = 0; counter < NUM_BOGUS; counter++)
|
|
{
|
|
/*Give it a color.*/
|
|
bogus[counter].color = colors[randcolor()];
|
|
bogus[counter].bold = randbold();
|
|
|
|
/*Give it a character.*/
|
|
do {
|
|
bogus[counter].character = randchar();
|
|
} while (!(validchar(bogus[counter].character)));
|
|
|
|
/*Give it a position.*/
|
|
do
|
|
{
|
|
bogus[counter].x = randx();
|
|
bogus[counter].y = randy();
|
|
} while (screen[bogus[counter].x][bogus[counter].y] != EMPTY);
|
|
|
|
screen[bogus[counter].x][bogus[counter].y] = counter+2;
|
|
|
|
/*Find a message for this object.*/
|
|
do {
|
|
index = rb->rand() % MESSAGES;
|
|
} while (used_messages[index] != false);
|
|
bogus_messages[counter] = index;
|
|
used_messages[index] = true;
|
|
}
|
|
|
|
}
|
|
|
|
/*initialize_screen paints the screen.*/
|
|
static void initialize_screen()
|
|
{
|
|
int counter;
|
|
char buf[40];
|
|
|
|
/*
|
|
*Print the status portion of the screen.
|
|
*/
|
|
rb->lcd_clear_display();
|
|
rb->lcd_setfont(FONT_SYSFIXED);
|
|
rb->snprintf(buf, sizeof(buf), "robotfindskitten %s", RFK_VERSION);
|
|
rb->lcd_puts_scroll(0, 0, buf);
|
|
refresh();
|
|
|
|
/*Draw a line across the screen.*/
|
|
for (counter = X_MIN; counter <= X_MAX + 1; counter++)
|
|
{
|
|
drawchar(counter, ADV_ROW+1, '_');
|
|
}
|
|
|
|
/*
|
|
*Draw all the objects on the playing field.
|
|
*/
|
|
for (counter = 0; counter < NUM_BOGUS; counter++)
|
|
{
|
|
draw(bogus[counter]);
|
|
}
|
|
|
|
draw(kitten);
|
|
draw(robot);
|
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
/* this is the plugin entry point */
|
|
enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
|
|
{
|
|
(void)parameter;
|
|
rb = api;
|
|
|
|
exit_rfk = false;
|
|
|
|
rb->srand(*rb->current_tick);
|
|
|
|
initialize_arrays();
|
|
|
|
/*
|
|
* Now we initialize the various game objects.
|
|
*/
|
|
initialize_robot();
|
|
initialize_kitten();
|
|
initialize_bogus();
|
|
|
|
/*
|
|
* Set up white-on-black screen on color targets
|
|
*/
|
|
#if LCD_DEPTH >= 16
|
|
rb->lcd_set_backdrop(NULL);
|
|
rb->lcd_set_foreground(LCD_WHITE);
|
|
rb->lcd_set_background(LCD_BLACK);
|
|
#endif
|
|
|
|
/*
|
|
* Run the game
|
|
*/
|
|
instructions();
|
|
|
|
initialize_screen();
|
|
|
|
play_game();
|
|
|
|
rb->lcd_setfont(FONT_UI);
|
|
return PLUGIN_OK;
|
|
}
|