From c12e5fc86afc6595e8fcd2685de643b46c999837 Mon Sep 17 00:00:00 2001 From: Johannes Schwarz Date: Sat, 24 Oct 2009 18:54:48 +0000 Subject: [PATCH] =?UTF-8?q?FS#10497=20-=20New=20game=20codebuster,=20which?= =?UTF-8?q?=20is=20a=20clone=20of=20the=20classic=20game=20mastermind.=20I?= =?UTF-8?q?t=20just=20runs=20on=20color=20LCD.=20Thank=20you=20to=20the=20?= =?UTF-8?q?author=20Cl=C3=A9ment=20Pit--Claudel=20(CFP)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23332 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/CATEGORIES | 1 + apps/plugins/SOURCES | 1 + apps/plugins/codebuster.c | 514 ++++++++++++++++++ manual/plugins/brickmania.tex | 1 + manual/plugins/codebuster.tex | 34 ++ .../images/ss-codebuster-132x80x16.png | Bin 0 -> 556 bytes .../images/ss-codebuster-160x128x16.png | Bin 0 -> 611 bytes .../images/ss-codebuster-176x132x16.png | Bin 0 -> 643 bytes .../images/ss-codebuster-176x220x16.png | Bin 0 -> 788 bytes .../images/ss-codebuster-220x176x16.png | Bin 0 -> 735 bytes .../images/ss-codebuster-240x320x16.png | Bin 0 -> 939 bytes .../images/ss-codebuster-320x240x16.png | Bin 0 -> 1017 bytes manual/plugins/main.tex | 2 + 13 files changed, 553 insertions(+) create mode 100644 apps/plugins/codebuster.c create mode 100644 manual/plugins/codebuster.tex create mode 100644 manual/plugins/images/ss-codebuster-132x80x16.png create mode 100644 manual/plugins/images/ss-codebuster-160x128x16.png create mode 100644 manual/plugins/images/ss-codebuster-176x132x16.png create mode 100644 manual/plugins/images/ss-codebuster-176x220x16.png create mode 100644 manual/plugins/images/ss-codebuster-220x176x16.png create mode 100644 manual/plugins/images/ss-codebuster-240x320x16.png create mode 100644 manual/plugins/images/ss-codebuster-320x240x16.png diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES index 6809844efb..505f83bff1 100644 --- a/apps/plugins/CATEGORIES +++ b/apps/plugins/CATEGORIES @@ -14,6 +14,7 @@ chip8,viewers chopper,games clix,games clock,apps +codebuster,games credits,viewers cube,demos demystify,demos diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index d89b0b6e5f..82dd4b78e4 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES @@ -67,6 +67,7 @@ robotfindskitten.c #ifdef HAVE_LCD_COLOR clix.c ppmviewer.c +codebuster.c #endif /* Plugins needing the grayscale lib on low-depth LCDs */ diff --git a/apps/plugins/codebuster.c b/apps/plugins/codebuster.c new file mode 100644 index 0000000000..edcb7e7904 --- /dev/null +++ b/apps/plugins/codebuster.c @@ -0,0 +1,514 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id: +* +* Copyright (C) 2009 Clément Pit--Claudel +* +* 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/configfile.h" +#include "lib/playback_control.h" +#include "lib/pluginlib_actions.h" + +PLUGIN_HEADER + +/* Limits */ +#define MAX_PIECES_COUNT 5 +#define MAX_COLORS_COUNT 8 +#define MAX_GUESSES_COUNT 10 + +const struct button_mapping *plugin_contexts[] = + {generic_directions, generic_actions}; + +/* Mapping */ +#define EXIT PLA_QUIT +#define VALIDATE PLA_FIRE +#define PREV_PIECE PLA_LEFT +#define PREV_PIECE_REPEAT PLA_LEFT_REPEAT +#define NEXT_PIECE PLA_RIGHT +#define NEXT_PIECE_REPEAT PLA_RIGHT_REPEAT +#define PREV_COLOR PLA_UP +#define PREV_COLOR_REPEAT PLA_UP_REPEAT +#define NEXT_COLOR PLA_DOWN +#define NEXT_COLOR_REPEAT PLA_DOWN_REPEAT + +/* + * Screen structure: + * * (guesses_count) lines of guesses, + * * 1 center line of solution (hidden), + * * 1 line showing available colors. + * + * Status vars: + * * quit: exit the plugin + * * leave: restart the plugin (leave the current game) + * * game_ended: the game has ended + * * found: the combination has been found + * + * Colors used are taken from the Tango project. + * + * Due to integer truncations, 2 vars are used for some objects' dimensions + * (eg. true_guess_w, true_score_w). The actual dimension of these objects is + * stored in the corresponding var. without the "true" prefix. + */ + +struct mm_score { + int correct; + int misplaced; +}; + +struct mm_line { + struct mm_score score; + int pieces[MAX_PIECES_COUNT]; +}; + +const int colors[MAX_COLORS_COUNT] = { + LCD_RGBPACK(252, 233, 79), + LCD_RGBPACK(206, 92, 0), + LCD_RGBPACK(143, 89, 2), + LCD_RGBPACK( 78, 154, 6), + /* LCD_RGBPACK( 32, 74, 135), */ + LCD_RGBPACK( 52, 101, 164), + /* LCD_RGBPACK(114, 159, 207), */ + LCD_RGBPACK(117, 80, 123), + /* LCD_RGBPACK(173, 127, 168), */ + LCD_RGBPACK(164, 0, 0), + LCD_RGBPACK(238, 238, 236), + }; + +/* Flags */ +static bool quit, leave, usb; +static bool found, game_ended; + +/* Settings */ +static int pieces_count; +static int colors_count; +static int guesses_count; +static int pieces_tmp = 5; +static int colors_tmp = 7; +static int guesses_tmp = 10; +static bool labeling = false, framing = false; + +/* Display */ +#define ALUMINIUM LCD_RGBPACK(136, 138, 133) + +#define MARGIN 5 +#define X_MARGIN (LCD_WIDTH / 20) +#define Y_MARGIN (LCD_HEIGHT / 20) +#define GAME_H (LCD_HEIGHT - (2 * Y_MARGIN)) +#define LINE_W (LCD_WIDTH - (2 * X_MARGIN)) + +#define CONFIG_FILE_NAME "codebuster.cfg" + +static struct configdata config[] = { + {TYPE_INT, 0, MAX_PIECES_COUNT, { .int_p = &pieces_tmp }, "pieces", NULL}, + {TYPE_INT, 0, MAX_COLORS_COUNT, { .int_p = &colors_tmp }, "colors", NULL}, + {TYPE_INT, 0, MAX_GUESSES_COUNT, { .int_p = &guesses_tmp }, "guesses", NULL}, + {TYPE_BOOL, 0, 1, { .bool_p = &labeling }, "labeling", NULL}, + {TYPE_BOOL, 0, 1, { .bool_p = &framing }, "framing", NULL}, +}; +static bool settings_changed = false; + +static int line_h; +static int piece_w, tick_w; +static int true_guess_w, true_score_w, guess_w, score_w; + +/* Guesses and solution */ +struct mm_line solution, hidden; +struct mm_line guesses[MAX_GUESSES_COUNT]; + +/* Alias for pluginlib_getaction */ +static inline int get_button(void) { + return pluginlib_getaction(TIMEOUT_BLOCK, plugin_contexts, 2); +} + +/* Computes the margin to center an element */ +static inline int get_margin(int width, int full_w) { + return ((full_w - width) / 2); +} + +static inline bool stop_game(void) { + return (quit || leave || found); +} + +static void fill_color_rect(int x, int y, int w, int h, int color) { + rb->lcd_set_foreground(color); + rb->lcd_fillrect(x, y, w, h); + rb->lcd_set_foreground(LCD_WHITE); +} + +static void overfill_rect(int x, int y, int w, int h) { + rb->lcd_fillrect(x - 2, y - 2, w + 4, h + 4); +} + +static void draw_piece(int x, int y, int w, int h, int color_id, bool emph) { + int color = LCD_BLACK; + + if (color_id >= 0) + color = colors[color_id]; + else if (color_id == -2) /* Hidden piece */ + color = ALUMINIUM; + + if (emph) + overfill_rect(x, y, w, h); + + if (color_id == -1) /* Uninitialised color */ + rb->lcd_drawrect(x, y, w, h); + else + fill_color_rect(x, y, w, h, color); + + if (!emph && framing) + rb->lcd_drawrect(x, y, w, h); + + if (labeling && color_id >= 0) { + char text[2]; + rb->snprintf(text, 2, "%d", color_id); + + int fw, fh; rb->font_getstringsize(text, &fw, &fh, FONT_SYSFIXED); + rb->lcd_putsxy(x + get_margin(fw, w), y + get_margin(fh, h), text); + } +} + +/* Compute the score for a given guess (expressed in ticks) */ +static void validate_guess(struct mm_line* guess) { + bool solution_match[pieces_count]; + bool guess_match[pieces_count]; + + guess->score.misplaced = 0; + guess->score.correct = 0; + + int guess_pos; + + /* Initialisation with 0s */ + for (guess_pos = 0; guess_pos < pieces_count; guess_pos++) + solution_match[guess_pos] = guess_match[guess_pos] = false; + + /* 1st step : detect correctly positioned pieces */ + for (guess_pos = 0; guess_pos < pieces_count; guess_pos++) { + if (solution.pieces[guess_pos] == guess->pieces[guess_pos]) { + guess->score.correct += 1; + + guess_match[guess_pos] = solution_match[guess_pos] + = true; + } + } + + /* Second step : detect mispositioned pieces */ + for (guess_pos = 0; guess_pos < pieces_count; guess_pos++) { + if (guess_match[guess_pos]) continue; + + int sol_pos; + for (sol_pos = 0; sol_pos < pieces_count; sol_pos++) { + if (guess_match[guess_pos]) break; + if (solution_match[sol_pos]) continue; + + if (guess->pieces[guess_pos] == solution.pieces[sol_pos]) { + guess->score.misplaced += 1; + + solution_match[sol_pos] = true; + break; + } + } + } +} + +static void draw_guess(int line, struct mm_line* guess, int cur_guess, + int cur_piece, bool show_score) { + int cur_y = (Y_MARGIN + MARGIN) + 2 * line_h * line; + int l_margin = X_MARGIN + (show_score ? 0 : get_margin(guess_w, LINE_W)); + + int piece; + for (piece = 0; piece < pieces_count; piece++) { + int cur_x = l_margin + 2 * piece_w * piece; + draw_piece(cur_x, cur_y, piece_w, line_h, guess->pieces[piece], + line == cur_guess && piece == cur_piece); + } +} + +static void draw_score(int line, struct mm_line* guess) { + int cur_y = Y_MARGIN + 2 * line_h * line; + int l_margin = X_MARGIN + true_guess_w + MARGIN; + + int tick = 0; + for (; tick < guess->score.correct; tick++) { + int cur_x = l_margin + 2 * tick_w * tick; + + fill_color_rect(cur_x, cur_y, tick_w, line_h, LCD_RGBPACK(239, 41, 41)); + } + + for (; tick < guess->score.correct + guess->score.misplaced; tick++) { + int cur_x = l_margin + 2 * tick_w * tick; + + fill_color_rect(cur_x, cur_y, tick_w, line_h, LCD_RGBPACK(211, 215, 207)); + } +} + +static void draw_board(int cur_guess, int cur_piece) { + rb->lcd_clear_display(); + + int line = 0; + for (; line < guesses_count; line++) { + draw_guess(line, &guesses[line], cur_guess, cur_piece, true); + if (line < cur_guess) draw_score(line, &guesses[line]); + } + + int color; + int colors_margin = 2; + int cur_y = (Y_MARGIN + MARGIN) + 2 * line_h * line; + int color_w = (LINE_W - colors_margin * (colors_count - 1)) / colors_count; + + for (color = 0; color < colors_count; color++) { + int cur_x = X_MARGIN + color * (color_w + colors_margin); + draw_piece(cur_x, cur_y, color_w, line_h, color, + color == guesses[cur_guess].pieces[cur_piece]); + } + + line++; + + if(game_ended) + draw_guess(line, &solution, cur_guess, cur_piece, false); + else + draw_guess(line, &hidden, cur_guess, cur_piece, false); + + rb->lcd_update(); +} + +static void init_vars(void) { + quit = leave = usb = found = game_ended = false; + + int guess, piece; + for (guess = 0; guess < guesses_count; guess++) { + for (piece = 0; piece < pieces_count; piece++) + guesses[guess].pieces[piece] = -1; + } + for (piece = 0; piece < pieces_count; piece++) { + guesses[0].pieces[piece] = 0; + hidden.pieces[piece] = -2; + } +} + +static void init_board(void) { + + pieces_count = pieces_tmp; + colors_count = colors_tmp; + guesses_count = guesses_tmp; + + line_h = GAME_H / (2 * (guesses_count + 2) - 1); + + true_score_w = LINE_W * 0.25; + true_guess_w = LINE_W - (true_score_w + MARGIN); + + tick_w = true_score_w / (2 * pieces_count - 1); + piece_w = true_guess_w / (2 * pieces_count - 1); + + /* Readjust (due to integer divisions) */ + score_w = tick_w * (2 * pieces_count - 1); + guess_w = piece_w * (2 * pieces_count - 1); +} + +static void randomize_solution(void) { + int piece_id; + for (piece_id = 0; piece_id < pieces_count; piece_id++) + solution.pieces[piece_id] = rb->rand() % colors_count; +} + +static void settings_menu(void) { + MENUITEM_STRINGLIST(settings_menu, "Settings", NULL, + "Number of colors", "Number of pieces", + "Number of guesses", "Labels ?", "Frames ?"); + + int cur_item =0; + + bool menu_quit = false; + while(!menu_quit) { + + switch(rb->do_menu(&settings_menu, &cur_item, NULL, false)) { + case 0: + rb->set_int("Number of colors", "", UNIT_INT, &colors_tmp, + NULL, -1, MAX_COLORS_COUNT, 1, NULL); + break; + case 1: + rb->set_int("Number of pieces", "", UNIT_INT, &pieces_tmp, + NULL, -1, MAX_PIECES_COUNT, 1, NULL); + break; + case 2: + rb->set_int("Number of guesses", "", UNIT_INT, &guesses_tmp, + NULL, -1, MAX_GUESSES_COUNT, 1, NULL); + break; + case 3: + rb->set_bool("Display labels ?", &labeling); + break; + case 4: + rb->set_bool("Display frames ?", &framing); + break; + case GO_TO_PREVIOUS: + menu_quit = true; + break; + default: + break; + } + } +} + +static bool resume; +static int menu_cb(int action, const struct menu_item_ex *this_item) +{ + int i = ((intptr_t)this_item); + if ((action == ACTION_REQUEST_MENUITEM) && (!resume && (i==0))) + return ACTION_EXIT_MENUITEM; + return action; +} + +static void main_menu(void) { + MENUITEM_STRINGLIST(main_menu, "Codebuster Menu", menu_cb, + "Resume Game", "Start New Game", "Settings", + "Playback Control", "Quit"); + + int cur_item =0; + + bool menu_quit = false; + while(!menu_quit) { + + switch(rb->do_menu(&main_menu, &cur_item, NULL, false)) { + case 0: + resume = true; + menu_quit = true; + break; + case 1: + leave = true; + menu_quit = true; + break; + case 2: + settings_menu(); + settings_changed = true; + break; + case 3: + playback_control(NULL); + break; + case 4: + quit = menu_quit = true; + break; + case MENU_ATTACHED_USB: + usb = menu_quit = true; + break; + default: + break; + } + } +} + +enum plugin_status plugin_start(const void* parameter) { + (void)parameter; + + rb->srand(*rb->current_tick); + rb->lcd_setfont(FONT_SYSFIXED); + rb->lcd_set_backdrop(NULL); + rb->lcd_set_foreground(LCD_WHITE); + rb->lcd_set_background(LCD_BLACK); + + configfile_load(CONFIG_FILE_NAME,config,5,0); + + main_menu(); + while (!quit) { + init_board(); + randomize_solution(); + init_vars(); + + draw_board(0, 0); + int button = 0, guess = 0, piece = 0; + for (guess = 0; guess < guesses_count && !stop_game(); guess++) { + while(!stop_game()) { + draw_board(guess, piece); + + if ((button = get_button()) == VALIDATE) break; + + switch (button) { + + case EXIT: + resume = true; + main_menu(); + break; + + case NEXT_PIECE: + case NEXT_PIECE_REPEAT: + piece = (piece + 1) % pieces_count; + break; + + case PREV_PIECE: + case PREV_PIECE_REPEAT: + piece = (piece + pieces_count - 1) % pieces_count; + break; + + + case NEXT_COLOR: + case NEXT_COLOR_REPEAT: + guesses[guess].pieces[piece] = + (guesses[guess].pieces[piece] + 1) + % colors_count; + break; + + case PREV_COLOR: + case PREV_COLOR_REPEAT: + guesses[guess].pieces[piece] = + (guesses[guess].pieces[piece] + colors_count - 1) + % colors_count; + break; + + default: + if (rb->default_event_handler(button) == SYS_USB_CONNECTED) + quit = usb = true; + } + + if (guesses[guess].pieces[piece] == -1) + guesses[guess].pieces[piece] = 0; + } + + if (!quit) { + validate_guess(&guesses[guess]); + + if (guesses[guess].score.correct == pieces_count) + found = true; + + if (guess + 1 < guesses_count && !found) + guesses[guess + 1] = guesses[guess]; + } + } + + game_ended = true; + resume = false; + if (!quit && !leave) { + draw_board(guess, piece); + + if (found) + rb->splash(HZ, "Well done :)"); + else + rb->splash(HZ, "Wooops :("); + do { + button = rb->button_get(true); + if (rb->default_event_handler(button) == SYS_USB_CONNECTED) { + quit = usb = true; + } + } while( ( button == BUTTON_NONE ) + || ( button & (BUTTON_REL|BUTTON_REPEAT) ) ); + main_menu(); + } + } + if (settings_changed) + configfile_save(CONFIG_FILE_NAME,config,5,0); + + rb->lcd_setfont(FONT_UI); + return (usb) ? PLUGIN_USB_CONNECTED : PLUGIN_OK; +} diff --git a/manual/plugins/brickmania.tex b/manual/plugins/brickmania.tex index f16e8e765c..5be236f7a3 100644 --- a/manual/plugins/brickmania.tex +++ b/manual/plugins/brickmania.tex @@ -1,3 +1,4 @@ +% $Id: \subsection{BrickMania} \screenshot{plugins/images/ss-brickmania}{BrickMania}% {img:brickmania} diff --git a/manual/plugins/codebuster.tex b/manual/plugins/codebuster.tex new file mode 100644 index 0000000000..791e29f8a5 --- /dev/null +++ b/manual/plugins/codebuster.tex @@ -0,0 +1,34 @@ +% $Id: +\subsection{Codebuster} +\screenshot{plugins/images/ss-codebuster}{Codebuster}{img:codebuster} + +Codebuster is a clone of the classic mastermind game. Your aim is to guess the +computer's combination in a minimal number of attempts. At every turn, you input +a combination, which is then validated by the computer, which displays the number +of well-placed and misplaced pegs (a red square for right ones, and a white square +for misplaced ones). + +\begin{table} + \begin{btnmap}{}{} + \opt{IAUDIO_X5_PAD,SANSA_E200_PAD,SANSA_C200_PAD,GIGABEAT_PAD}{\ButtonPower} + \opt{SANSA_FUZE_PAD}{Long \ButtonHome} + \opt{IPOD_4G_PAD}{\ButtonMenu} + \opt{IRIVER_H300_PAD}{\ButtonOff} + \opt{GIGABEAT_S_PAD}{\ButtonBack} + \opt{HAVEREMOTEKEYMAP}{& } + & Show menu \\ + \opt{IRIVER_H300_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD,SANSA_E200_PAD,SANSA_C200_PAD,SANSA_FUZE_PAD,IPOD_4G_PAD} + {\ButtonSelect} + \opt{IAUDIO_X5_PAD}{\ButtonRec} + \opt{HAVEREMOTEKEYMAP}{& } + & Validate suggestion and move to next line \\ + \ButtonLeft\ / \ButtonRight + \opt{HAVEREMOTEKEYMAP}{& } + & Select a peg \\ + \opt{IRIVER_H300_PAD,IAUDIO_X5_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD,SANSA_C200_PAD} + {\ButtonUp\ / \ButtonDown} + \opt{SANSA_E200_PAD,SANSA_FUZE_PAD,IPOD_4G_PAD}{\ButtonScrollFwd\ / \ButtonScrollBack} + \opt{HAVEREMOTEKEYMAP}{& } + & Change current peg \\ + \end{btnmap} +\end{table} diff --git a/manual/plugins/images/ss-codebuster-132x80x16.png b/manual/plugins/images/ss-codebuster-132x80x16.png new file mode 100644 index 0000000000000000000000000000000000000000..c74f2673a2287f2fd165555a6522eb9c60715f52 GIT binary patch literal 556 zcmeAS@N?(olHy`uVBq!ia0vp^EkGQ=!2~43JH1W{XE)7O>#CYJ=isQP~CDOZ4|tnqYl45_&F z_Kt1Pp#Tww#DW_QtknY6k!)ep_>w!NCv3bJB+YKVVCmtAsFOw;guf>|6iS+CbVvON z1LMBn)Mrm8KKXm6`jAiFVn&7oNhZ9Gv*s`Mt1I|ndYa!Z{qmm&J1(#N7RBDS+inIA z!|mw@j(@!QcgM>Vfp5Pn_y0b5E03mZSjko778=>&yHV4c`GEbQ zD^~wgwbFy1)~vrHyH1#MkJ}8M1lO$DsW+X%vo2*MW>sf|rUoxR|L{<~d`ix#a{|j0 z8gEM;_}8-1<%Va=tk}0(6Bo_)d)@|QJpA5s>HGH8dl?vVx`3ve9bLxI0HJS)Fl+$v zZ-~rhVt~*|!3+mL{G{L zXl7>>t6--RkL#yRJe^tmuAhGKcYfk_{nW(sICb8l{B(}s2?taQ9A004jp0ssI20|E6+00001b5ch_0Itp) z=>Px%9Z5t%RCwC$+A%U@Q4|K?W6A~u_Ao-0(U}k>WCOY+gfoe18z58&WxWi-nLP+B z;mIUZ)A#kg`gwhPU!-{NbhqE_y7$)+006*pHv9@!=k4dt$Jbkb{}jK!3O~ekz zMY|yzD=XMrGy+DztjQ*8a#Of-ep0`FapLg3CAP= xu*v!qv#Vc=FPNTRn$Ty0V-f-uFaic(NbkOu(b~ka?SlXS002ovPDHLkV1ngd2ax~( literal 0 HcmV?d00001 diff --git a/manual/plugins/images/ss-codebuster-176x132x16.png b/manual/plugins/images/ss-codebuster-176x132x16.png new file mode 100644 index 0000000000000000000000000000000000000000..bee6ff8db80d5d8daaa17600fa21788879e28aa2 GIT binary patch literal 643 zcmV-}0(||6P)Px%JxN4CRCwC$+C5GiK^O+$kBAFIB6kSs@G(q9i7ZOE09G1Oh9)u>fD&1hRQMRC z3%Ns(as;Qdl43PRJM8SvK2K%A@X9}*c4uB30RRAy)1J4WtUsR*w~s&mdf7hy`Lf?&Q0awH2uf(hxD51ZFBE4q0!;6_kcR5Gr>7?honJWZt z85jr*1moUR(v7C3(iH-?3=9MYf+{wZxth53S^7y#?$c{Fzo+RJ^J$e_b9r)=Tyyhy zom`Wv$#sRmKwuy+5LCUXq#N)39Z6RR3wc&!`Mwu-zcUq5H!qNk^o?Q)13@!QW%(QcsHBI; zcx$ZcAnufbQ^r4Dk6Q&FJVnI%tX0P3J|bdq+A3uJ5D_uGZ56V*h=@2kU$zN(zMDnF z<;hi>kekQrh*-A!)Mu^`xMg4h2OvkQhUaqY9Yqhzrpt;8 zPe|R~Z2kDFoqub>k0a+-N4|ff-2pV5fuUio(Ff%fuiqbh`1|(!`u%G6?{D+3Q{`Y` zYINApxHnaPhox`*rzeJcpS`G=74x~s-f!Jbo1I?kcGm9lTPJyU>HcRoU8~MMI+k6^e2w00wL4M*j%_k-o0QI91~x{4p~2~kqSo)PR`vD2Uq3YXS*Nd6IK9z9K_F-G zwci4lMfY`lR2ahRDMeUas`0qFle~QFEXm-xRVOv z?oN1lctL9$}S??szG@U2LNW%w21KrNRPz6-Jsr0u%?sxS)D_lT&ntZq1 zYo9TH>2)6YOWCu2?U(*%*CNZ*xZoqut~IyYGj`j9tOnb~(D2JDU}3b5*y~zv=Di2b zTDCuEKKDiO(OZkXY5y&Ey2u~m1GyjKJ%NzNON6XhfML0mr|8|k@STs%)7@3}KNLOR zedeVYdoWa0%5CirRySopVg7G1e_*9w`U}G;GdJhjoQ9gDFx$@MuO&!+?-H8ITjyMO zb@z&&?Yr0P?7=k~{v^D5YmxooU$M)X^PAFm6U)rof{cYs7ZUHx3vIVCg!013@DPXGV_ literal 0 HcmV?d00001 diff --git a/manual/plugins/images/ss-codebuster-220x176x16.png b/manual/plugins/images/ss-codebuster-220x176x16.png new file mode 100644 index 0000000000000000000000000000000000000000..c35ac9972f326ebfa3a11ef071d0d8742548ee03 GIT binary patch literal 735 zcmeAS@N?(olHy`uVBq!ia0vp^cYt^U2NRHd%{To!kYX$ja(7}_cTVOd0|V1cPZ!6K zid%2**!oEaim+XH-NCygfnPB;P4b<_!9_=;4|qABy5N$=?AO^a>D=i8s}ph>n^=}F z-Bg)z>~r0@mpb#-y?J=pINxqI2he1OhC*MNrd@x37k&En@cDlI_y1o;*!|o(QF@_^ zhNFh&xr_HiZ(q;6b?s%v+%0vL)4uhH_h-I-FWi4Zr|@sd)NOUP{?Y&68od?$^V4>< z{S=Yh?`>9--)_5XzE0CoL(r??*2{g`+jrlstGl_U_Q%&H#lP6V=B_;WZ`Q|GU#v18 zy}j&v>DR*DUoY)`dDivT=a=uKI8>$~MoKmAGD4l`Eb z+P_(aFW#r}-<9RRTgzWtQ*!^xvJQ~7PMl7i0lG{C98G9$wgU zzqb5y`;xD-uU1~Fy1TvHOTeo|s6}XD%f_Alo#kG@fcRK`e`WQPE43ScK0c=(bjk7O zfmpfE$LG#h+}c(h-;^yat+U<7xJ$60b^*r5UadSXZK#(AXwtk+f7wV7CP06lp0 zV(n~xpp=RG_q)ITD81}^J#W5ZFCWk=o`og%pD_ca&R)b}RlwX%=K_PnZ^N~+3-1Ui zS^v)G%#B}O`_ET?y>e3V#pl^C-YF?fORqf_W8>!H6aH8JW8S|DkGGtCWuvstZ+-Qg z%HJizg5US_Z;ST?obA^DZ@VDKJr|ai?z81eRxv{xij6r zH1Aa^XLc?SG~tj^GwA-g=wao=Kl9K2O?{=h`tpyW=4{&Gxi6)1Ud_p)7eimE z+Sk<Md#9lN*m@a-3;`3c#b-B8Z{9j%uJpH} z2C(9&;0tkmzrVcyr}B5wG_y3H&B;e(rC4~I*c_FCT71sO@m)*uOTBTL5h&8UfDvTI zi8DaKQ}=fY9%k1zalOC+R4%NMp?};YT8;1gr0?}M_tf71&&ZtIX$^Eg&?q;cn`X@0 zE3@WmMbTkdsr_J^p`kG0oYn>{-oqQN?%sF(&F^ zZ-GsMIEq8csQA`8FTdM6otYuVTv)N<{1^8*G7^PR2c{S;H_`aoDqUW349L84^q~sa zH_hPC1Txz^HlJ8O`bV!>fda7QRx*sryb;A0ifHV;n|U*->wN$ zIiYp0%(5@X^}6Q)&8`H7H8@;AF$FgJThR=-p^*b>=+w{V2i}}vnSXfy&+ylL>KCW1 zIP7`&v(oAP+y16!ABl1uK5(yP^4*~ zVX)wUgSLBaec~=Ii&trK8%}&uN|<=G_<)l6^X+%Vch>KGz3;@$ue*LLt>@qTu6vDg z++1VUQdTxrHW3Ml@3sa{7QAKayd(FXi$?=&7RbI0{hcx*FTYQpyx0E!pVzuiJ}&*B zX#%pSf$8wVj9T+c-?SdAIGcPx>E{`?lj@#!#Ri{dPJaY4VZng|$L}*c^WDljpiz6c zO&e@v0~0gQ&;kjcCks5(ivBze&Im|wXk=t1R#6kSElB1_gGtB9EX-p(g*Hm%@{H zW-iIgOJhLRGUJ4z=U=$;1^Gi&`GXBR?oYCH`_FZK?t%X&{d6=UUeAr#{rx3JeSg2X z=F+!UIs5}Y&j|85-7Uzsx57_z>C3Dw;_k-vQvVj*zMAUw^4XuVAI|sp-&s24TC&Ym zlEjNjpfy)78&qol`;+0O-qsQ2+n{ literal 0 HcmV?d00001 diff --git a/manual/plugins/main.tex b/manual/plugins/main.tex index cad7436b63..292327046f 100644 --- a/manual/plugins/main.tex +++ b/manual/plugins/main.tex @@ -31,6 +31,8 @@ text files% \opt{lcd_bitmap}{\input{plugins/chopper.tex}} +\opt{lcd_color}{\input{plugins/codebuster.tex}} + {\input{plugins/dice.tex}} \opt{swcodec}{\input{plugins/doom.tex}}