grr.. typo
[Rockbox.git] / apps / plugins / robotfindskitten.c
bloba2786b7e2d312a246ec2888d03589a93bc70d955
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * robotfindskitten: A Zen simulation
12 * Copyright (C) 1997,2000 Leonard Richardson
13 * leonardr@segfault.org
14 * http://www.crummy.com/devel/
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License as
18 * published by the Free Software Foundation; either version 2 of
19 * the License, or (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or EXISTANCE OF KITTEN. See the GNU General
24 * Public License for more details.
26 * http://www.gnu.org/copyleft/gpl.html
28 * Ported to Rockbox 2007 by Jonas Häggqvist
31 #include "plugin.h"
32 #include "pluginlib_actions.h"
34 /* This macros must always be included. Should be placed at the top by
35 convention, although the actual position doesn't matter */
36 PLUGIN_HEADER
38 /*The messages go in a separate file because they are collectively
39 huge, and you might want to modify them. It would be nice to load
40 the messages from a text file at run time.*/
41 #include "robotfindskitten_messages.h"
43 #define TRUE true
44 #define FALSE false
46 #define RFK_VERSION "v1.4142135.406"
48 /* Button definitions stolen from maze.c */
49 #if (CONFIG_KEYPAD == IPOD_4G_PAD) || \
50 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
51 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
52 # undef __PLUGINLIB_ACTIONS_H__
53 # define RFK_QUIT (BUTTON_SELECT | BUTTON_MENU)
54 # define RFK_RIGHT BUTTON_RIGHT
55 # define RFK_LEFT BUTTON_LEFT
56 # define RFK_UP BUTTON_MENU
57 # define RFK_DOWN BUTTON_PLAY
58 # define RFK_RRIGHT (BUTTON_RIGHT | BUTTON_REPEAT)
59 # define RFK_RLEFT (BUTTON_LEFT | BUTTON_REPEAT)
60 # define RFK_RUP (BUTTON_MENU | BUTTON_REPEAT)
61 # define RFK_RDOWN (BUTTON_PLAY | BUTTON_REPEAT)
63 #else
64 # define RFK_QUIT PLA_QUIT
65 # define RFK_RIGHT PLA_RIGHT
66 # define RFK_LEFT PLA_LEFT
67 # define RFK_UP PLA_UP
68 # define RFK_DOWN PLA_DOWN
69 # define RFK_RRIGHT PLA_RIGHT_REPEAT
70 # define RFK_RLEFT PLA_LEFT_REPEAT
71 # define RFK_RUP PLA_UP_REPEAT
72 # define RFK_RDOWN PLA_DOWN_REPEAT
74 #endif
75 /*Constants for our internal representation of the screen.*/
76 #define EMPTY -1
77 #define ROBOT 0
78 #define KITTEN 1
80 /*Screen dimensions.*/
81 #define X_MIN 0
82 #define X_MAX ((LCD_WIDTH/SYSFONT_WIDTH) - 1)
83 #define Y_MIN 3
84 #define Y_MAX ((LCD_HEIGHT/SYSFONT_HEIGHT) - 1)
86 /* Colours used */
87 #if LCD_DEPTH >= 16
88 #define NUM_COLORS 6
89 #define ROBOT_COLOR LCD_DARKGRAY
90 const unsigned colors[NUM_COLORS] = {
91 LCD_RGBPACK(255, 255, 0), /* Yellow */
92 LCD_RGBPACK(0, 255, 255), /* Cyan */
93 LCD_RGBPACK(255, 0, 255), /* Purple */
94 LCD_RGBPACK(0, 0, 255), /* Blue */
95 LCD_RGBPACK(255, 0, 0), /* Red */
96 LCD_RGBPACK(0, 255, 0), /* Green */
98 #elif LCD_DEPTH == 2
99 #define NUM_COLORS 3
100 #define ROBOT_COLOR LCD_DARKGRAY
101 const unsigned colors[NUM_COLORS] = {
102 LCD_LIGHTGRAY,
103 LCD_DARKGRAY,
104 LCD_BLACK,
106 #elif LCD_DEPTH == 1
107 #define NUM_COLORS 1
108 #define ROBOT_COLOR 0
109 const unsigned colors[NUM_COLORS] = {
112 #endif /* HAVE_LCD_COLOR */
114 /*Macros for generating numbers in different ranges*/
115 #define randx() (rb->rand() % X_MAX) + 1
116 #define randy() (rb->rand() % (Y_MAX-Y_MIN+1))+Y_MIN /*I'm feeling randy()!*/
117 #define randchar() rb->rand() % (126-'!'+1)+'!';
118 #define randcolor() rb->rand() % NUM_COLORS
119 #define randbold() (rb->rand() % 2 ? TRUE:FALSE)
121 /*Row constants for the animation*/
122 #define ADV_ROW 1
123 #define ANIMATION_MEET (X_MAX/3)*2
124 #define ANIMATION_LENGTH 4
126 /*This struct contains all the information we need to display an object
127 on the screen*/
128 typedef struct
130 short x;
131 short y;
132 int color;
133 bool bold;
134 char character;
135 } screen_object;
138 *Function definitions
141 /*Initialization and setup functions*/
142 static void initialize_arrays(void);
143 static void initialize_robot(void);
144 static void initialize_kitten(void);
145 static void initialize_bogus(void);
146 static void initialize_screen(void);
147 static void instructions(void);
148 static void finish(int sig);
150 /*Game functions*/
151 static void play_game(void);
152 static void process_input(int);
154 /*Helper functions*/
155 static void pause(void);
156 static int validchar(char);
157 static void play_animation(int);
159 /*Global variables. Bite me, it's fun.*/
160 screen_object robot;
161 screen_object kitten;
163 #if X_MAX*Y_MAX < 200
164 #define NUM_BOGUS 15
165 #else
166 #define NUM_BOGUS 20
167 #endif
168 screen_object bogus[NUM_BOGUS];
169 unsigned short bogus_messages[NUM_BOGUS];
170 bool used_messages[MESSAGES];
172 bool exit_rfk;
174 /* This array contains our internal representation of the screen. The
175 array is bigger than it needs to be, as we don't need to keep track
176 of the first few rows of the screen. But that requires making an
177 offset function and using that everywhere. So not right now. */
178 int screen[X_MAX + 1][Y_MAX + 1];
180 /* here is a global api struct pointer. while not strictly necessary,
181 it's nice not to have to pass the api pointer in all function calls
182 in the plugin */
183 static struct plugin_api* rb;
185 /******************************************************************************
187 * Begin meaty routines that do the dirty work.
189 *****************************************************************************/
191 MEM_FUNCTION_WRAPPERS(rb)
193 static void drawchar(int x, int y, char c)
195 char str[2];
196 rb->snprintf(str, sizeof(str), "%c", c);
197 rb->lcd_putsxy(x*SYSFONT_WIDTH, y*SYSFONT_HEIGHT, str);
200 static void draw(screen_object o)
202 #if LCD_DEPTH > 1
203 unsigned oldforeground;
204 oldforeground = rb->lcd_get_foreground();
205 rb->lcd_set_foreground(o.color);
206 drawchar(o.x, o.y, o.character);
207 rb->lcd_set_foreground(oldforeground);
208 #else
209 drawchar(o.x, o.y, o.character);
210 #endif
213 static void message(char * str)
215 rb->lcd_puts_scroll(0, ADV_ROW, str);
218 static void refresh(void)
220 rb->lcd_update();
224 *play_game waits in a loop getting input and sending it to process_input
226 static void play_game()
228 int old_x = robot.x;
229 int old_y = robot.y;
230 int input = 0; /* Not sure what a reasonable initial value is */
231 #ifdef __PLUGINLIB_ACTIONS_H__
232 const struct button_mapping *plugin_contexts[] = {generic_directions, generic_actions};
233 #endif
235 while (input != RFK_QUIT && exit_rfk == false)
237 process_input(input);
239 /*Redraw robot, where applicable. We're your station, robot.*/
240 if (!(old_x == robot.x && old_y == robot.y))
242 /*Get rid of the old robot*/
243 drawchar(old_x, old_y, ' ');
244 screen[old_x][old_y] = EMPTY;
246 /*Meet the new robot, same as the old robot.*/
247 draw(robot);
248 refresh();
249 screen[robot.x][robot.y] = ROBOT;
251 old_x = robot.x;
252 old_y = robot.y;
254 #ifdef __PLUGINLIB_ACTIONS_H__
255 input = pluginlib_getaction(rb, TIMEOUT_BLOCK, plugin_contexts, 2);
256 #else
257 input = rb->button_get(true);
258 #endif
260 message("Bye!");
261 refresh();
265 *Given the keyboard input, process_input interprets it in terms of moving,
266 *touching objects, etc.
268 static void process_input(int input)
270 int check_x = robot.x;
271 int check_y = robot.y;
273 switch (input)
275 case RFK_UP:
276 case RFK_RUP:
277 check_y--;
278 break;
279 case RFK_DOWN:
280 case RFK_RDOWN:
281 check_y++;
282 break;
283 case RFK_LEFT:
284 case RFK_RLEFT:
285 check_x--;
286 break;
287 case RFK_RIGHT:
288 case RFK_RRIGHT:
289 check_x++;
290 break;
293 /*Check for going off the edge of the screen.*/
294 if (check_y < Y_MIN || check_y > Y_MAX || check_x < X_MIN || check_x > X_MAX)
296 return; /*Do nothing.*/
300 * Clear textline
301 * disabled because it breaks the scrolling for some reason
303 /* rb->lcd_puts_scroll(0, ADV_ROW, " "); */
305 /*Check for collision*/
306 if (screen[check_x][check_y] != EMPTY)
308 switch (screen[check_x][check_y])
310 case ROBOT:
311 /*We didn't move, or we're stuck in a
312 time warp or something.*/
313 break;
314 case KITTEN: /*Found it!*/
315 play_animation(input);
316 /* Wait for the user to click something */
317 pause();
318 break;
319 default: /*We hit a bogus object; print its message.*/
320 message(messages[bogus_messages[screen[check_x][check_y]-2]]);
321 refresh();
322 break;
324 return;
327 /*Otherwise, move the robot.*/
328 robot.x = check_x;
329 robot.y = check_y;
332 /*finish is called upon signal or progam exit*/
333 static void finish(int sig)
335 (void)sig;
336 exit_rfk = true;
339 /******************************************************************************
341 * Begin helper routines
343 *****************************************************************************/
345 static void pause()
347 int button;
348 rb->lcd_update();
350 button = rb->button_get(true);
351 while( ( button == BUTTON_NONE )
352 || ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
355 static int validchar(char a)
357 switch(a)
359 case '#':
360 case ' ':
361 case 127:
362 return 0;
364 return 1;
367 static void play_animation(int input)
369 int counter;
370 screen_object left;
371 screen_object right;
372 /*The grand cinema scene.*/
373 rb->lcd_puts_scroll(0, ADV_ROW, " ");
375 if (input == RFK_RIGHT || input == RFK_DOWN || input == RFK_RRIGHT || input == RFK_RDOWN) {
376 left = robot;
377 right = kitten;
379 else {
380 left = kitten;
381 right = robot;
383 left.y = ADV_ROW;
384 right.y = ADV_ROW;
385 left.x = ANIMATION_MEET - ANIMATION_LENGTH - 1;
386 right.x = ANIMATION_MEET + ANIMATION_LENGTH;
388 for (counter = ANIMATION_LENGTH; counter > 0; counter--)
390 left.x++;
391 right.x--;
392 /* Clear the previous position (empty the first time) */
393 drawchar(left.x - 1, left.y, ' ');
394 drawchar(right.x + 1, right.y, ' ');
395 draw(left);
396 draw(right);
397 refresh();
398 rb->sleep(HZ);
401 message("You found kitten! Way to go, robot!");
402 refresh();
403 finish(0);
406 /******************************************************************************
408 * Begin initialization routines (called before play begins).
410 *****************************************************************************/
412 static void instructions()
414 #define MARGIN 2
415 int y = MARGIN, space_w, width, height;
416 unsigned short x = MARGIN, i = 0;
417 #define WORDS (sizeof instructions / sizeof (char*))
418 static char* instructions[] = {
419 #if 0
420 /* Not sure if we want to include this? */
421 "robotfindskitten", RFK_VERSION, "", "",
422 "By", "the", "illustrious", "Leonard", "Richardson", "(C)", "1997,", "2000", "",
423 "Written", "originally", "for", "the", "Nerth", "Pork", "robotfindskitten", "contest", "", "",
424 #endif
425 "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.", "",
426 "The", "game", "ends", "when", "robotfindskitten.", "", "",
427 "Press", "any", "key", "to", "start",
429 rb->lcd_clear_display();
430 rb->lcd_getstringsize(" ", &space_w, &height);
431 for (i = 0; i < WORDS; i++) {
432 rb->lcd_getstringsize(instructions[i], &width, NULL);
433 /* Skip to next line if the current one can't fit the word */
434 if (x + width > LCD_WIDTH - MARGIN) {
435 x = MARGIN;
436 y += height;
438 /* .. or if the word is the empty string */
439 if (rb->strcmp(instructions[i], "") == 0) {
440 x = MARGIN;
441 y += height;
442 continue;
444 /* We filled the screen */
445 if (y + height > LCD_HEIGHT - MARGIN) {
446 y = MARGIN;
447 pause();
448 rb->lcd_clear_display();
450 rb->lcd_putsxy(x, y, instructions[i]);
451 x += width + space_w;
453 pause();
456 static void initialize_arrays()
458 unsigned int counter, counter2;
459 screen_object empty;
461 /*Initialize the empty object.*/
462 empty.x = -1;
463 empty.y = -1;
464 #if LCD_DEPTH > 1
465 empty.color = LCD_BLACK;
466 #else
467 empty.color = 0;
468 #endif
469 empty.bold = FALSE;
470 empty.character = ' ';
472 for (counter = 0; counter <= X_MAX; counter++)
474 for (counter2 = 0; counter2 <= Y_MAX; counter2++)
476 screen[counter][counter2] = EMPTY;
480 /*Initialize the other arrays.*/
481 for (counter = 0; counter < MESSAGES; counter++)
483 used_messages[counter] = false;
485 for (counter = 0; counter < NUM_BOGUS; counter++)
487 bogus_messages[counter] = 0;
488 bogus[counter] = empty;
492 /*initialize_robot initializes robot.*/
493 static void initialize_robot()
495 /*Assign a position to the player.*/
496 robot.x = randx();
497 robot.y = randy();
499 robot.character = '#';
500 robot.color = ROBOT_COLOR;
501 robot.bold = FALSE;
502 screen[robot.x][robot.y] = ROBOT;
505 /*initialize kitten, well, initializes kitten.*/
506 static void initialize_kitten()
508 /*Assign the kitten a unique position.*/
511 kitten.x = randx();
512 kitten.y = randy();
513 } while (screen[kitten.x][kitten.y] != EMPTY);
515 /*Assign the kitten a character and a color.*/
516 do {
517 kitten.character = randchar();
518 } while (!(validchar(kitten.character)));
519 screen[kitten.x][kitten.y] = KITTEN;
521 kitten.color = colors[randcolor()];
522 kitten.bold = randbold();
525 /*initialize_bogus initializes all non-kitten objects to be used in this run.*/
526 static void initialize_bogus()
528 int counter, index;
529 for (counter = 0; counter < NUM_BOGUS; counter++)
531 /*Give it a color.*/
532 bogus[counter].color = colors[randcolor()];
533 bogus[counter].bold = randbold();
535 /*Give it a character.*/
536 do {
537 bogus[counter].character = randchar();
538 } while (!(validchar(bogus[counter].character)));
540 /*Give it a position.*/
543 bogus[counter].x = randx();
544 bogus[counter].y = randy();
545 } while (screen[bogus[counter].x][bogus[counter].y] != EMPTY);
547 screen[bogus[counter].x][bogus[counter].y] = counter+2;
549 /*Find a message for this object.*/
550 do {
551 index = rb->rand() % MESSAGES;
552 } while (used_messages[index] != false);
553 bogus_messages[counter] = index;
554 used_messages[index] = true;
559 /*initialize_screen paints the screen.*/
560 static void initialize_screen()
562 int counter;
563 char buf[40];
566 *Print the status portion of the screen.
568 rb->lcd_clear_display();
569 rb->lcd_setfont(FONT_SYSFIXED);
570 rb->snprintf(buf, sizeof(buf), "robotfindskitten %s", RFK_VERSION);
571 rb->lcd_puts_scroll(0, 0, buf);
572 refresh();
574 /*Draw a line across the screen.*/
575 for (counter = X_MIN; counter <= X_MAX + 1; counter++)
577 drawchar(counter, ADV_ROW+1, '_');
581 *Draw all the objects on the playing field.
583 for (counter = 0; counter < NUM_BOGUS; counter++)
585 draw(bogus[counter]);
588 draw(kitten);
589 draw(robot);
591 refresh();
595 /* this is the plugin entry point */
596 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
598 (void)parameter;
599 rb = api;
601 exit_rfk = false;
603 rb->srand(*rb->current_tick);
605 initialize_arrays();
608 * Now we initialize the various game objects.
610 initialize_robot();
611 initialize_kitten();
612 initialize_bogus();
615 * Set up white-on-black screen on color targets
617 #if LCD_DEPTH >= 16
618 rb->lcd_set_backdrop(NULL);
619 rb->lcd_set_foreground(LCD_WHITE);
620 rb->lcd_set_background(LCD_BLACK);
621 #endif
624 * Run the game
626 instructions();
628 initialize_screen();
630 play_game();
632 rb->lcd_setfont(FONT_UI);
633 return PLUGIN_OK;