A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 2651 lines 78 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2002 Philipp Pertermann 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation; either version 2 15 * of the License, or (at your option) any later version. 16 * 17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 * KIND, either express or implied. 19 * 20 ****************************************************************************/ 21#include "plugin.h" 22#include "lib/configfile.h" 23#include "lib/helper.h" 24#include "lib/playback_control.h" 25 26#ifdef DEBUG_WORMLET 27static long max_cycle; 28#endif 29 30/* size of the field the worm lives in */ 31#define FIELD_RECT_X 1 32#define FIELD_RECT_Y 1 33#define FIELD_RECT_WIDTH (LCD_WIDTH - 45) 34#define FIELD_RECT_HEIGHT (LCD_HEIGHT - 2) 35 36/* when the game starts */ 37#define INITIAL_WORM_LENGTH 10 38 39/* num of pixel the worm grows per eaten food */ 40#define WORM_PER_FOOD 7 41 42/* num of worms creeping in the FIELD */ 43#define MAX_WORMS 3 44 45/* minimal distance between a worm and an argh 46 when a new argh is made */ 47#define MIN_ARGH_DIST 5 48 49#if (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \ 50 (CONFIG_KEYPAD == IPOD_1G2G_PAD) 51 52#define BTN_DIR_UP BUTTON_MENU 53#define BTN_DIR_DOWN BUTTON_PLAY 54#define BTN_DIR_LEFT BUTTON_LEFT 55#define BTN_DIR_RIGHT BUTTON_RIGHT 56#define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL) 57#define BTN_QUIT (BUTTON_SELECT|BUTTON_REPEAT) 58#define BTN_STOPRESET (BUTTON_SELECT|BUTTON_PLAY) 59 60#elif (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD) 61 62#define BTN_DIR_UP BUTTON_UP 63#define BTN_DIR_DOWN BUTTON_DOWN 64#define BTN_DIR_LEFT BUTTON_LEFT 65#define BTN_DIR_RIGHT BUTTON_RIGHT 66#define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL) 67#define BTN_QUIT BUTTON_OFF 68#define BTN_STOPRESET BUTTON_ON 69 70#define BTN_RC_QUIT BUTTON_RC_STOP 71 72#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD) 73 74#define BTN_DIR_UP BUTTON_UP 75#define BTN_DIR_DOWN BUTTON_DOWN 76#define BTN_DIR_LEFT BUTTON_LEFT 77#define BTN_DIR_RIGHT BUTTON_RIGHT 78#define BTN_STARTPAUSE BUTTON_PLAY 79#define BTN_QUIT BUTTON_POWER 80#define BTN_STOPRESET BUTTON_REC 81 82#elif (CONFIG_KEYPAD == GIGABEAT_PAD) 83 84#define BTN_DIR_UP BUTTON_UP 85#define BTN_DIR_DOWN BUTTON_DOWN 86#define BTN_DIR_LEFT BUTTON_LEFT 87#define BTN_DIR_RIGHT BUTTON_RIGHT 88#define BTN_STARTPAUSE BUTTON_SELECT 89#define BTN_QUIT BUTTON_POWER 90#define BTN_STOPRESET BUTTON_A 91 92#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \ 93(CONFIG_KEYPAD == SANSA_C200_PAD) 94 95#define BTN_DIR_UP BUTTON_UP 96#define BTN_DIR_DOWN BUTTON_DOWN 97#define BTN_DIR_LEFT BUTTON_LEFT 98#define BTN_DIR_RIGHT BUTTON_RIGHT 99#define BTN_STARTPAUSE BUTTON_SELECT 100#define BTN_QUIT BUTTON_POWER 101#define BTN_STOPRESET BUTTON_REC 102 103#elif (CONFIG_KEYPAD == SANSA_CLIP_PAD) 104 105#define BTN_DIR_UP BUTTON_UP 106#define BTN_DIR_DOWN BUTTON_DOWN 107#define BTN_DIR_LEFT BUTTON_LEFT 108#define BTN_DIR_RIGHT BUTTON_RIGHT 109#define BTN_STARTPAUSE BUTTON_SELECT 110#define BTN_QUIT BUTTON_POWER 111#define BTN_STOPRESET BUTTON_HOME 112 113#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD) 114 115#define BTN_DIR_UP BUTTON_UP 116#define BTN_DIR_DOWN BUTTON_DOWN 117#define BTN_DIR_LEFT BUTTON_LEFT 118#define BTN_DIR_RIGHT BUTTON_RIGHT 119#define BTN_STARTPAUSE BUTTON_SELECT 120#define BTN_QUIT (BUTTON_HOME|BUTTON_REPEAT) 121#define BTN_STOPRESET (BUTTON_SELECT | BUTTON_UP) 122 123#elif (CONFIG_KEYPAD == SANSA_M200_PAD) 124 125#define BTN_DIR_UP BUTTON_UP 126#define BTN_DIR_DOWN BUTTON_DOWN 127#define BTN_DIR_LEFT BUTTON_LEFT 128#define BTN_DIR_RIGHT BUTTON_RIGHT 129#define BTN_STARTPAUSE (BUTTON_SELECT | BUTTON_REL) 130#define BTN_QUIT BUTTON_POWER 131#define BTN_STOPRESET (BUTTON_SELECT | BUTTON_UP) 132 133#elif (CONFIG_KEYPAD == IRIVER_H10_PAD) 134 135#define BTN_DIR_UP BUTTON_SCROLL_UP 136#define BTN_DIR_DOWN BUTTON_SCROLL_DOWN 137#define BTN_DIR_LEFT BUTTON_LEFT 138#define BTN_DIR_RIGHT BUTTON_RIGHT 139#define BTN_STARTPAUSE BUTTON_PLAY 140#define BTN_QUIT BUTTON_POWER 141#define BTN_STOPRESET BUTTON_REW 142 143#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \ 144 (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD) 145 146#define BTN_DIR_UP BUTTON_UP 147#define BTN_DIR_DOWN BUTTON_DOWN 148#define BTN_DIR_LEFT BUTTON_LEFT 149#define BTN_DIR_RIGHT BUTTON_RIGHT 150#define BTN_STARTPAUSE BUTTON_SELECT 151#define BTN_QUIT BUTTON_BACK 152#define BTN_STOPRESET BUTTON_MENU 153 154#elif (CONFIG_KEYPAD == MROBE100_PAD) 155 156#define BTN_DIR_UP BUTTON_UP 157#define BTN_DIR_DOWN BUTTON_DOWN 158#define BTN_DIR_LEFT BUTTON_LEFT 159#define BTN_DIR_RIGHT BUTTON_RIGHT 160#define BTN_STARTPAUSE BUTTON_SELECT 161#define BTN_QUIT BUTTON_POWER 162#define BTN_STOPRESET BUTTON_DISPLAY 163 164#elif CONFIG_KEYPAD == IAUDIO_M3_PAD 165 166#define BTN_DIR_UP BUTTON_RC_VOL_UP 167#define BTN_DIR_DOWN BUTTON_RC_VOL_DOWN 168#define BTN_DIR_LEFT BUTTON_RC_REW 169#define BTN_DIR_RIGHT BUTTON_RC_FF 170#define BTN_STARTPAUSE BUTTON_RC_PLAY 171#define BTN_QUIT BUTTON_RC_REC 172#define BTN_STOPRESET BUTTON_RC_MODE 173 174#elif (CONFIG_KEYPAD == COWON_D2_PAD) 175 176#define BTN_QUIT BUTTON_POWER 177 178#elif CONFIG_KEYPAD == CREATIVEZVM_PAD 179 180#define BTN_DIR_UP BUTTON_UP 181#define BTN_DIR_DOWN BUTTON_DOWN 182#define BTN_DIR_LEFT BUTTON_LEFT 183#define BTN_DIR_RIGHT BUTTON_RIGHT 184#define BTN_STARTPAUSE BUTTON_PLAY 185#define BTN_QUIT BUTTON_BACK 186#define BTN_STOPRESET BUTTON_MENU 187 188#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD 189 190#define BTN_DIR_UP BUTTON_UP 191#define BTN_DIR_DOWN BUTTON_DOWN 192#define BTN_DIR_LEFT BUTTON_BACK 193#define BTN_DIR_RIGHT BUTTON_MENU 194#define BTN_STARTPAUSE (BUTTON_PLAY|BUTTON_REL) 195#define BTN_QUIT BUTTON_POWER 196#define BTN_STOPRESET (BUTTON_PLAY|BUTTON_REPEAT) 197 198#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD 199 200#define BTN_DIR_UP BUTTON_UP 201#define BTN_DIR_DOWN BUTTON_DOWN 202#define BTN_DIR_LEFT BUTTON_LEFT 203#define BTN_DIR_RIGHT BUTTON_RIGHT 204#define BTN_STARTPAUSE BUTTON_MENU 205#define BTN_QUIT BUTTON_POWER 206#define BTN_STOPRESET BUTTON_VIEW 207 208#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD 209 210#define BTN_DIR_UP BUTTON_UP 211#define BTN_DIR_DOWN BUTTON_DOWN 212#define BTN_DIR_LEFT BUTTON_PREV 213#define BTN_DIR_RIGHT BUTTON_NEXT 214#define BTN_STARTPAUSE BUTTON_MENU 215#define BTN_QUIT BUTTON_POWER 216#define BTN_STOPRESET BUTTON_RIGHT 217 218#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD 219 220#define BTN_DIR_UP BUTTON_UP 221#define BTN_DIR_DOWN BUTTON_DOWN 222#define BTN_DIR_LEFT BUTTON_PREV 223#define BTN_DIR_RIGHT BUTTON_RIGHT 224#define BTN_STARTPAUSE BUTTON_MENU 225#define BTN_QUIT BUTTON_POWER 226#define BTN_STOPRESET BUTTON_RIGHT 227 228#elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \ 229(CONFIG_KEYPAD == ONDAVX777_PAD) || \ 230CONFIG_KEYPAD == MROBE500_PAD 231 232#define BTN_QUIT BUTTON_POWER 233 234#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \ 235 (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD) 236 237#define BTN_DIR_UP BUTTON_UP 238#define BTN_DIR_DOWN BUTTON_DOWN 239#define BTN_DIR_LEFT BUTTON_LEFT 240#define BTN_DIR_RIGHT BUTTON_RIGHT 241#define BTN_STARTPAUSE BUTTON_PLAY 242#define BTN_QUIT BUTTON_REW 243#define BTN_STOPRESET BUTTON_FFWD 244 245#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD 246 247#define BTN_DIR_UP BUTTON_UP 248#define BTN_DIR_DOWN BUTTON_DOWN 249#define BTN_DIR_LEFT BUTTON_PREV 250#define BTN_DIR_RIGHT BUTTON_NEXT 251#define BTN_STARTPAUSE BUTTON_PLAY 252#define BTN_QUIT BUTTON_REC 253#define BTN_STOPRESET BUTTON_CANCEL 254 255#elif CONFIG_KEYPAD == MPIO_HD200_PAD 256 257#define BTN_DIR_UP BUTTON_REW 258#define BTN_DIR_DOWN BUTTON_FF 259#define BTN_DIR_LEFT BUTTON_VOL_DOWN 260#define BTN_DIR_RIGHT BUTTON_VOL_UP 261#define BTN_STARTPAUSE BUTTON_FUNC 262#define BTN_QUIT (BUTTON_REC|BUTTON_PLAY) 263#define BTN_STOPRESET (BUTTON_FUNC|BUTTON_REPEAT) 264 265#elif CONFIG_KEYPAD == MPIO_HD300_PAD 266 267#define BTN_DIR_UP BUTTON_UP 268#define BTN_DIR_DOWN BUTTON_DOWN 269#define BTN_DIR_LEFT BUTTON_REW 270#define BTN_DIR_RIGHT BUTTON_FF 271#define BTN_STARTPAUSE BUTTON_PLAY 272#define BTN_QUIT (BUTTON_MENU | BUTTON_REPEAT) 273#define BTN_STOPRESET (BUTTON_PLAY | BUTTON_REPEAT) 274 275#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD 276 277#define BTN_DIR_UP BUTTON_UP 278#define BTN_DIR_DOWN BUTTON_DOWN 279#define BTN_DIR_LEFT BUTTON_LEFT 280#define BTN_DIR_RIGHT BUTTON_RIGHT 281#define BTN_STARTPAUSE BUTTON_PLAYPAUSE 282#define BTN_QUIT BUTTON_POWER 283#define BTN_STOPRESET BUTTON_BACK 284 285#elif CONFIG_KEYPAD == SANSA_CONNECT_PAD 286 287#define BTN_DIR_UP BUTTON_UP 288#define BTN_DIR_DOWN BUTTON_DOWN 289#define BTN_DIR_LEFT BUTTON_LEFT 290#define BTN_DIR_RIGHT BUTTON_RIGHT 291#define BTN_STARTPAUSE BUTTON_SELECT 292#define BTN_QUIT BUTTON_POWER 293#define BTN_STOPRESET BUTTON_VOL_DOWN 294 295#elif CONFIG_KEYPAD == HM60X_PAD 296 297#define BTN_DIR_UP BUTTON_UP 298#define BTN_DIR_DOWN BUTTON_DOWN 299#define BTN_DIR_LEFT BUTTON_LEFT 300#define BTN_DIR_RIGHT BUTTON_RIGHT 301#define BTN_STARTPAUSE BUTTON_SELECT 302#define BTN_QUIT BUTTON_POWER 303#define BTN_STOPRESET (BUTTON_POWER|BUTTON_SELECT) 304 305#elif CONFIG_KEYPAD == HM801_PAD 306 307#define BTN_DIR_UP BUTTON_UP 308#define BTN_DIR_DOWN BUTTON_DOWN 309#define BTN_DIR_LEFT BUTTON_LEFT 310#define BTN_DIR_RIGHT BUTTON_RIGHT 311#define BTN_STARTPAUSE BUTTON_SELECT 312#define BTN_QUIT BUTTON_POWER 313#define BTN_STOPRESET BUTTON_PLAY 314 315#elif CONFIG_KEYPAD == SONY_NWZ_PAD 316#define BTN_DIR_UP BUTTON_UP 317#define BTN_DIR_DOWN BUTTON_DOWN 318#define BTN_DIR_LEFT BUTTON_LEFT 319#define BTN_DIR_RIGHT BUTTON_RIGHT 320#define BTN_STARTPAUSE BUTTON_PLAY 321#define BTN_QUIT BUTTON_BACK 322#define BTN_STOPRESET BUTTON_POWER 323 324#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD 325#define BTN_DIR_UP BUTTON_UP 326#define BTN_DIR_DOWN BUTTON_DOWN 327#define BTN_DIR_LEFT BUTTON_LEFT 328#define BTN_DIR_RIGHT BUTTON_RIGHT 329#define BTN_STARTPAUSE BUTTON_PLAYPAUSE 330#define BTN_QUIT BUTTON_BACK 331#define BTN_STOPRESET BUTTON_SHORTCUT 332 333#elif CONFIG_KEYPAD == DX50_PAD 334#define BTN_DIR_UP BUTTON_VOL_UP 335#define BTN_DIR_DOWN BUTTON_VOL_DOWN 336#define BTN_DIR_LEFT BUTTON_LEFT 337#define BTN_DIR_RIGHT BUTTON_RIGHT 338#define BTN_STARTPAUSE BUTTON_PLAY 339#define BTN_QUIT BUTTON_POWER 340#define BTN_STOPRESET (BUTTON_PLAY|BUTTON_REPEAT) 341 342#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD 343#define BTN_QUIT BUTTON_POWER 344#define BTN_STARTPAUSE BUTTON_MENU 345#define BTN_STOPRESET (BUTTON_MENU|BUTTON_REPEAT) 346 347#elif CONFIG_KEYPAD == AGPTEK_ROCKER_PAD 348#define BTN_DIR_UP BUTTON_UP 349#define BTN_DIR_DOWN BUTTON_DOWN 350#define BTN_DIR_LEFT BUTTON_LEFT 351#define BTN_DIR_RIGHT BUTTON_RIGHT 352#define BTN_STARTPAUSE BUTTON_SELECT 353#define BTN_QUIT BUTTON_POWER 354#define BTN_STOPRESET (BUTTON_SELECT|BUTTON_REPEAT) 355 356#elif CONFIG_KEYPAD == XDUOO_X3_PAD 357#define BTN_DIR_UP BUTTON_HOME 358#define BTN_DIR_DOWN BUTTON_OPTION 359#define BTN_DIR_LEFT BUTTON_PREV 360#define BTN_DIR_RIGHT BUTTON_NEXT 361#define BTN_STARTPAUSE BUTTON_PLAY 362#define BTN_QUIT BUTTON_POWER 363#define BTN_STOPRESET (BUTTON_HOME | BUTTON_PWRALT) 364 365#elif CONFIG_KEYPAD == XDUOO_X3II_PAD || CONFIG_KEYPAD == XDUOO_X20_PAD 366#define BTN_DIR_UP BUTTON_HOME 367#define BTN_DIR_DOWN BUTTON_OPTION 368#define BTN_DIR_LEFT BUTTON_PREV 369#define BTN_DIR_RIGHT BUTTON_NEXT 370#define BTN_STARTPAUSE BUTTON_PLAY 371#define BTN_QUIT BUTTON_POWER 372#define BTN_STOPRESET (BUTTON_HOME | BUTTON_POWER) 373 374#elif CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD 375#define BTN_DIR_UP BUTTON_HOME 376#define BTN_DIR_DOWN BUTTON_OPTION 377#define BTN_DIR_LEFT BUTTON_PREV 378#define BTN_DIR_RIGHT BUTTON_NEXT 379#define BTN_STARTPAUSE BUTTON_PLAY 380#define BTN_QUIT BUTTON_POWER 381#define BTN_STOPRESET (BUTTON_HOME | BUTTON_POWER) 382 383#elif CONFIG_KEYPAD == IHIFI_770_PAD || CONFIG_KEYPAD == IHIFI_800_PAD 384#define BTN_DIR_UP BUTTON_PREV 385#define BTN_DIR_DOWN BUTTON_NEXT 386#define BTN_DIR_LEFT BUTTON_HOME 387#define BTN_DIR_RIGHT BUTTON_VOL_DOWN 388#define BTN_STARTPAUSE BUTTON_PLAY 389#define BTN_QUIT BUTTON_POWER 390#define BTN_STOPRESET BUTTON_VOL_UP 391 392#elif CONFIG_KEYPAD == EROSQ_PAD 393#define BTN_DIR_UP BUTTON_PREV 394#define BTN_DIR_DOWN BUTTON_NEXT 395#define BTN_DIR_LEFT BUTTON_SCROLL_BACK 396#define BTN_DIR_RIGHT BUTTON_SCROLL_FWD 397#define BTN_STARTPAUSE BUTTON_PLAY 398#define BTN_QUIT BUTTON_POWER 399#define BTN_STOPRESET BUTTON_BACK 400 401#elif CONFIG_KEYPAD == FIIO_M3K_PAD 402#define BTN_DIR_UP BUTTON_UP 403#define BTN_DIR_DOWN BUTTON_DOWN 404#define BTN_DIR_LEFT BUTTON_LEFT 405#define BTN_DIR_RIGHT BUTTON_RIGHT 406#define BTN_STARTPAUSE BUTTON_PLAY 407#define BTN_QUIT BUTTON_POWER 408#define BTN_STOPRESET BUTTON_BACK 409 410#elif CONFIG_KEYPAD == SHANLING_Q1_PAD 411/* use touchscreen */ 412 413#elif CONFIG_KEYPAD == SDL_PAD 414/* use touchscreen */ 415#elif CONFIG_KEYPAD == MA_PAD 416#define BTN_DIR_UP BUTTON_UP 417#define BTN_DIR_DOWN BUTTON_DOWN 418#define BTN_DIR_LEFT BUTTON_LEFT 419#define BTN_DIR_RIGHT BUTTON_RIGHT 420#define BTN_STARTPAUSE BUTTON_PLAY 421#define BTN_QUIT BUTTON_BACK 422#define BTN_STOPRESET BUTTON_MENU 423 424#elif CONFIG_KEYPAD == RG_NANO_PAD 425 426#define BTN_DIR_UP BUTTON_UP 427#define BTN_DIR_DOWN BUTTON_DOWN 428#define BTN_DIR_LEFT BUTTON_LEFT 429#define BTN_DIR_RIGHT BUTTON_RIGHT 430#define BTN_STARTPAUSE BUTTON_A 431#define BTN_QUIT BUTTON_START 432#define BTN_STOPRESET BUTTON_B 433 434#else 435#error No keymap defined! 436#endif 437 438#ifdef HAVE_TOUCHSCREEN 439#ifndef BTN_DIR_UP 440#define BTN_DIR_UP BUTTON_TOPMIDDLE 441#endif 442#ifndef BTN_DIR_DOWN 443#define BTN_DIR_DOWN BUTTON_BOTTOMMIDDLE 444#endif 445#ifndef BTN_DIR_LEFT 446#define BTN_DIR_LEFT BUTTON_MIDLEFT 447#endif 448#ifndef BTN_DIR_RIGHT 449#define BTN_DIR_RIGHT BUTTON_MIDRIGHT 450#endif 451#ifndef BTN_STARTPAUSE 452#define BTN_STARTPAUSE BUTTON_CENTER 453#endif 454#ifndef BTN_QUIT 455#define BTN_QUIT BUTTON_TOPLEFT 456#endif 457#ifndef BTN_STOPRESET 458#define BTN_STOPRESET BUTTON_TOPRIGHT 459#endif 460#endif 461 462#if (LCD_WIDTH == 96) && (LCD_HEIGHT == 96) 463#define FOOD_SIZE 3 464#define ARGH_SIZE 4 465#define SPEED 14 466#define MAX_WORM_SEGMENTS 128 467#elif (LCD_WIDTH == 112) && (LCD_HEIGHT == 64) 468#define FOOD_SIZE 3 469#define ARGH_SIZE 4 470#define SPEED 14 471#define MAX_WORM_SEGMENTS 128 472#elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 64) 473#define FOOD_SIZE 3 474#define ARGH_SIZE 4 475#define SPEED 14 476#define MAX_WORM_SEGMENTS 128 477#elif (LCD_WIDTH == 132) && (LCD_HEIGHT == 80) 478#define FOOD_SIZE 3 479#define ARGH_SIZE 4 480#define SPEED 14 481#define MAX_WORM_SEGMENTS 128 482#elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 96) 483#define FOOD_SIZE 3 484#define ARGH_SIZE 4 485#define SPEED 12 486#define MAX_WORM_SEGMENTS 128 487#elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110) 488#define FOOD_SIZE 4 489#define ARGH_SIZE 5 490#define SPEED 10 491#define MAX_WORM_SEGMENTS 128 492#elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128) 493#define FOOD_SIZE 4 494#define ARGH_SIZE 5 495#define SPEED 9 496#define MAX_WORM_SEGMENTS 128 497#elif ((LCD_WIDTH == 160) && (LCD_HEIGHT == 128)) || \ 498 ((LCD_WIDTH == 128) && (LCD_HEIGHT == 160)) 499#define FOOD_SIZE 4 500#define ARGH_SIZE 5 501#define SPEED 8 502#define MAX_WORM_SEGMENTS 256 503#elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132) 504#define FOOD_SIZE 4 505#define ARGH_SIZE 5 506#define SPEED 6 507#define MAX_WORM_SEGMENTS 256 508#elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176) 509#define FOOD_SIZE 5 510#define ARGH_SIZE 6 511#define SPEED 4 512#define MAX_WORM_SEGMENTS 512 513#elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220) 514#define FOOD_SIZE 5 515#define ARGH_SIZE 6 516#define SPEED 4 517#define MAX_WORM_SEGMENTS 512 518#elif ((LCD_WIDTH == 240) && (LCD_HEIGHT == 240)) || \ 519 ((LCD_WIDTH == 320) && (LCD_HEIGHT == 240)) || \ 520 ((LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400))) || \ 521 ((LCD_WIDTH == 360) && (LCD_HEIGHT == 400)) 522#define FOOD_SIZE 7 523#define ARGH_SIZE 8 524#define SPEED 4 525#define MAX_WORM_SEGMENTS 512 526#elif ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \ 527 ((LCD_WIDTH == 480) && (LCD_HEIGHT == 640)) 528#define FOOD_SIZE 14 529#define ARGH_SIZE 16 530#define SPEED 4 531#define MAX_WORM_SEGMENTS 512 532#endif 533 534#ifdef HAVE_LCD_COLOR 535#define COLOR_WORM LCD_RGBPACK(80, 40, 0) 536#define COLOR_ARGH LCD_RGBPACK(175, 0, 0) 537#define COLOR_FOOD LCD_RGBPACK(0, 150, 0) 538#define COLOR_FG LCD_RGBPACK(0, 0, 0) 539#define COLOR_BG LCD_RGBPACK(181, 199, 231) 540#endif 541 542#define CHECK_SQUARE_COLLISION(x1,y1,s1,x2,y2,s2) (x1+s1>x2)&&(x2+s2>x1)&&(y1+s1>y2)&&(y2+s2>y1) 543 544/** 545 * All the properties that a worm has. 546 */ 547static struct worm { 548 /* The worm is stored in a ring of xy coordinates */ 549 int x[MAX_WORM_SEGMENTS]; 550 int y[MAX_WORM_SEGMENTS]; 551 552 int head; /* index of the head within the buffer */ 553 int tail; /* index of the tail within the buffer */ 554 int growing; /* number of cyles the worm still keeps growing */ 555 bool alive; /* the worms living state */ 556 557 /* direction vector in which the worm moves */ 558 int dirx; /* only values -1 0 1 allowed */ 559 int diry; /* only values -1 0 1 allowed */ 560 561 /* this method is used to fetch the direction the user 562 has selected. It can be one of the values 563 human_player1, human_player2, remote_player, virtual_player. 564 All these values are fuctions, that can change the direction 565 of the worm */ 566 void (*fetch_worm_direction)(struct worm *w); 567} worms[MAX_WORMS]; 568 569/* stores the highscore - besides it was scored by a virtual player */ 570static int highscore; 571 572#define MAX_FOOD 5 /* maximal number of food items */ 573 574/* The arrays store the food coordinates */ 575static int foodx[MAX_FOOD]; 576static int foody[MAX_FOOD]; 577 578#define MAX_ARGH 100 /* maximal number of argh items */ 579#define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */ 580 581/* The arrays store the argh coordinates */ 582static int arghx[MAX_ARGH]; 583static int arghy[MAX_ARGH]; 584 585/* the number of arghs that are currently in use */ 586static int argh_count; 587 588/* the number of arghs per food, settable by user */ 589static int arghs_per_food = ARGHS_PER_FOOD; 590/* the size of the argh, settable by user */ 591static int argh_size = ARGH_SIZE; 592/* the size of the food, settable by user */ 593static int food_size = FOOD_SIZE; 594/* the speed of the worm, settable by user */ 595static int speed = SPEED; 596/* the amount a worm grows by eating a food, settable by user */ 597static int worm_food = WORM_PER_FOOD; 598 599/* End additional variables */ 600 601/* the number of active worms (dead or alive) */ 602static int worm_count = MAX_WORMS; 603 604/* in multiplayer mode: en- / disables the remote worm control 605 in singleplayer mode: toggles 4 / 2 button worm control */ 606static bool use_remote = false; 607 608/* return values of check_collision */ 609#define COLLISION_NONE 0 610#define COLLISION_WORM 1 611#define COLLISION_FOOD 2 612#define COLLISION_ARGH 3 613#define COLLISION_FIELD 4 614 615static const char *const state_desc[] = { 616 [COLLISION_NONE] = NULL, 617 [COLLISION_WORM] = "Wormed", 618 [COLLISION_FOOD] = "Growing", 619 [COLLISION_ARGH] = "Argh", 620 [COLLISION_FIELD] = "Crashed", 621}; 622 623/* constants for use as directions. 624 Note that the values are ordered clockwise. 625 Thus increasing / decreasing the values 626 is equivalent to right / left turns. */ 627#define WEST 0 628#define NORTH 1 629#define EAST 2 630#define SOUTH 3 631 632/* direction of human player 1 */ 633static int player1_dir = EAST; 634/* direction of human player 2 */ 635static int player2_dir = EAST; 636/* direction of human player 3 */ 637static int player3_dir = EAST; 638 639/* the number of (human) players that currently 640 control a worm */ 641static int players = 1; 642 643#define SETTINGS_VERSION 1 644#define SETTINGS_MIN_VERSION 1 645#define SETTINGS_FILENAME "wormlet.cfg" 646 647static struct configdata config[] = 648{ 649 {TYPE_INT, 0, 1024, { .int_p = &highscore }, "highscore", NULL}, 650 {TYPE_INT, 0, 15, { .int_p = &arghs_per_food }, "arghs per food", NULL}, 651 {TYPE_INT, 0, 15, { .int_p = &argh_size }, "argh size", NULL}, 652 {TYPE_INT, 0, 15, { .int_p = &food_size }, "food size", NULL}, 653 {TYPE_INT, 0, 3, { .int_p = &players }, "players", NULL}, 654 {TYPE_INT, 0, 3, { .int_p = &worm_count }, "worms", NULL}, 655 {TYPE_INT, 0, 20, { .int_p = &speed }, "speed", NULL}, 656 {TYPE_INT, 0, 15, { .int_p = &worm_food }, "Worm Growth Per Food", NULL} 657}; 658 659/** 660 * Returns the direction id in which the worm 661 * currently is creeping. 662 * @param struct worm *w The worm that is to be investigated. 663 * w Must not be null. 664 * @return int A value 0 <= value < 4 665 * Note the predefined constants NORTH, SOUTH, EAST, WEST 666 */ 667static int get_worm_dir(struct worm *w) 668{ 669 int retVal ; 670 if (w->dirx == 0) { 671 if (w->diry == 1) { 672 retVal = SOUTH; 673 } else { 674 retVal = NORTH; 675 } 676 } else { 677 if (w->dirx == 1) { 678 retVal = EAST; 679 } else { 680 retVal = WEST; 681 } 682 } 683 return retVal; 684} 685 686/** 687 * Set the direction of the specified worm with a direction id. 688 * Increasing the value by 1 means to turn the worm direction 689 * to right by 90 degree. 690 * @param struct worm *w The worm that is to be altered. w Must not be null. 691 * @param int dir The new direction in which the worm is to creep. 692 * dir must be 0 <= dir < 4. Use predefined constants 693 * NORTH, SOUTH, EAST, WEST 694 */ 695static void set_worm_dir(struct worm *w, int dir) 696{ 697 switch (dir) { 698 case WEST: 699 w->dirx = -1; 700 w->diry = 0; 701 break; 702 case NORTH: 703 w->dirx = 0; 704 w->diry = - 1; 705 break; 706 case EAST: 707 w->dirx = 1; 708 w->diry = 0; 709 break; 710 case SOUTH: 711 w->dirx = 0; 712 w->diry = 1; 713 break; 714 } 715} 716 717/** 718 * Returns the current length of the worm array. This 719 * is also a value for the number of bends that are in the worm. 720 * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS 721 */ 722static int get_worm_array_length(struct worm *w) 723{ 724 /* initial simple calculation will be overwritten if wrong. */ 725 int retVal = w->head - w->tail; 726 727 /* if the worm 'crosses' the boundaries of the ringbuffer */ 728 if (retVal < 0) { 729 retVal = w->head + MAX_WORM_SEGMENTS - w->tail; 730 } 731 732 return retVal; 733} 734 735/** 736 * Returns the score the specified worm. The score is the length 737 * of the worm. 738 * @param struct worm *w The worm that is to be investigated. 739 * w must not be null. 740 * @return int The length of the worm (>= 0). 741 */ 742static int get_score(struct worm *w) 743{ 744 int retval = 0; 745 int length = get_worm_array_length(w); 746 int i; 747 for (i = 0; i < length; i++) { 748 749 /* The iteration iterates the length of the worm. 750 Here's the conversion to the true indices within the worm arrays. */ 751 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS; 752 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS; 753 int startx = w->x[linestart]; 754 int starty = w->y[linestart]; 755 int endx = w->x[lineend]; 756 int endy = w->y[lineend]; 757 758 int minimum, maximum; 759 760 if (startx == endx) { 761 minimum = MIN(starty, endy); 762 maximum = MAX(starty, endy); 763 } else { 764 minimum = MIN(startx, endx); 765 maximum = MAX(startx, endx); 766 } 767 retval += abs(maximum - minimum); 768 } 769 return retval; 770} 771 772/** 773 * Determines wether the line specified by startx, starty, endx, endy intersects 774 * the rectangle specified by x, y, width, height. Note that the line must be exactly 775 * horizontal or vertical (startx == endx or starty == endy). 776 * @param int startx The x coordinate of the start point of the line. 777 * @param int starty The y coordinate of the start point of the line. 778 * @param int endx The x coordinate of the end point of the line. 779 * @param int endy The y coordinate of the end point of the line. 780 * @param int x The x coordinate of the top left corner of the rectangle. 781 * @param int y The y coordinate of the top left corner of the rectangle. 782 * @param int width The width of the rectangle. 783 * @param int height The height of the rectangle. 784 * @return bool Returns true if the specified line intersects with the recangle. 785 */ 786static bool line_in_rect(int startx, int starty, int endx, int endy, 787 int x, int y, int width, int height) 788{ 789 bool retval = false; 790 int simple, simplemin, simplemax; 791 int compa, compb, compmin, compmax; 792 int temp; 793 if (startx == endx) { 794 simple = startx; 795 simplemin = x; 796 simplemax = x + width; 797 798 compa = starty; 799 compb = endy; 800 compmin = y; 801 compmax = y + height; 802 } else { 803 simple = starty; 804 simplemin = y; 805 simplemax = y + height; 806 807 compa = startx; 808 compb = endx; 809 compmin = x; 810 compmax = x + width; 811 }; 812 813 temp = compa; 814 compa = MIN(compa, compb); 815 compb = MAX(temp, compb); 816 817 if (simplemin <= simple && simple <= simplemax) { 818 if ((compmin <= compa && compa <= compmax) || 819 (compmin <= compb && compb <= compmax) || 820 (compa <= compmin && compb >= compmax)) { 821 retval = true; 822 } 823 } 824 return retval; 825} 826 827/** 828 * Tests wether the specified worm intersects with the rect. 829 * @param struct worm *w The worm to be investigated 830 * @param int x The x coordinate of the top left corner of the rect 831 * @param int y The y coordinate of the top left corner of the rect 832 * @param int widht The width of the rect 833 * @param int height The height of the rect 834 * @return bool Returns true if the worm intersects with the rect 835 */ 836static bool worm_in_rect(struct worm *w, int x, int y, int width, int height) 837{ 838 bool retval = false; 839 840 841 /* get_worm_array_length is expensive -> buffer the value */ 842 int wormLength = get_worm_array_length(w); 843 int i; 844 845 /* test each entry that is part of the worm */ 846 for (i = 0; i < wormLength && retval == false; i++) { 847 848 /* The iteration iterates the length of the worm. 849 Here's the conversion to the true indices within the worm arrays. */ 850 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS; 851 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS; 852 int startx = w->x[linestart]; 853 int starty = w->y[linestart]; 854 int endx = w->x[lineend]; 855 int endy = w->y[lineend]; 856 857 retval = line_in_rect(startx, starty, endx, endy, x, y, width, height); 858 } 859 860 return retval; 861} 862 863/** 864 * Checks wether a specific food in the food arrays is at the 865 * specified coordinates. 866 * @param int foodIndex The index of the food in the food arrays 867 * @param int x the x coordinate. 868 * @param int y the y coordinate. 869 * @return Returns true if the coordinate hits the food specified by 870 * foodIndex. 871 */ 872static bool specific_food_collision(int foodIndex, int x, int y) 873{ 874 bool retVal = false; 875 if (x >= foodx[foodIndex] && 876 x < foodx[foodIndex] + food_size && 877 y >= foody[foodIndex] && 878 y < foody[foodIndex] + food_size) { 879 880 retVal = true; 881 } 882 return retVal; 883} 884 885/** 886 * Returns the index of the food that is at the 887 * given coordinates. If no food is at the coordinates 888 * -1 is returned. 889 * @return int -1 <= value < MAX_FOOD 890 */ 891static int food_collision(int x, int y) 892{ 893 int i = 0; 894 int retVal = -1; 895 for (i = 0; i < MAX_FOOD; i++) { 896 if (specific_food_collision(i, x, y)) { 897 retVal = i; 898 break; 899 } 900 } 901 return retVal; 902} 903 904/** 905 * Checks wether a specific argh in the argh arrays is at the 906 * specified coordinates. 907 * @param int arghIndex The index of the argh in the argh arrays 908 * @param int x the x coordinate. 909 * @param int y the y coordinate. 910 * @return Returns true if the coordinate hits the argh specified by 911 * arghIndex. 912 */ 913static bool specific_argh_collision(int arghIndex, int x, int y) 914{ 915 if ( x >= arghx[arghIndex] && 916 y >= arghy[arghIndex] && 917 x < arghx[arghIndex] + argh_size && 918 y < arghy[arghIndex] + argh_size ) 919 { 920 return true; 921 } 922 923 return false; 924} 925 926/** 927 * Returns the index of the argh that is at the 928 * given coordinates. If no argh is at the coordinates 929 * -1 is returned. 930 * @param int x The x coordinate. 931 * @param int y The y coordinate. 932 * @return int -1 <= value < argh_count <= MAX_ARGH 933 */ 934static int argh_collision(int x, int y) 935{ 936 int i = 0; 937 int retVal = -1; 938 939 /* search for the argh that has the specified coords */ 940 for (i = 0; i < argh_count; i++) { 941 if (specific_argh_collision(i, x, y)) { 942 retVal = i; 943 break; 944 } 945 } 946 return retVal; 947} 948 949/** 950 * Checks wether the worm collides with the food at the specfied food-arrays. 951 * @param int foodIndex The index of the food in the arrays. Ensure the value is 952 * 0 <= foodIndex <= MAX_FOOD 953 * @return Returns true if the worm collides with the specified food. 954 */ 955static bool worm_food_collision(struct worm *w, int foodIndex) 956{ 957 bool retVal = false; 958 959 retVal = worm_in_rect(w, foodx[foodIndex], foody[foodIndex], 960 food_size - 1, food_size - 1); 961 962 return retVal; 963} 964 965/** 966 * Returns true if the worm hits the argh within the next moves (unless 967 * the worm changes it's direction). 968 * @param struct worm *w - The worm to investigate 969 * @param int argh_idx - The index of the argh 970 * @param int moves - The number of moves that are considered. 971 * @return Returns false if the specified argh is not hit within the next 972 * moves. 973 */ 974static bool worm_argh_collision_in_moves(struct worm *w, int argh_idx, int moves) 975{ 976 bool retVal = false; 977 int x1, y1, x2, y2; 978 x1 = w->x[w->head]; 979 y1 = w->y[w->head]; 980 981 x2 = w->x[w->head] + moves * w->dirx; 982 y2 = w->y[w->head] + moves * w->diry; 983 984 retVal = line_in_rect(x1, y1, x2, y2, arghx[argh_idx], arghy[argh_idx], 985 argh_size, argh_size); 986 return retVal; 987} 988 989/** 990 * Checks wether the worm collides with the argh at the specfied argh-arrays. 991 * @param int arghIndex The index of the argh in the arrays. 992 * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH 993 * @return Returns true if the worm collides with the specified argh. 994 */ 995static bool worm_argh_collision(struct worm *w, int arghIndex) 996{ 997 bool retVal = false; 998 999 retVal = worm_in_rect(w, arghx[arghIndex], arghy[arghIndex], 1000 argh_size - 1, argh_size - 1); 1001 1002 return retVal; 1003} 1004 1005/** 1006 * Find new coordinates for the food stored in foodx[index], foody[index] 1007 * that don't collide with any other food or argh 1008 * @param int index 1009 * Ensure that 0 <= index < MAX_FOOD. 1010 */ 1011static void make_food(int index) 1012{ 1013 int x = 0; 1014 int y = 0; 1015 bool collisionDetected = false; 1016 int i; 1017 1018 do { 1019 /* make coordinates for a new food so that 1020 the entire food lies within the FIELD */ 1021 x = rb->rand() % (FIELD_RECT_WIDTH - food_size); 1022 y = rb->rand() % (FIELD_RECT_HEIGHT - food_size); 1023 collisionDetected = false; 1024 /* Ensure that the new food doesn't collide with any 1025 existing foods or arghs. 1026 If the new food hit any existing 1027 argh or food a collision is detected. 1028 */ 1029 1030 for (i=0; i<MAX_FOOD && !collisionDetected; i++) { 1031 collisionDetected = CHECK_SQUARE_COLLISION(x,y,food_size,foodx[i],foody[i],food_size); 1032 } 1033 for (i=0; i<argh_count && !collisionDetected; i++) { 1034 collisionDetected = CHECK_SQUARE_COLLISION(x,y,food_size,arghx[i],arghy[i],argh_size); 1035 } 1036 1037 /* use coordinates for further testing */ 1038 foodx[index] = x; 1039 foody[index] = y; 1040 1041 /* now test wether we accidently hit the worm with food ;) */ 1042 i = 0; 1043 for (i = 0; i < worm_count && !collisionDetected; i++) { 1044 1045 collisionDetected = worm_food_collision(&worms[i], index); 1046 1047 } 1048 } 1049 while (collisionDetected); 1050 return; 1051} 1052 1053/** 1054 * Clears a food from the lcd buffer. 1055 * @param int index The index of the food arrays under which 1056 * the coordinates of the desired food can be found. Ensure 1057 * that the value is 0 <= index <= MAX_FOOD. 1058 */ 1059static void clear_food(int index) 1060{ 1061 /* remove the old food from the screen */ 1062 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); 1063 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X, 1064 foody[index] + FIELD_RECT_Y, 1065 food_size, food_size); 1066 rb->lcd_set_drawmode(DRMODE_SOLID); 1067} 1068 1069/** 1070 * Draws a food in the lcd buffer. 1071 * @param int index The index of the food arrays under which 1072 * the coordinates of the desired food can be found. Ensure 1073 * that the value is 0 <= index <= MAX_FOOD. 1074 */ 1075static void draw_food(int index) 1076{ 1077 /* draw the food object */ 1078#ifdef HAVE_LCD_COLOR 1079 rb->lcd_set_foreground(COLOR_FOOD); 1080#endif 1081 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X, 1082 foody[index] + FIELD_RECT_Y, 1083 food_size, food_size); 1084 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); 1085 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X + 1, 1086 foody[index] + FIELD_RECT_Y + 1, 1087 food_size - 2, food_size - 2); 1088 rb->lcd_set_drawmode(DRMODE_SOLID); 1089#ifdef HAVE_LCD_COLOR 1090 rb->lcd_set_foreground(COLOR_FG); 1091#endif 1092} 1093 1094/** 1095 * Find new coordinates for the argh stored in arghx[index], arghy[index] 1096 * that don't collide with any other food or argh. 1097 * @param int index 1098 * Ensure that 0 <= index < argh_count < MAX_ARGH. 1099 */ 1100static void make_argh(int index) 1101{ 1102 int x = -1; 1103 int y = -1; 1104 bool collisionDetected = false; 1105 int i; 1106 1107 do { 1108 /* make coordinates for a new argh so that 1109 the entire food lies within the FIELD */ 1110 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size); 1111 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size); 1112 collisionDetected = false; 1113 /* Ensure that the new argh doesn't intersect with any 1114 existing foods or arghs. 1115 If the new argh hit any existing 1116 argh or food an intersection is detected. 1117 */ 1118 1119 for (i=0; i<MAX_FOOD && !collisionDetected; i++) { 1120 collisionDetected = CHECK_SQUARE_COLLISION(x,y,argh_size,foodx[i],foody[i],food_size); 1121 } 1122 for (i=0; i<argh_count && !collisionDetected; i++) { 1123 collisionDetected = CHECK_SQUARE_COLLISION(x,y,argh_size,arghx[i],arghy[i],argh_size); 1124 } 1125 1126 /* use the candidate coordinates to make a real argh */ 1127 arghx[index] = x; 1128 arghy[index] = y; 1129 1130 /* now test wether we accidently hit the worm with argh ;) */ 1131 for (i = 0; i < worm_count && !collisionDetected; i++) { 1132 collisionDetected |= worm_argh_collision(&worms[i], index); 1133 collisionDetected |= worm_argh_collision_in_moves(&worms[i], index, 1134 MIN_ARGH_DIST); 1135 } 1136 } 1137 while (collisionDetected); 1138 return; 1139} 1140 1141/** 1142 * Draws an argh in the lcd buffer. 1143 * @param int index The index of the argh arrays under which 1144 * the coordinates of the desired argh can be found. Ensure 1145 * that the value is 0 <= index < argh_count <= MAX_ARGH. 1146 */ 1147static void draw_argh(int index) 1148{ 1149 /* draw the new argh */ 1150#ifdef HAVE_LCD_COLOR 1151 rb->lcd_set_foreground(COLOR_ARGH); 1152#endif 1153 rb->lcd_fillrect(arghx[index] + FIELD_RECT_X, 1154 arghy[index] + FIELD_RECT_Y, 1155 argh_size, argh_size); 1156#ifdef HAVE_LCD_COLOR 1157 rb->lcd_set_foreground(COLOR_FG); 1158#endif 1159} 1160 1161static void virtual_player(struct worm *w); 1162/** 1163 * Initialzes the specified worm with INITIAL_WORM_LENGTH 1164 * and the tail at the specified position. The worm will 1165 * be initialized alive and creeping EAST. 1166 * @param struct worm *w The worm that is to be initialized 1167 * @param int x The x coordinate at which the tail of the worm starts. 1168 * x must be 0 <= x < FIELD_RECT_WIDTH. 1169 * @param int y The y coordinate at which the tail of the worm starts 1170 * y must be 0 <= y < FIELD_RECT_WIDTH. 1171 */ 1172static void init_worm(struct worm *w, int x, int y) 1173{ 1174 /* initialize the worm size */ 1175 w->head = 1; 1176 w->tail = 0; 1177 1178 w->x[w->head] = x + 1; 1179 w->y[w->head] = y; 1180 1181 w->x[w->tail] = x; 1182 w->y[w->tail] = y; 1183 1184 /* set the initial direction the worm creeps to */ 1185 w->dirx = 1; 1186 w->diry = 0; 1187 1188 w->growing = INITIAL_WORM_LENGTH - 1; 1189 w->alive = true; 1190 w->fetch_worm_direction = virtual_player; 1191} 1192 1193/** 1194 * Writes the direction that was stored for 1195 * human player 1 into the specified worm. This function 1196 * may be used to be stored in worm.fetch_worm_direction. 1197 * The value of 1198 * the direction is read from player1_dir. 1199 * @param struct worm *w - The worm of which the direction 1200 * is altered. 1201 */ 1202static void human_player1(struct worm *w) { 1203 set_worm_dir(w, player1_dir); 1204} 1205 1206/** 1207 * Writes the direction that was stored for 1208 * human player 2 into the specified worm. This function 1209 * may be used to be stored in worm.fetch_worm_direction. 1210 * The value of 1211 * the direction is read from player2_dir. 1212 * @param struct worm *w - The worm of which the direction 1213 * is altered. 1214 */ 1215static void human_player2(struct worm *w) { 1216 set_worm_dir(w, player2_dir); 1217} 1218 1219/** 1220 * Writes the direction that was stored for 1221 * human player using a remote control 1222 * into the specified worm. This function 1223 * may be used to be stored in worm.fetch_worm_direction. 1224 * The value of 1225 * the direction is read from player3_dir. 1226 * @param struct worm *w - The worm of which the direction 1227 * is altered. 1228 */ 1229static void remote_player(struct worm *w) { 1230 set_worm_dir(w, player3_dir); 1231} 1232 1233/** 1234 * Initializes the worm-, food- and argh-arrays, draws a frame, 1235 * makes some food and argh and display all that stuff. 1236 */ 1237static void init_wormlet(void) 1238{ 1239 int i; 1240 1241 for (i = 0; i< worm_count; i++) { 1242 /* Initialize all the worm coordinates to center. */ 1243 int x = (int)(FIELD_RECT_WIDTH / 2); 1244 int y = (int)((FIELD_RECT_HEIGHT - 20)/ 2) + i * 10; 1245 1246 init_worm(&worms[i], x, y); 1247 } 1248 1249 player1_dir = EAST; 1250 player2_dir = EAST; 1251 player3_dir = EAST; 1252 1253 if (players > 0) { 1254 worms[0].fetch_worm_direction = human_player1; 1255 } 1256 1257 if (players > 1) { 1258 if (use_remote) { 1259 worms[1].fetch_worm_direction = remote_player; 1260 } else { 1261 worms[1].fetch_worm_direction = human_player2; 1262 } 1263 } 1264 1265 if (players > 2) { 1266 worms[2].fetch_worm_direction = human_player2; 1267 } 1268 1269 /* Needed when the game is restarted using BTN_STOPRESET */ 1270 rb->lcd_clear_display(); 1271 1272 /* make and display some food and argh */ 1273 argh_count = MAX_FOOD; 1274 for (i = 0; i < MAX_FOOD; i++) { 1275 make_food(i); 1276 draw_food(i); 1277 make_argh(i); 1278 draw_argh(i); 1279 } 1280 1281 /* draw the game field */ 1282 rb->lcd_set_drawmode(DRMODE_COMPLEMENT); 1283 rb->lcd_fillrect(0, 0, FIELD_RECT_WIDTH + 2, FIELD_RECT_HEIGHT + 2); 1284 rb->lcd_fillrect(1, 1, FIELD_RECT_WIDTH, FIELD_RECT_HEIGHT); 1285 rb->lcd_set_drawmode(DRMODE_SOLID); 1286 1287 /* make everything visible */ 1288 rb->lcd_update(); 1289} 1290 1291 1292/** 1293 * Move the worm one step further if it is alive. 1294 * The direction in which the worm moves is taken from dirx and diry. 1295 * move_worm decreases growing if > 0. While the worm is growing the tail 1296 * is left untouched. 1297 * @param struct worm *w The worm to move. w must not be NULL. 1298 */ 1299static void move_worm(struct worm *w) 1300{ 1301 if (w->alive) { 1302 /* determine the head point and its precessor */ 1303 int headx = w->x[w->head]; 1304 int heady = w->y[w->head]; 1305 int prehead = (w->head + MAX_WORM_SEGMENTS - 1) % MAX_WORM_SEGMENTS; 1306 int preheadx = w->x[prehead]; 1307 int preheady = w->y[prehead]; 1308 1309 /* determine the old direction */ 1310 int olddirx; 1311 int olddiry; 1312 if (headx == preheadx) { 1313 olddirx = 0; 1314 olddiry = (heady > preheady) ? 1 : -1; 1315 } else { 1316 olddiry = 0; 1317 olddirx = (headx > preheadx) ? 1 : -1; 1318 } 1319 1320 /* olddir == dir? 1321 a change of direction means a new segment 1322 has been opened */ 1323 if (olddirx != w->dirx || 1324 olddiry != w->diry) { 1325 w->head = (w->head + 1) % MAX_WORM_SEGMENTS; 1326 } 1327 1328 /* new head position */ 1329 w->x[w->head] = headx + w->dirx; 1330 w->y[w->head] = heady + w->diry; 1331 1332 1333 /* while the worm is growing no tail procession is necessary */ 1334 if (w->growing > 0) { 1335 /* update the worms grow state */ 1336 w->growing--; 1337 } 1338 1339 /* if the worm isn't growing the tail has to be dragged */ 1340 else { 1341 /* index of the end of the tail segment */ 1342 int tail_segment_end = (w->tail + 1) % MAX_WORM_SEGMENTS; 1343 1344 /* drag the end of the tail */ 1345 /* only one coordinate has to be altered. Here it is 1346 determined which one */ 1347 int dir = 0; /* specifies wether the coord has to be in- or decreased */ 1348 if (w->x[w->tail] == w->x[tail_segment_end]) { 1349 dir = (w->y[w->tail] - w->y[tail_segment_end] < 0) ? 1 : -1; 1350 w->y[w->tail] += dir; 1351 } else { 1352 dir = (w->x[w->tail] - w->x[tail_segment_end] < 0) ? 1 : -1; 1353 w->x[w->tail] += dir; 1354 } 1355 1356 /* when the tail has been dragged so far that it meets 1357 the next segment start the tail segment is obsolete and 1358 must be freed */ 1359 if (w->x[w->tail] == w->x[tail_segment_end] && 1360 w->y[w->tail] == w->y[tail_segment_end]){ 1361 1362 /* drop the last tail point */ 1363 w->tail = tail_segment_end; 1364 } 1365 } 1366 } 1367} 1368 1369/** 1370 * Draws the head and clears the tail of the worm in 1371 * the display buffer. lcd_update() is NOT called thus 1372 * the caller has to take care that the buffer is displayed. 1373 */ 1374static void draw_worm(struct worm *w) 1375{ 1376 /* draw the new head */ 1377 int x = w->x[w->head]; 1378 int y = w->y[w->head]; 1379#ifdef HAVE_LCD_COLOR 1380 rb->lcd_set_foreground(COLOR_WORM); 1381#endif 1382 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) { 1383 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y); 1384 } 1385 1386 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); 1387 1388 /* clear the space behind the worm */ 1389 x = w->x[w->tail] ; 1390 y = w->y[w->tail] ; 1391 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) { 1392 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y); 1393 } 1394 rb->lcd_set_drawmode(DRMODE_SOLID); 1395#ifdef HAVE_LCD_COLOR 1396 rb->lcd_set_foreground(COLOR_FG); 1397#endif 1398} 1399 1400/** 1401 * Checks wether the coordinate is part of the worm. Returns 1402 * true if any part of the worm was hit - including the head. 1403 * @param x int The x coordinate 1404 * @param y int The y coordinate 1405 * @return int The index of the worm arrays that contain x, y. 1406 * Returns -1 if the coordinates are not part of the worm. 1407 */ 1408static int specific_worm_collision(struct worm *w, int x, int y) 1409{ 1410 int retVal = -1; 1411 1412 /* get_worm_array_length is expensive -> buffer the value */ 1413 int wormLength = get_worm_array_length(w); 1414 int i; 1415 1416 /* test each entry that is part of the worm */ 1417 for (i = 0; i < wormLength && retVal == -1; i++) { 1418 1419 /* The iteration iterates the length of the worm. 1420 Here's the conversion to the true indices within the worm arrays. */ 1421 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS; 1422 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS; 1423 bool samex = (w->x[linestart] == x) && (w->x[lineend] == x); 1424 bool samey = (w->y[linestart] == y) && (w->y[lineend] == y); 1425 if (samex || samey){ 1426 int test, min, max, tmp; 1427 1428 if (samey) { 1429 min = w->x[linestart]; 1430 max = w->x[lineend]; 1431 test = x; 1432 } else { 1433 min = w->y[linestart]; 1434 max = w->y[lineend]; 1435 test = y; 1436 } 1437 1438 tmp = min; 1439 min = MIN(min, max); 1440 max = MAX(tmp, max); 1441 1442 if (min <= test && test <= max) { 1443 retVal = lineend; 1444 } 1445 } 1446 } 1447 return retVal; 1448} 1449 1450/** 1451 * Increases the length of the specified worm by marking 1452 * that it may grow by len pixels. Note that the worm has 1453 * to move to make the growing happen. 1454 * @param worm *w The worm that is to be altered. 1455 * @param int len A positive value specifying the amount of 1456 * pixels the worm may grow. 1457 */ 1458static void add_growing(struct worm *w, int len) { 1459 w->growing += len; 1460} 1461 1462/** 1463 * Determins the worm that is at the coordinates x, y. The parameter 1464 * w is a switch parameter that changes the functionality of worm_collision. 1465 * If w is specified and x,y hits the head of w NULL is returned. 1466 * This is a useful way to determine wether the head of w hits 1467 * any worm but including itself but excluding its own head. 1468 * (It hits always its own head ;)) 1469 * If w is set to NULL worm_collision returns any worm including all heads 1470 * that is at position of x,y. 1471 * @param struct worm *w The worm of which the head should be excluded in 1472 * the test. w may be set to NULL. 1473 * @param int x The x coordinate that is checked 1474 * @param int y The y coordinate that is checkec 1475 * @return struct worm* The worm that has been hit by x,y. If no worm 1476 * was at the position NULL is returned. 1477 */ 1478static struct worm* worm_collision(struct worm *w, int x, int y) 1479{ 1480 struct worm *retVal = NULL; 1481 int i; 1482 for (i = 0; (i < worm_count) && (retVal == NULL); i++) { 1483 int collision_at = specific_worm_collision(&worms[i], x, y); 1484 if (collision_at != -1) { 1485 if (!(w == &worms[i] && collision_at == w->head)){ 1486 retVal = &worms[i]; 1487 } 1488 } 1489 } 1490 return retVal; 1491} 1492 1493/** 1494 * Returns true if the head of the worm just has 1495 * crossed the field boundaries. 1496 * @return bool true if the worm just has wrapped. 1497 */ 1498static bool field_collision(struct worm *w) 1499{ 1500 bool retVal = false; 1501 if ((w->x[w->head] >= FIELD_RECT_WIDTH) || 1502 (w->y[w->head] >= FIELD_RECT_HEIGHT) || 1503 (w->x[w->head] < 0) || 1504 (w->y[w->head] < 0)) 1505 { 1506 retVal = true; 1507 } 1508 return retVal; 1509} 1510 1511 1512/** 1513 * Returns true if the specified coordinates are within the 1514 * field specified by the FIELD_RECT_XXX constants. 1515 * @param int x The x coordinate of the point that is investigated 1516 * @param int y The y coordinate of the point that is investigated 1517 * @return bool Returns false if x,y specifies a point outside the 1518 * field of worms. 1519 */ 1520static bool is_in_field_rect(int x, int y) 1521{ 1522 bool retVal = false; 1523 retVal = (x >= 0 && x < FIELD_RECT_WIDTH && 1524 y >= 0 && y < FIELD_RECT_HEIGHT); 1525 return retVal; 1526} 1527 1528/** 1529 * Checks and returns wether the head of the w 1530 * is colliding with something currently. 1531 * @return int One of the values: 1532 * COLLISION_NONE 1533 * COLLISION_w 1534 * COLLISION_FOOD 1535 * COLLISION_ARGH 1536 * COLLISION_FIELD 1537 */ 1538static int check_collision(struct worm *w) 1539{ 1540 int retVal = COLLISION_NONE; 1541 1542 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) 1543 retVal = COLLISION_WORM; 1544 1545 if (food_collision(w->x[w->head], w->y[w->head]) >= 0) 1546 retVal = COLLISION_FOOD; 1547 1548 if (argh_collision(w->x[w->head], w->y[w->head]) >= 0) 1549 retVal = COLLISION_ARGH; 1550 1551 if (field_collision(w)) 1552 retVal = COLLISION_FIELD; 1553 1554 return retVal; 1555} 1556 1557/** 1558 * Returns the index of the food that is closest to the point 1559 * specified by x, y. This index may be used in the foodx and 1560 * foody arrays. 1561 * @param int x The x coordinate of the point 1562 * @param int y The y coordinate of the point 1563 * @return int A value usable as index in foodx and foody. 1564 */ 1565static int get_nearest_food(int x, int y) 1566{ 1567 int nearestfood = 0; 1568 int olddistance = FIELD_RECT_WIDTH + FIELD_RECT_HEIGHT; 1569 int deltax = 0; 1570 int deltay = 0; 1571 int foodindex; 1572 for (foodindex = 0; foodindex < MAX_FOOD; foodindex++) { 1573 int distance; 1574 deltax = foodx[foodindex] - x; 1575 deltay = foody[foodindex] - y; 1576 deltax = deltax > 0 ? deltax : deltax * (-1); 1577 deltay = deltay > 0 ? deltay : deltay * (-1); 1578 distance = deltax + deltay; 1579 1580 if (distance < olddistance) { 1581 olddistance = distance; 1582 nearestfood = foodindex; 1583 } 1584 } 1585 return nearestfood; 1586} 1587 1588/** 1589 * Returns wether the specified position is next to the worm 1590 * and in the direction the worm looks. Use this method to 1591 * test wether this position would be hit with the next move of 1592 * the worm unless the worm changes its direction. 1593 * @param struct worm *w - The worm to be investigated 1594 * @param int x - The x coordinate of the position to test. 1595 * @param int y - The y coordinate of the position to test. 1596 * @return Returns true if the worm will hit the position unless 1597 * it change its direction before the next move. 1598 */ 1599static bool is_in_front_of_worm(struct worm *w, int x, int y) 1600{ 1601 bool infront = false; 1602 int deltax = x - w->x[w->head]; 1603 int deltay = y - w->y[w->head]; 1604 1605 if (w->dirx == 0) { 1606 infront = (w->diry * deltay) > 0; 1607 } else { 1608 infront = (w->dirx * deltax) > 0; 1609 } 1610 return infront; 1611} 1612 1613/** 1614 * Returns true if the worm will collide with the next move unless 1615 * it changes its direction. 1616 * @param struct worm *w - The worm to be investigated. 1617 * @return Returns true if the worm will collide with the next move 1618 * unless it changes its direction. 1619 */ 1620static bool will_worm_collide(struct worm *w) 1621{ 1622 int x = w->x[w->head] + w->dirx; 1623 int y = w->y[w->head] + w->diry; 1624 bool retVal = !is_in_field_rect(x, y); 1625 if (!retVal) { 1626 retVal = (argh_collision(x, y) != -1); 1627 } 1628 1629 if (!retVal) { 1630 retVal = (worm_collision(w, x, y) != NULL); 1631 } 1632 return retVal; 1633} 1634 1635/** 1636 * This function 1637 * may be used to be stored in worm.fetch_worm_direction for 1638 * worms that are not controlled by humans but by artificial stupidity. 1639 * A direction is searched that doesn't lead to collision but to the nearest 1640 * food - but not very intelligent. The direction is written to the specified 1641 * worm. 1642 * @param struct worm *w - The worm of which the direction 1643 * is altered. 1644 */ 1645static void virtual_player(struct worm *w) 1646{ 1647 bool isright; 1648 int plana, planb, planc; 1649 /* find the next lunch */ 1650 int nearestfood = get_nearest_food(w->x[w->head], w->y[w->head]); 1651 1652 /* determine in which direction it is */ 1653 1654 /* in front of me? */ 1655 bool infront = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]); 1656 1657 /* left right of me? */ 1658 int olddir = get_worm_dir(w); 1659 set_worm_dir(w, (olddir + 1) % 4); 1660 isright = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]); 1661 set_worm_dir(w, olddir); 1662 1663 /* detect situation, set strategy */ 1664 if (infront) { 1665 if (isright) { 1666 plana = olddir; 1667 planb = (olddir + 1) % 4; 1668 planc = (olddir + 3) % 4; 1669 } else { 1670 plana = olddir; 1671 planb = (olddir + 3) % 4; 1672 planc = (olddir + 1) % 4; 1673 } 1674 } else { 1675 if (isright) { 1676 plana = (olddir + 1) % 4; 1677 planb = olddir; 1678 planc = (olddir + 3) % 4; 1679 } else { 1680 plana = (olddir + 3) % 4; 1681 planb = olddir; 1682 planc = (olddir + 1) % 4; 1683 } 1684 } 1685 1686 /* test for collision */ 1687 set_worm_dir(w, plana); 1688 if (will_worm_collide(w)){ 1689 1690 /* plan b */ 1691 set_worm_dir(w, planb); 1692 1693 /* test for collision */ 1694 if (will_worm_collide(w)) { 1695 1696 /* plan c */ 1697 set_worm_dir(w, planc); 1698 } 1699 } 1700} 1701 1702/** 1703 * prints out the score board with all the status information 1704 * about the game. 1705 */ 1706static void score_board(void) 1707{ 1708 int i; 1709 int y = 0; 1710 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); 1711 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, 0, 1712 LCD_WIDTH - FIELD_RECT_WIDTH - 2, LCD_HEIGHT); 1713 rb->lcd_set_drawmode(DRMODE_SOLID); 1714 for (i = 0; i < worm_count; i++) { 1715 int score = get_score(&worms[i]); 1716 int collision = check_collision(&worms[i]); 1717 const char *state_str; 1718 1719 /* high score */ 1720 if (worms[i].fetch_worm_direction != virtual_player){ 1721 if (highscore < score) { 1722 highscore = score; 1723 } 1724 } 1725 1726 /* worm state */ 1727 if (collision == COLLISION_NONE) { 1728 if (worms[i].growing > 0) 1729 state_str = "Growing"; 1730 else { 1731 state_str = worms[i].alive ? "Hungry" : "Wormed"; 1732 } 1733 } else { 1734 state_str = state_desc[collision]; 1735 } 1736 1737 /* length */ 1738 rb->lcd_putsxyf(FIELD_RECT_WIDTH + 3, y , "Len:%d", score); 1739 rb->lcd_putsxyf(FIELD_RECT_WIDTH + 3, y+8, state_str); 1740 1741 if (!worms[i].alive){ 1742 rb->lcd_set_drawmode(DRMODE_COMPLEMENT); 1743 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, y, 1744 LCD_WIDTH - FIELD_RECT_WIDTH - 2, 17); 1745 rb->lcd_set_drawmode(DRMODE_SOLID); 1746 } 1747 y += 19; 1748 } 1749#ifdef DEBUG_WORMLET 1750 rb->lcd_putsxyf(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, "ticks %d", max_cycle); 1751#else 1752 rb->lcd_putsxyf(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, "Hs: %d", highscore); 1753#endif 1754} 1755 1756/** 1757 * Checks for collisions of the worm and its environment and 1758 * takes appropriate actions like growing the worm or killing it. 1759 * @return bool Returns true if the worm is dead. Returns 1760 * false if the worm is healthy, up and creeping. 1761 */ 1762static bool process_collisions(struct worm *w) 1763{ 1764 int index = -1; 1765 1766 w->alive &= !field_collision(w); 1767 1768 if (w->alive) { 1769 1770 /* check if food was eaten */ 1771 index = food_collision(w->x[w->head], w->y[w->head]); 1772 if (index != -1){ 1773 int i; 1774 1775 clear_food(index); 1776 make_food(index); 1777 draw_food(index); 1778 1779 for (i = 0; i < arghs_per_food; i++) { 1780 argh_count++; 1781 if (argh_count > MAX_ARGH) 1782 argh_count = MAX_ARGH; 1783 make_argh(argh_count - 1); 1784 draw_argh(argh_count - 1); 1785 } 1786 1787 add_growing(w, worm_food); 1788 1789 draw_worm(w); 1790 } 1791 1792 /* check if argh was eaten */ 1793 else { 1794 index = argh_collision(w->x[w->head], w->y[w->head]); 1795 if (index != -1) { 1796 w->alive = false; 1797 } 1798 else { 1799 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) { 1800 w->alive = false; 1801 } 1802 } 1803 } 1804 } 1805 return !w->alive; 1806} 1807 1808/** 1809 * The main loop of the game. 1810 * @return bool Returns true if the game ended 1811 * with a dead worm. Returns false if the user 1812 * aborted the game manually. 1813 */ 1814static int run(void) 1815{ 1816 int button = 0; 1817 int wormDead = false; 1818 bool paused = false; 1819 1820 /* ticks are counted to compensate speed variations */ 1821 long cycle_start = 0, cycle_end = 0; 1822#ifdef DEBUG_WORMLET 1823 int ticks_to_max_cycle_reset = 20; 1824 max_cycle = 0; 1825#endif 1826 1827 /* initialize the board and so on */ 1828 init_wormlet(); 1829 1830 cycle_start = *rb->current_tick; 1831 /* change the direction of the worm */ 1832 while (!wormDead) 1833 { 1834 int i; 1835 long cycle_duration=0; 1836 1837#ifdef HAS_BUTTON_HOLD 1838 if (rb->button_hold()) 1839 paused = true; 1840#endif 1841 1842 switch (button) { 1843 case BTN_STARTPAUSE: 1844 paused = !paused; 1845 break; 1846 case BTN_STOPRESET: 1847 if (paused) 1848 return 1; /* restart game */ 1849 else 1850 paused = true; 1851 break; 1852#ifdef BTN_RC_QUIT 1853 case BTN_RC_QUIT: 1854#endif 1855 case BTN_QUIT: 1856 return 2; /* back to menu */ 1857 break; 1858 } 1859 if (!paused) 1860 { 1861 switch (button) { 1862 case BTN_DIR_UP: 1863 if (players == 1 && !use_remote) { 1864 player1_dir = NORTH; 1865 } 1866 break; 1867 1868 case BTN_DIR_DOWN: 1869 if (players == 1 && !use_remote) { 1870 player1_dir = SOUTH; 1871 } 1872 break; 1873 1874 case BTN_DIR_LEFT: 1875 if (players != 1 || use_remote) { 1876 player1_dir = (player1_dir + 3) % 4; 1877 } else { 1878 player1_dir = WEST; 1879 } 1880 break; 1881 1882 case BTN_DIR_RIGHT: 1883 if (players != 1 || use_remote) { 1884 player1_dir = (player1_dir + 1) % 4; 1885 } else { 1886 player1_dir = EAST; 1887 } 1888 break; 1889 1890#ifdef MULTIPLAYER 1891 case BTN_PLAYER2_DIR1: 1892 player2_dir = (player2_dir + 3) % 4; 1893 break; 1894 1895 case BTN_PLAYER2_DIR2: 1896 player2_dir = (player2_dir + 1) % 4; 1897 break; 1898#endif 1899 1900#ifdef REMOTE 1901 case BTN_RC_UP: 1902 player3_dir = (player3_dir + 1) % 4; 1903 break; 1904 1905 case BTN_RC_DOWN: 1906 player3_dir = (player3_dir + 3) % 4; 1907 break; 1908#endif 1909 } 1910 1911 1912 for (i = 0; i < worm_count; i++) { 1913 worms[i].fetch_worm_direction(&worms[i]); 1914 } 1915 1916 wormDead = true; 1917 for (i = 0; i < worm_count; i++){ 1918 struct worm *w = &worms[i]; 1919 move_worm(w); 1920 wormDead &= process_collisions(w); 1921 draw_worm(w); 1922 } 1923 score_board(); 1924 rb->lcd_update(); 1925 if (button == BTN_STOPRESET) { 1926 wormDead = true; 1927 } 1928 1929 /* here the wormlet game cycle ends 1930 thus the current tick is stored 1931 as end time */ 1932 cycle_end = *rb->current_tick; 1933 1934 /* The duration of the game cycle */ 1935 cycle_duration = cycle_end - cycle_start; 1936 cycle_duration = MAX(0, cycle_duration); 1937 cycle_duration = MIN(speed -1, cycle_duration); 1938 1939 1940#ifdef DEBUG_WORMLET 1941 ticks_to_max_cycle_reset--; 1942 if (ticks_to_max_cycle_reset <= 0) { 1943 max_cycle = 0; 1944 } 1945 1946 if (max_cycle < cycle_duration) { 1947 max_cycle = cycle_duration; 1948 ticks_to_max_cycle_reset = 20; 1949 } 1950#endif 1951 } 1952 /* adjust the number of ticks to wait for a button. 1953 This ensures that a complete game cycle including 1954 user input runs in constant time */ 1955 button = rb->button_get_w_tmo(speed - cycle_duration); 1956 cycle_start = *rb->current_tick; 1957 } 1958 1959 rb->splash(HZ*2, "Game Over!"); 1960 1961 return 2; /* back to menu */ 1962} 1963 1964#ifdef DEBUG_WORMLET 1965 1966/** 1967 * Just a test routine that checks that worm_food_collision works 1968 * in some typical situations. 1969 */ 1970static void test_worm_food_collision(void) 1971{ 1972 int collision_count = 0; 1973 int i; 1974 rb->lcd_clear_display(); 1975 init_worm(&worms[0], 10, 10); 1976 add_growing(&worms[0], 10); 1977 set_worm_dir(&worms[0], EAST); 1978 for (i = 0; i < 10; i++) { 1979 move_worm(&worms[0]); 1980 draw_worm(&worms[0]); 1981 } 1982 1983 set_worm_dir(&worms[0], SOUTH); 1984 for (i = 0; i < 10; i++) { 1985 move_worm(&worms[0]); 1986 draw_worm(&worms[0]); 1987 } 1988 1989 foodx[0] = 15; 1990 foody[0] = 12; 1991 for (foody[0] = 20; foody[0] > 0; foody[0] --) { 1992 bool collision; 1993 draw_worm(&worms[0]); 1994 draw_food(0); 1995 collision = worm_food_collision(&worms[0], 0); 1996 if (collision) { 1997 collision_count++; 1998 } 1999 rb->lcd_putsxyf(0, LCD_HEIGHT -8, "collisions: %d", collision_count); 2000 rb->lcd_update(); 2001 } 2002 if (collision_count != food_size) { 2003 rb->button_get(true); 2004 } 2005 2006 2007 foody[0] = 15; 2008 for (foodx[0] = 30; foodx[0] > 0; foodx[0] --) { 2009 bool collision; 2010 draw_worm(&worms[0]); 2011 draw_food(0); 2012 collision = worm_food_collision(&worms[0], 0); 2013 if (collision) { 2014 collision_count ++; 2015 } 2016 rb->lcd_putsxyf(0, LCD_HEIGHT -8, "collisions: %d", collision_count); 2017 rb->lcd_update(); 2018 } 2019 if (collision_count != food_size * 2) { 2020 rb->button_get(true); 2021 } 2022 2023} 2024 2025static bool expensive_worm_in_rect(struct worm *w, int rx, int ry, int rw, int rh) 2026{ 2027 int x, y; 2028 bool retVal = false; 2029 for (x = rx; x < rx + rw; x++){ 2030 for (y = ry; y < ry + rh; y++) { 2031 if (specific_worm_collision(w, x, y) != -1) { 2032 retVal = true; 2033 } 2034 } 2035 } 2036 return retVal; 2037} 2038 2039static void test_worm_argh_collision(void) 2040{ 2041 int i; 2042 int dir; 2043 int collision_count = 0; 2044 rb->lcd_clear_display(); 2045 init_worm(&worms[0], 10, 10); 2046 add_growing(&worms[0], 40); 2047 for (dir = 0; dir < 4; dir++) { 2048 set_worm_dir(&worms[0], (EAST + dir) % 4); 2049 for (i = 0; i < 10; i++) { 2050 move_worm(&worms[0]); 2051 draw_worm(&worms[0]); 2052 } 2053 } 2054 2055 arghx[0] = 12; 2056 for (arghy[0] = 0; arghy[0] < FIELD_RECT_HEIGHT - argh_size; arghy[0]++){ 2057 bool collision; 2058 draw_argh(0); 2059 collision = worm_argh_collision(&worms[0], 0); 2060 if (collision) { 2061 collision_count ++; 2062 } 2063 rb->lcd_putsxyf(0, LCD_HEIGHT -8, "collisions: %d", collision_count); 2064 rb->lcd_update(); 2065 } 2066 if (collision_count != argh_size * 2) { 2067 rb->button_get(true); 2068 } 2069 2070 arghy[0] = 12; 2071 for (arghx[0] = 0; arghx[0] < FIELD_RECT_HEIGHT - argh_size; arghx[0]++){ 2072 bool collision; 2073 draw_argh(0); 2074 collision = worm_argh_collision(&worms[0], 0); 2075 if (collision) { 2076 collision_count ++; 2077 } 2078 rb->lcd_putsxyf(0, LCD_HEIGHT -8, "collisions: %d", collision_count); 2079 rb->lcd_update(); 2080 } 2081 if (collision_count != argh_size * 4) { 2082 rb->button_get(true); 2083 } 2084} 2085 2086static int testline_in_rect(void) 2087{ 2088 int testfailed = -1; 2089 2090 int rx = 10; 2091 int ry = 15; 2092 int rw = 20; 2093 int rh = 25; 2094 2095 /* Test 1 */ 2096 int x1 = 12; 2097 int y1 = 8; 2098 int x2 = 12; 2099 int y2 = 42; 2100 2101 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && 2102 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { 2103 rb->lcd_drawrect(rx, ry, rw, rh); 2104 rb->lcd_drawline(x1, y1, x2, y2); 2105 rb->lcd_update(); 2106 rb->lcd_putsxy(0, 0, "failed 1"); 2107 rb->button_get(true); 2108 testfailed = 1; 2109 } 2110 2111 /* test 2 */ 2112 y2 = 20; 2113 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && 2114 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { 2115 rb->lcd_drawrect(rx, ry, rw, rh); 2116 rb->lcd_drawline(x1, y1, x2, y2); 2117 rb->lcd_putsxy(0, 0, "failed 2"); 2118 rb->lcd_update(); 2119 rb->button_get(true); 2120 testfailed = 2; 2121 } 2122 2123 /* test 3 */ 2124 y1 = 30; 2125 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && 2126 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { 2127 rb->lcd_drawrect(rx, ry, rw, rh); 2128 rb->lcd_drawline(x1, y1, x2, y2); 2129 rb->lcd_putsxy(0, 0, "failed 3"); 2130 rb->lcd_update(); 2131 rb->button_get(true); 2132 testfailed = 3; 2133 } 2134 2135 /* test 4 */ 2136 y2 = 45; 2137 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && 2138 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { 2139 rb->lcd_drawrect(rx, ry, rw, rh); 2140 rb->lcd_drawline(x1, y1, x2, y2); 2141 rb->lcd_putsxy(0, 0, "failed 4"); 2142 rb->lcd_update(); 2143 rb->button_get(true); 2144 testfailed = 4; 2145 } 2146 2147 /* test 5 */ 2148 y1 = 50; 2149 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) || 2150 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { 2151 rb->lcd_drawrect(rx, ry, rw, rh); 2152 rb->lcd_drawline(x1, y1, x2, y2); 2153 rb->lcd_putsxy(0, 0, "failed 5"); 2154 rb->lcd_update(); 2155 rb->button_get(true); 2156 testfailed = 5; 2157 } 2158 2159 /* test 6 */ 2160 y1 = 5; 2161 y2 = 7; 2162 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) || 2163 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { 2164 rb->lcd_drawrect(rx, ry, rw, rh); 2165 rb->lcd_drawline(x1, y1, x2, y2); 2166 rb->lcd_putsxy(0, 0, "failed 6"); 2167 rb->lcd_update(); 2168 rb->button_get(true); 2169 testfailed = 6; 2170 } 2171 2172 /* test 7 */ 2173 x1 = 8; 2174 y1 = 20; 2175 x2 = 35; 2176 y2 = 20; 2177 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && 2178 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { 2179 rb->lcd_drawrect(rx, ry, rw, rh); 2180 rb->lcd_drawline(x1, y1, x2, y2); 2181 rb->lcd_putsxy(0, 0, "failed 7"); 2182 rb->lcd_update(); 2183 rb->button_get(true); 2184 testfailed = 7; 2185 } 2186 2187 /* test 8 */ 2188 x2 = 12; 2189 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && 2190 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { 2191 rb->lcd_drawrect(rx, ry, rw, rh); 2192 rb->lcd_drawline(x1, y1, x2, y2); 2193 rb->lcd_putsxy(0, 0, "failed 8"); 2194 rb->lcd_update(); 2195 rb->button_get(true); 2196 testfailed = 8; 2197 } 2198 2199 /* test 9 */ 2200 x1 = 25; 2201 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && 2202 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { 2203 rb->lcd_drawrect(rx, ry, rw, rh); 2204 rb->lcd_drawline(x1, y1, x2, y2); 2205 rb->lcd_putsxy(0, 0, "failed 9"); 2206 rb->lcd_update(); 2207 rb->button_get(true); 2208 testfailed = 9; 2209 } 2210 2211 /* test 10 */ 2212 x2 = 37; 2213 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && 2214 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { 2215 rb->lcd_drawrect(rx, ry, rw, rh); 2216 rb->lcd_drawline(x1, y1, x2, y2); 2217 rb->lcd_putsxy(0, 0, "failed 10"); 2218 rb->lcd_update(); 2219 rb->button_get(true); 2220 testfailed = 10; 2221 } 2222 2223 /* test 11 */ 2224 x1 = 42; 2225 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) || 2226 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { 2227 rb->lcd_drawrect(rx, ry, rw, rh); 2228 rb->lcd_drawline(x1, y1, x2, y2); 2229 rb->lcd_putsxy(0, 0, "failed 11"); 2230 rb->lcd_update(); 2231 rb->button_get(true); 2232 testfailed = 11; 2233 } 2234 2235 /* test 12 */ 2236 x1 = 5; 2237 x2 = 7; 2238 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) || 2239 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { 2240 rb->lcd_drawrect(rx, ry, rw, rh); 2241 rb->lcd_drawline(x1, y1, x2, y2); 2242 rb->lcd_putsxy(0, 0, "failed 12"); 2243 rb->lcd_update(); 2244 rb->button_get(true); 2245 testfailed = 12; 2246 } 2247 2248 /* test 13 */ 2249 rx = 9; 2250 ry = 15; 2251 rw = food_size; 2252 rh = food_size; 2253 2254 x1 = 10; 2255 y1 = 10; 2256 x2 = 10; 2257 y2 = 20; 2258 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && 2259 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) { 2260 rb->lcd_drawrect(rx, ry, rw, rh); 2261 rb->lcd_drawline(x1, y1, x2, y2); 2262 rb->lcd_putsxy(0, 0, "failed 13"); 2263 rb->lcd_update(); 2264 rb->button_get(true); 2265 testfailed = 13; 2266 } 2267 2268 /* test 14 */ 2269 rx = 9; 2270 ry = 15; 2271 rw = 4; 2272 rh = 4; 2273 2274 x1 = 10; 2275 y1 = 10; 2276 x2 = 10; 2277 y2 = 19; 2278 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && 2279 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) { 2280 rb->lcd_drawline(x1, y1, x2, y2); 2281 rb->lcd_invertrect(rx, ry, rw, rh); 2282 rb->lcd_putsxy(0, 0, "failed 14"); 2283 rb->lcd_update(); 2284 rb->button_get(true); 2285 testfailed = 14; 2286 } 2287 2288 rb->lcd_clear_display(); 2289 2290 return testfailed; 2291} 2292 2293/** 2294 * Just a test routine to test wether specific_worm_collision might work properly 2295 */ 2296static int test_specific_worm_collision(void) 2297{ 2298 int collisions = 0; 2299 int dir; 2300 int x = 0; 2301 int y = 0; 2302 rb->lcd_clear_display(); 2303 init_worm(&worms[0], 10, 20); 2304 add_growing(&worms[0], 20 - INITIAL_WORM_LENGTH); 2305 2306 for (dir = EAST; dir < EAST + 4; dir++) { 2307 int i; 2308 set_worm_dir(&worms[0], dir % 4); 2309 for (i = 0; i < 5; i++) { 2310 if (!(dir % 4 == NORTH && i == 9)) { 2311 move_worm(&worms[0]); 2312 draw_worm(&worms[0]); 2313 } 2314 } 2315 } 2316 2317 for (y = 15; y < 30; y ++){ 2318 for (x = 5; x < 20; x++) { 2319 if (specific_worm_collision(&worms[0], x, y) != -1) { 2320 collisions ++; 2321 } 2322 rb->lcd_invertpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y); 2323 rb->lcd_putsxyf(0, LCD_HEIGHT - 8, "collisions %d", collisions); 2324 rb->lcd_update(); 2325 } 2326 } 2327 if (collisions != 21) { 2328 rb->button_get(true); 2329 } 2330 return collisions; 2331} 2332 2333static void test_make_argh(void) 2334{ 2335 int dir; 2336 int seed = 0; 2337 int hit = 0; 2338 int failures = 0; 2339 int last_failures = 0; 2340 int i, worm_idx; 2341 rb->lcd_clear_display(); 2342 worm_count = 3; 2343 2344 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) { 2345 init_worm(&worms[worm_idx], 10 + worm_idx * 20, 20); 2346 add_growing(&worms[worm_idx], 40 - INITIAL_WORM_LENGTH); 2347 } 2348 2349 for (dir = EAST; dir < EAST + 4; dir++) { 2350 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) { 2351 set_worm_dir(&worms[worm_idx], dir % 4); 2352 for (i = 0; i < 10; i++) { 2353 if (!(dir % 4 == NORTH && i == 9)) { 2354 move_worm(&worms[worm_idx]); 2355 draw_worm(&worms[worm_idx]); 2356 } 2357 } 2358 } 2359 } 2360 2361 rb->lcd_update(); 2362 2363 for (seed = 0; hit < 20; seed += 2) { 2364 int x, y; 2365 rb->srand(seed); 2366 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size); 2367 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size); 2368 2369 for (worm_idx = 0; worm_idx < worm_count; worm_idx++){ 2370 if (expensive_worm_in_rect(&worms[worm_idx], x, y, argh_size, argh_size)) { 2371 int tries = 0; 2372 rb->srand(seed); 2373 2374 tries = make_argh(0); 2375 if ((x == arghx[0] && y == arghy[0]) || tries < 2) { 2376 failures ++; 2377 } 2378 2379 rb->lcd_putsxyf(0, LCD_HEIGHT - 8, "(%d;%d) fail%d try%d", 2380 x, y, failures, tries); 2381 rb->lcd_update(); 2382 rb->lcd_invertrect(x + FIELD_RECT_X, y+ FIELD_RECT_Y, 2383 argh_size, argh_size); 2384 rb->lcd_update(); 2385 draw_argh(0); 2386 rb->lcd_update(); 2387 rb->lcd_invertrect(x + FIELD_RECT_X, y + FIELD_RECT_Y, 2388 argh_size, argh_size); 2389 rb->lcd_clearrect(arghx[0] + FIELD_RECT_X, arghy[0] + FIELD_RECT_Y, 2390 argh_size, argh_size); 2391 2392 if (failures > last_failures) { 2393 rb->button_get(true); 2394 } 2395 last_failures = failures; 2396 hit ++; 2397 } 2398 } 2399 } 2400} 2401 2402static void test_worm_argh_collision_in_moves(void) { 2403 int hit_count = 0; 2404 int i; 2405 rb->lcd_clear_display(); 2406 init_worm(&worms[0], 10, 20); 2407 2408 arghx[0] = 20; 2409 arghy[0] = 18; 2410 draw_argh(0); 2411 2412 set_worm_dir(&worms[0], EAST); 2413 for (i = 0; i < 20; i++) { 2414 move_worm(&worms[0]); 2415 draw_worm(&worms[0]); 2416 if (worm_argh_collision_in_moves(&worms[0], 0, 5)){ 2417 hit_count ++; 2418 } 2419 rb->lcd_putsxyf(0, LCD_HEIGHT - 8, "in 5 moves hits: %d", hit_count); 2420 rb->lcd_update(); 2421 } 2422 if (hit_count != argh_size + 5) { 2423 rb->button_get(true); 2424 } 2425} 2426#endif /* DEBUG_WORMLET */ 2427 2428/* 2429 * Reverts default settings 2430 */ 2431static void default_settings(void) 2432{ 2433 arghs_per_food = ARGHS_PER_FOOD; 2434 argh_size = ARGH_SIZE; 2435 food_size = FOOD_SIZE; 2436 speed = SPEED; 2437 worm_food = WORM_PER_FOOD; 2438 players = 1; 2439 worm_count = MAX_WORMS; 2440 use_remote = false; 2441 return; 2442} 2443 2444/* 2445 * Launches the wormlet game 2446 */ 2447static bool launch_wormlet(void) 2448{ 2449 int game_result = 1; 2450 2451 rb->lcd_clear_display(); 2452 2453 /* Turn off backlight timeout */ 2454 backlight_ignore_timeout(); 2455 2456 /* start the game */ 2457 while (game_result == 1) 2458 game_result = run(); 2459 2460 switch (game_result) 2461 { 2462 case 2: 2463 2464 /* Turn on backlight timeout (revert to settings) */ 2465 backlight_use_settings(); 2466 2467 return false; 2468 break; 2469 } 2470 return false; 2471} 2472 2473/** 2474 * Main entry point 2475 */ 2476enum plugin_status plugin_start(const void* parameter) 2477{ 2478 int result; 2479 int menu_quit = 0; 2480 int new_setting; 2481 2482 (void)(parameter); 2483 2484 default_settings(); 2485 if (configfile_load(SETTINGS_FILENAME, config, 2486 sizeof(config)/sizeof(*config), 2487 SETTINGS_MIN_VERSION ) < 0) 2488 { 2489 /* If the loading failed, save a new config file (as the disk is 2490 already spinning) */ 2491 configfile_save(SETTINGS_FILENAME, config, 2492 sizeof(config)/sizeof(*config), 2493 SETTINGS_VERSION); 2494 } 2495 2496#ifdef HAVE_LCD_COLOR 2497 rb->lcd_set_foreground(COLOR_FG); 2498 rb->lcd_set_background(COLOR_BG); 2499#endif 2500 2501#if LCD_DEPTH > 1 2502 rb->lcd_set_backdrop(NULL); 2503#endif 2504 2505#ifdef DEBUG_WORMLET 2506 testline_in_rect(); 2507 test_worm_argh_collision_in_moves(); 2508 test_make_argh(); 2509 test_worm_food_collision(); 2510 test_worm_argh_collision(); 2511 test_specific_worm_collision(); 2512#endif 2513 2514 /* Setup screen */ 2515 2516 static const struct opt_items noyes[2] = { 2517 { STR(LANG_SET_BOOL_NO) }, 2518 { STR(LANG_SET_BOOL_YES) }, 2519 }; 2520 2521 static const struct opt_items remoteonly_option[1] = { 2522 { STR(LANG_REMOTE_CONTROL) } 2523 }; 2524 2525 static const struct opt_items key24_option[2] = { 2526 { STR(LANG_4_KEY_CONTROL) }, 2527 { STR(LANG_2_KEY_CONTROL) } 2528 }; 2529 2530#ifdef REMOTE 2531 static const struct opt_items remote_option[2] = { 2532 { STR(LANG_REMOTE_CONTROL) }, 2533 { STR(LANG_NO_REM_CONTROL) } 2534 }; 2535#else 2536 static const struct opt_items key2_option[1] = { 2537 { STR(LANG_2_KEY_CONTROL) } 2538 }; 2539#endif 2540 2541 static const struct opt_items nokey_option[1] = { 2542 { STR(LANG_OUT_OF_CONTROL) } 2543 }; 2544 2545 MENUITEM_STRINGLIST(menu, "Wormlet Menu", NULL, 2546 ID2P(LANG_PLAY_WORMLET), ID2P(LANG_NUMBER_OF_WORMS), 2547 ID2P(LANG_NUMBER_OF_PLAYERS), ID2P(LANG_CONTROL_STYLE), 2548 ID2P(LANG_WORM_GROWTH_PER_FOOD), ID2P(LANG_WORM_SPEED), 2549 ID2P(LANG_ARGHS_PER_FOOD), ID2P(LANG_ARGH_SIZE), 2550 ID2P(LANG_FOOD_SIZE), ID2P(LANG_REVERT_TO_DEFAULT_SETTINGS), 2551 ID2P(LANG_PLAYBACK_CONTROL), ID2P(LANG_MENU_QUIT)); 2552 2553 rb->button_clear_queue(); 2554 2555 while (!menu_quit) { 2556 switch(rb->do_menu(&menu, &result, NULL, false)) 2557 { 2558 case 0: 2559 rb->lcd_setfont(FONT_SYSFIXED); 2560 launch_wormlet(); 2561 break; 2562 case 1: 2563 rb->set_int(rb->str(LANG_NUMBER_OF_WORMS), "", UNIT_INT, &worm_count, NULL, 2564 1, 1, 3, NULL); 2565 if (worm_count < players) { 2566 worm_count = players; 2567 } 2568 break; 2569 case 2: 2570#ifdef MULTIPLAYER 2571 rb->set_int(rb->str(LANG_NUMBER_OF_PLAYERS), "", UNIT_INT, &players, NULL, 2572 1, 0, 4, NULL); 2573#else 2574 rb->set_int(rb->str(LANG_NUMBER_OF_PLAYERS), "", UNIT_INT, &players, NULL, 2575 1, 0, 2, NULL); 2576#endif 2577 if (players > worm_count) { 2578 worm_count = players; 2579 } 2580 if (players > 2) { 2581 use_remote = true; 2582 } 2583 break; 2584 case 3: 2585 switch(players) { 2586 case 0: 2587 rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote, RB_INT, 2588 nokey_option, 1, NULL); 2589 break; 2590 case 1: 2591 rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote, RB_INT, 2592 key24_option, 2, NULL); 2593 break; 2594 case 2: 2595#ifdef REMOTE 2596 rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote, RB_INT, 2597 remote_option, 2, NULL); 2598#else 2599 rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote, RB_INT, 2600 key2_option, 1, NULL); 2601#endif 2602 break; 2603 case 3: 2604 rb->set_option(rb->str(LANG_CONTROL_STYLE),&use_remote, RB_INT, 2605 remoteonly_option, 1, NULL); 2606 break; 2607 } 2608 break; 2609 case 4: 2610 rb->set_int(rb->str(LANG_WORM_GROWTH_PER_FOOD), "", UNIT_INT, &worm_food, 2611 NULL, 1, 0, 15, NULL); 2612 break; 2613 case 5: 2614 new_setting = 20 - speed; 2615 rb->set_int(rb->str(LANG_WORM_SPEED), "", UNIT_INT, &new_setting, 2616 NULL, 1, 0, 20, NULL); 2617 speed = 20 - new_setting; 2618 break; 2619 case 6: 2620 rb->set_int(rb->str(LANG_ARGHS_PER_FOOD), "", UNIT_INT, &arghs_per_food, 2621 NULL, 1, 0, 8, NULL); 2622 break; 2623 case 7: 2624 rb->set_int(rb->str(LANG_ARGH_SIZE), "", UNIT_INT, &argh_size, 2625 NULL, 1, 2, 10, NULL); 2626 break; 2627 case 8: 2628 rb->set_int(rb->str(LANG_FOOD_SIZE), "", UNIT_INT, &food_size, 2629 NULL, 1, 2, 10, NULL); 2630 break; 2631 case 9: 2632 new_setting = 0; 2633 rb->set_option(rb->str(LANG_RESET), &new_setting, RB_INT, noyes , 2, NULL); 2634 if (new_setting == 1) 2635 default_settings(); 2636 break; 2637 case 10: 2638 playback_control(NULL); 2639 break; 2640 default: 2641 menu_quit=1; 2642 break; 2643 } 2644 } 2645 2646 configfile_save(SETTINGS_FILENAME, config, 2647 sizeof(config)/sizeof(*config), 2648 SETTINGS_VERSION); 2649 2650 return PLUGIN_OK; 2651}