// Download this in a .zip file.
/* Camel Converted/Reimagined for the Freetronics 16x2 LCD shield by eturnerx@gmail.com 201211 http://eturnerx.com Based info found here: http://www.atariarchives.org/morebasicgames/showpage.php?page=24 Original game by Heath Users Group http://www.freetronics.com/pages/16x2-lcd-shield-quickstart-guide#.UBKMm7T9OK9 */ /*-------------------------------------------------------------------------------------- Includes --------------------------------------------------------------------------------------*/ #include// include LCD library #include // put the strings into flash RAM - SRAM is filling up! /*-------------------------------------------------------------------------------------- Defines --------------------------------------------------------------------------------------*/ // Pins in use #define BUTTON_ADC_PIN A0 // A0 is the button ADC input #define LCD_BACKLIGHT_PIN 3 // D3 controls LCD backlight // ADC readings expected for the 5 buttons on the ADC input #define RIGHT_10BIT_ADC 0 // right #define UP_10BIT_ADC 145 // up #define DOWN_10BIT_ADC 329 // down #define LEFT_10BIT_ADC 505 // left #define SELECT_10BIT_ADC 741 // right #define BUTTONHYSTERESIS 10 // hysteresis for valid button sensing window //return values for ReadButtons() #define BUTTON_NONE 0 // #define BUTTON_RIGHT 1 // #define BUTTON_UP 2 // #define BUTTON_DOWN 3 // #define BUTTON_LEFT 4 // #define BUTTON_SELECT 5 // // Define our states #define STATE_TITLE 00 // A title screen #define STATE_KEYWAIT 10 // Waiting for a keypress #define STATE_NEWGAME 30 #define STATE_NEWTURN 40 #define STATE_PLAYERMOVE 50 #define STATE_DRINK 60 #define STATE_WALK 70 #define STATE_RUN 80 #define STATE_STOP 90 #define STATE_STATUS 100 #define STATE_HOPE 110 #define STATE_CAPTUREDMOVE 120 #define STATE_GAMEOVERWON 250 #define STATE_GAMEOVERLOST 251 // Misc. Defines #define MSGFLASHDELAY 1250 // How long to flash messages for (ms)? /*-------------------------------------------------------------------------------------- String Table --------------------------------------------------------------------------------------*/ #define STR_CAMEL 6 // 1234567890123456 prog_char string_0[] PROGMEM = "hope DRINK_ walk"; prog_char string_1[] PROGMEM = "drink _WALK_ run"; prog_char string_2[] PROGMEM = "walk _RUN__ stop"; prog_char string_3[] PROGMEM = "run _STOP_ stats"; prog_char string_4[] PROGMEM = "stop STATS_ hope"; prog_char string_5[] PROGMEM = "stats HOPE drink"; prog_char string_6[] PROGMEM = "CAMEL - HEATH UG"; prog_char string_7[] PROGMEM = "-- GAME OVER --"; prog_char string_8[] PROGMEM = "YOU WIN - PARTY!"; prog_char string_9[] PROGMEM = "Pygmies come too"; // 1234567890123456 prog_char string_10[] PROGMEM = "Sandstorm! Lost."; prog_char string_11[] PROGMEM = "Camel hurt hump"; prog_char string_12[] PROGMEM = "Died in desert"; prog_char string_13[] PROGMEM = "CamelUnion pro-"; prog_char string_14[] PROGMEM = "test at funeral"; prog_char string_15[] PROGMEM = "Eaten by vulture"; prog_char string_16[] PROGMEM = "and cannibals"; prog_char string_17[] PROGMEM = "Your skull's now"; prog_char string_18[] PROGMEM = "used as a purse"; prog_char string_19[] PROGMEM = "Dumb people:"; // 1234567890123456 prog_char string_20[] PROGMEM = "stay off camels"; prog_char string_21[] PROGMEM = "Turkeys fly not"; prog_char string_22[] PROGMEM = "ride camels."; prog_char string_23[] PROGMEM = "New camel&game?"; prog_char string_24[] PROGMEM = "Ran out of water"; //prog_char string_25[] PROGMEM = "Gone XXX miles."; prog_char string_25[] PROGMEM = ""; prog_char string_26[] PROGMEM = "At an oasis."; prog_char string_27[] PROGMEM = "Water refilled"; prog_char string_28[] PROGMEM = "You are Captured"; prog_char string_29[] PROGMEM = "held for ransom"; // 1234567890123456 prog_char string_30[] PROGMEM = "ESCAPE_ or wait"; prog_char string_31[] PROGMEM = "escape or _WAIT"; prog_char string_32[] PROGMEM = "Escape succeeded"; prog_char string_33[] PROGMEM = "Escape fail. You"; prog_char string_34[] PROGMEM = "were stabbed"; prog_char string_35[] PROGMEM = "Ransom paid!"; prog_char string_36[] PROGMEM = "Still waiting..."; prog_char string_37[] PROGMEM = "Resting..."; prog_char string_38[] PROGMEM = "Camel thanks you"; prog_char string_39[] PROGMEM = "Camel burns sand"; // 1234567890123456 prog_char string_40[] PROGMEM = "You ran poor old"; prog_char string_41[] PROGMEM = "camel to death"; prog_char string_42[] PROGMEM = "Camel likes walk"; prog_char string_43[] PROGMEM = "Hope finds you"; prog_char string_44[] PROGMEM = "Gives you water"; prog_char string_45[] PROGMEM = "Pygmies got you"; prog_char string_46[] PROGMEM = "Camel & You soup"; prog_char string_47[] PROGMEM = "-W A R N I N G -"; prog_char string_48[] PROGMEM = " Get a drink"; prog_char string_49[] PROGMEM = "You're at oasis"; // 1234567890123456 prog_char string_50[] PROGMEM = "desert allround"; prog_char string_51[] PROGMEM = "Good luck and"; prog_char string_52[] PROGMEM = " good cameling"; prog_char string_53[] PROGMEM = "You take a drink"; PROGMEM const char *string_table[] = { string_0, string_1, string_2, string_3, string_4, string_5, string_6, string_7, string_8, string_9, string_10, string_11, string_12, string_13, string_14, string_15, string_16, string_17, string_18, string_19, string_20, string_21, string_22, string_23, string_24, string_25, string_26, string_27, string_28, string_29, string_30, string_31, string_32, string_33, string_34, string_35, string_36, string_37, string_38, string_39, string_40, string_41, string_42, string_43, string_44, string_45, string_46, string_47, string_48, string_49, string_50, string_51, string_52, string_53 }; /*-------------------------------------------------------------------------------------- Variables --------------------------------------------------------------------------------------*/ byte buttonJustPressed = false; //this will be true after a ReadButtons() call if triggered byte buttonJustReleased = false; //this will be true after a ReadButtons() call if triggered byte buttonWas = BUTTON_NONE; //used by ReadButtons() for detection of button events // Setup the variables that will be used byte state, nextstate; int z; // thirst level int s; // drinks left in canteen byte c; // player distance travelled byte c1; // pygmies distance travelled byte q; // 0 if free, higher if captured for ransom byte f; // condition of the camel byte p; // pygmies can't capture in the first three turns byte choice; // Your move char stringbuffer[17]; int choicetostate[6] = { STATE_DRINK, STATE_WALK, STATE_RUN, STATE_STOP, STATE_STATUS, STATE_HOPE }; /*-------------------------------------------------------------------------------------- Init the LCD library with the LCD pins to be used --------------------------------------------------------------------------------------*/ LiquidCrystal lcd( 8, 9, 4, 5, 6, 7 ); //Pins for the freetronics 16x2 LCD shield. LCD: ( RS, E, LCD-D4, LCD-D5, LCD-D6, LCD-D7 ) /*-------------------------------------------------------------------------------------- setup() Called by the Arduino framework once, before the main loop begins --------------------------------------------------------------------------------------*/ void setup() { // Setup pins. pinMode(BUTTON_ADC_PIN, INPUT ); // Analogue input for the LCD's buttons digitalWrite(BUTTON_ADC_PIN, LOW ); pinMode(LCD_BACKLIGHT_PIN, OUTPUT ); // Backlight control (off/on) digitalWrite(LCD_BACKLIGHT_PIN, HIGH ); // Turn the backlight on randomSeed(analogRead(1)); // Send the random number generator lcd.begin( 16, 2 ); // Tell LCD library this is 16x2 display lcd.setCursor( 0, 0 ); // 1234567890123456 lcd.print(getString(STR_CAMEL)); lcd.setCursor( 0, 1 ); lcd.print( '.' ); lcd.print( '.' ); flashbacklight(); lcd.print( '.' ); flashbacklight(); lcd.print( '.' ); flashbacklight(); state = STATE_TITLE; } /*-------------------------------------------------------------------------------------- loop() Arduino main loop --------------------------------------------------------------------------------------*/ void loop() { byte button; button = ReadButtons(); switch(state) { // Do different things depending on "state" case STATE_TITLE: lcd.setCursor( 0, 0 ); // 1234567890123456 lcd.print(getString(STR_CAMEL)); pressanykey(STATE_NEWGAME); break; case STATE_KEYWAIT: if(buttonJustPressed) state = nextstate; break; case STATE_NEWGAME: z = 4; s = 6; c = c1 = q = f = p = 0; printscreen(49,50); printscreen(51,52); state = STATE_NEWTURN; // falls through case STATE_NEWTURN: if(c > 199) { state = STATE_GAMEOVERWON; break; } z--; p++; choice = 0; if(z == 1) printscreen(47,48); else if(z < 0) { printscreen(7,24); delay(MSGFLASHDELAY); state = STATE_GAMEOVERLOST; break; } if(q > 0) { lcd.clear(); lcd.print(getString(28)); lcd.setCursor(0,1); lcd.print(getString(30)); state = STATE_CAPTUREDMOVE; break; } if(p > 3) { c1 += random(2,13); if(c1 >= c) { printscreen(45,46); delay(MSGFLASHDELAY); state = STATE_GAMEOVERLOST; break; } } lcd.clear(); lcd.print("DIST:"); if(c < 100) lcd.print(' '); if(c < 10) lcd.print(' '); lcd.print(c); if(p > 3) { lcd.print(" P:"); if(c-c1 < 100) lcd.print(' '); if(c-c1 < 10) lcd.print(' '); lcd.print(c-c1); } lcd.setCursor(0,1); lcd.print(getString(choice)); state = STATE_PLAYERMOVE; // falls through... case STATE_PLAYERMOVE: if(buttonJustPressed) { switch(button) { case BUTTON_LEFT: if(choice == 0) choice = 5; else choice = choice - 1; break; case BUTTON_RIGHT: if(choice == 5) choice = 0; else choice = choice + 1; break; case BUTTON_SELECT: state = choicetostate[choice]; break; } } if(state == STATE_PLAYERMOVE) { lcd.setCursor(0,1); lcd.print(getString(choice)); } break; case STATE_DRINK: s = s - 1; z = 4; if(s < 0) { printscreen(7,24); delay(MSGFLASHDELAY); state = STATE_GAMEOVERLOST; break; } lcd.setCursor(0,1); lcd.print(getString(53)); delay(MSGFLASHDELAY); lcd.setCursor(0,1); state = STATE_PLAYERMOVE; break; case STATE_WALK: f++; if(f > 7) { printscreen(40,41); delay(MSGFLASHDELAY); state = STATE_GAMEOVERLOST; break; } movementevents(); if(q == 0) { lcd.clear(); lcd.print(getString(42)); c = c + random(10); delay(MSGFLASHDELAY); } state = STATE_NEWTURN; break; case STATE_RUN: f+=3; if(f > 7) { printscreen(40,41); delay(MSGFLASHDELAY); state = STATE_GAMEOVERLOST; break; } movementevents(); if(q == 0) { lcd.clear(); lcd.print(getString(39)); c = c + random(20); delay(MSGFLASHDELAY); } state = STATE_NEWTURN; break; case STATE_STOP: f = 0; printscreen(37,38); state = STATE_NEWTURN; break; case STATE_STATUS: lcd.setCursor(0,1); lcd.print("Camel "); lcd.print(f); lcd.print(" day left"); delay(MSGFLASHDELAY); lcd.setCursor(0,1); lcd.print("Drinks:"); lcd.print(s); lcd.print(" left "); delay(MSGFLASHDELAY); lcd.setCursor(0,1); lcd.print("Thirst:"); lcd.print(s); lcd.print(" actions"); delay(MSGFLASHDELAY); state = STATE_PLAYERMOVE; break; case STATE_HOPE: if(random(10)==4) { printscreen(43,44); s = 3, z = 0; state = STATE_NEWTURN; } else { printscreen(7,12); delay(MSGFLASHDELAY); state = STATE_GAMEOVERLOST; } break; case STATE_CAPTUREDMOVE: if(buttonJustPressed) { z = 4; // Captors provide drinking water switch(button) { case BUTTON_LEFT: case BUTTON_RIGHT: choice = 1 - choice; lcd.setCursor(0,1); lcd.print(getString(30 + choice)); break; case BUTTON_SELECT: if(choice == 0) { if(random(100) < 50) { lcd.clear(); lcd.print(getString(32)); delay(MSGFLASHDELAY); q = 0; state = STATE_NEWTURN; } else { printscreen(33,34); state = STATE_GAMEOVERLOST; } } else { if(random(100) < 75) { lcd.clear(); lcd.print(getString(35)); delay(MSGFLASHDELAY); q = 0; state = STATE_NEWTURN; } else { lcd.clear(); lcd.print(getString(36)); delay(MSGFLASHDELAY); state = STATE_NEWTURN; } } break; } } break; case STATE_GAMEOVERWON: printscreen(7,8); lcd.setCursor(0,1); lcd.print(getString(9)); nextstate = STATE_TITLE; state = STATE_KEYWAIT; break; case STATE_GAMEOVERLOST: choice = (byte)((random(0,5) * 2) + 13); printscreen(choice, choice + 1); lcd.clear(); lcd.print(getString(7)); lcd.setCursor(0,1); lcd.print(getString(23)); nextstate = STATE_TITLE; state = STATE_KEYWAIT; break; default: break; } //clear the buttonJustPressed & buttonJustReleased flags; they've already done their job if(buttonJustPressed) buttonJustPressed = false; if(buttonJustReleased) buttonJustReleased = false; delay(50); // Delay to stop buttons "jittering" false presses } void movementevents() { if(random(100)<5) { q = 1; printscreen(28,29); lcd.clear(); lcd.print(getString(29)); lcd.setCursor(0,1); lcd.print(getString(30)); state = STATE_NEWTURN; } else if(random(100)<20) { z = 4; s = 6; printscreen(26,27); } else if(random(100)<5) { lcd.clear(); lcd.print(getString(10)); delay(MSGFLASHDELAY); c += random(21 - 10); } else if(random(100)<5) { lcd.clear(); lcd.print(getString(11)); delay(MSGFLASHDELAY); c1++; } } void flashbacklight() { digitalWrite(LCD_BACKLIGHT_PIN, LOW); delay(150); digitalWrite(LCD_BACKLIGHT_PIN, HIGH); delay(150); } void pressanykey(byte _nextstate) { lcd.setCursor( 0, 1 ); // 1234567890123456 lcd.print( "Press [any] key " ); nextstate = _nextstate; state = STATE_KEYWAIT; } void printscreen(byte line1, byte line2) { lcd.clear(); lcd.print(getString(line1)); lcd.setCursor(0,1); lcd.print(getString(line2)); delay(MSGFLASHDELAY); } char *getString(int stringindex) { strcpy_P(stringbuffer, (char*)pgm_read_word(&(string_table[stringindex]))); return stringbuffer; } /*-------------------------------------------------------------------------------------- ReadButtons() Detect the button pressed and return the value Uses global values buttonWas, buttonJustPressed, buttonJustReleased. --------------------------------------------------------------------------------------*/ byte ReadButtons() { unsigned int buttonVoltage; byte button = BUTTON_NONE; // return no button pressed if the below checks don't write to btn //read the button ADC pin voltage buttonVoltage = analogRead( BUTTON_ADC_PIN ); //sense if the voltage falls within valid voltage windows if( buttonVoltage < ( RIGHT_10BIT_ADC + BUTTONHYSTERESIS ) ) { button = BUTTON_RIGHT; } else if( buttonVoltage >= ( UP_10BIT_ADC - BUTTONHYSTERESIS ) && buttonVoltage <= ( UP_10BIT_ADC + BUTTONHYSTERESIS ) ) { button = BUTTON_UP; } else if( buttonVoltage >= ( DOWN_10BIT_ADC - BUTTONHYSTERESIS ) && buttonVoltage <= ( DOWN_10BIT_ADC + BUTTONHYSTERESIS ) ) { button = BUTTON_DOWN; } else if( buttonVoltage >= ( LEFT_10BIT_ADC - BUTTONHYSTERESIS ) && buttonVoltage <= ( LEFT_10BIT_ADC + BUTTONHYSTERESIS ) ) { button = BUTTON_LEFT; } else if( buttonVoltage >= ( SELECT_10BIT_ADC - BUTTONHYSTERESIS ) && buttonVoltage <= ( SELECT_10BIT_ADC + BUTTONHYSTERESIS ) ) { button = BUTTON_SELECT; } //handle button flags for just pressed and just released events if( ( buttonWas == BUTTON_NONE ) && ( button != BUTTON_NONE ) ) { //the button was just pressed, set buttonJustPressed, this can optionally be used to trigger a once-off action for a button press event //it's the duty of the receiver to clear these flags if it wants to detect a new button change event buttonJustPressed = true; buttonJustReleased = false; } if( ( buttonWas != BUTTON_NONE ) && ( button == BUTTON_NONE ) ) { buttonJustPressed = false; buttonJustReleased = true; } //save the latest button value, for change event detection next time round buttonWas = button; return( button ); }