Version of Robotron arcade game using LPC1768, a Gameduino shield, a serial EEPROM (for high scores), two microswitch joysticks and two buttons plus a box to put it in. 20 levels of mayhem.
Dependencies: 25LCxxx_SPI CommonTypes Gameduino mbed
Revision 0:5fa232ee5fdf, committed 2013-06-04
- Comitter:
- RichardE
- Date:
- Tue Jun 04 20:16:33 2013 +0000
- Child:
- 1:dfd5eaaf96a3
- Commit message:
- Started conversion from Maple version of game. So far Gameduino seems to have been initialised OK and just displays a sign on message. Lots of commented out code.
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CommonTypes.lib Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,1 @@ +https://mbed.org/users/RichardE/code/CommonTypes/#033e112463bb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Game.h Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,27 @@ +/* + * SOURCE FILE : Game.h + * + * Abstract base class for all games. + * + */ + +#ifndef GameIncluded + + #define GameIncluded + + class Game { + + public : + + /*****************/ + /* PLAY THE GAME */ + /*****************/ + // This NEVER exits. + void Play( void ); + + }; + +#endif + +/* END of Game.h */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GameRobotRic.cpp Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,142 @@ +/* + * SOURCE FILE : GameRobotRic.cpp + * + * The RobotRic game class. + * + */ + +#include "GameRobotRic.h" // this module's prototypes +#include "Types.h" // various integer types etc. +#include "LevelCollection.h" // all the levels +#if 0 + #include "RobotRicCharacterSet.h" // character set used in this game + #include "GDExtra.h" // extra Gameduino functions + #include "GDConst.h" // a few more Gameduino constants + #include "ArenaConst.h" // gameplay arena constants + #include "I2CEEPROM.h" // for access to serial EEPROM + #include "HighScoreEntry.h" // for getting player's name for high score table +#endif + +// Number of lives player starts with. +#define START_LIVES 5 + +// Pin allocations for I2C communications link. +#define EEPROM_SCL 5 +#define EEPROM_SDA 7 +#define EEPROM_WP 8 + +// Address of EEPROM set using A0, A1 and A2 pins. +#define ADDRESS_OF_EEPROM 0 + +/**************************/ +/* CHECK FOR A HIGH SCORE */ +/**************************/ +// Pass pointer to high score table in highScores. +// Pass score that was achieved in score. +void GameRobotRic::CheckForHighScore( HighScoreTable *highScores, UInt32 score ) { +#if 0 + UInt8 tablePos; + // Enter name into high score table if score is high enough. + if( ( tablePos = highScores->GetPositionInTable( player.Score ) ) < highScores->GetCapacity() ) { + // Player has made it onto the high score table. + // Get player to input name. + HighScoreEntry nameGetter; + PlayerName name; + nameGetter.GetName( &name, &controls ); + // Add name and score to table. + highScores->Add( tablePos, &name, score ); + } +#endif +} + +/*****************/ +/* PLAY THE GAME */ +/*****************/ +// This NEVER exits. +void GameRobotRic::Play( void ) { + // Make a digital output for use with Gameduino. + DigitalOut cs( p8 ); + // Initialise an SPI link for communications with Gameduino. + // Use pin 5 for MOSI. + // Use pin 6 for MISO. + // Use pin 7 for SCK. + SPI spi( p5, p6, p7 ); + // 8MHz clock should be OK. + spi.frequency( 8000000 ); + // Set SPI format to use. + // Use 8 bits per SPI frame. + // Use SPI mode 0. + spi.format( 8, 0 ); + // Make a Gameduino and pass SPI link and digital output for chip select. + Gameduino gd( &spi, &cs ); + // Reset the Gameduino. + gd.begin(); + // Lets have a default ASCII character set. + gd.ascii(); + // Display sign on message. + gd.putstr( 3, 10, "RobotRic is up and running!" ); +#if 0 + // Initialise I2C communications (to talk to serial EEPROM). + Wire.begin( EEPROM_SDA, EEPROM_SCL ); + // Initialise serial EEPROM object with EEPROM address of zero. + I2CEEPROM eeprom; + eeprom.Open( &Wire, ADDRESS_OF_EEPROM, EEPROM_WP ); + // Create a high score table that uses the EEPROM. + HighScoreTable highScores( &eeprom ); +#endif + // Start on the attract level. + UInt8 levelNumber = LevelCollection::AttractLevel; +#if 0 + player.Lives = START_LIVES; +#endif + LevelCollection levels; + Level *level; + Level::LevelExitCode exitCode; +#if 0 + // Initialise panel controls. + controls.InitialisePins(); + // Specify controls player should use. + player.SetControls( &controls ); + // Restrict players movement. + player.MovementRestricted = true; + player.Bounds = &ArenaRectangle; +#endif + // Repeat forever. + while( true ) { +#if 0 + // Get level specified by level number. + level = levels.GetLevel( levelNumber ); + // If failed to get level with that number go back to first normal level. + // This will happen if player completes last level. + if( level == NULL ) { + levelNumber = LevelCollection::FirstNormalLevel; + // Refetch level. + level = levels.GetLevel( levelNumber ); + } + // Pass reference to high score table. + level->SetHighScores( &highScores ); + // Set player that is playing the level. + level->SetPlayer( &player ); + // Play the level. + exitCode = level->Play(); + // If player was killed then decrement lives otherwise + // advance to next level. + switch( exitCode ) { + case Level::GameOver : + // TODO : Do some sort of game over fuss. + CheckForHighScore( &highScores, player.Score ); + // Go back to attract level and reset player lives and score. + levelNumber = LevelCollection::AttractLevel; + player.Lives = START_LIVES; + player.Score = 0; + break; + case Level::Completed : + levelNumber++; + break; + } +#endif + // Finished with Gameduino. + gd.end(); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GameRobotRic.h Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,47 @@ +/* + * SOURCE FILE : GameRobotRic.h + * + * The RobotRic game class. + * + */ + +#ifndef GameRobotRicIncluded + + #define GameRobotRicIncluded + + #include "Gameduino.h" // Gameduino stuff + #include "Game.h" // base class for all games + // #include "PlayerObject.h" + #include "HighScoreTable.h" // for high score table stored in external EEPROM + + class GameRobotRic : public Game { + + public : + + /*****************/ + /* PLAY THE GAME */ + /*****************/ + // This NEVER exits. + void Play( void ); + + private : + + // The one and only player. + // PlayerObject player; + + // Controls used by player. + // PanelControls controls; + + /**************************/ + /* CHECK FOR A HIGH SCORE */ + /**************************/ + // Pass pointer to high score table in highScores. + // Pass score that was achieved in score. + void CheckForHighScore( HighScoreTable *highScores, UInt32 score ); + + }; + +#endif + +/* END of GameRobotRic.h */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Gameduino.lib Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,1 @@ +https://mbed.org/users/RichardE/code/Gameduino/#f6a33c5f0f7f
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HighScoreEntry.cpp Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,173 @@ +/* + * SOURCE FILE : HighScoreEntry.cpp + * + * Definition of class HighScoreEntry. + * Routine to allow player to enter their name using joysticks. + * + */ + +#include "HighScoreEntry.h" +#include "Gameduino.h" // Gameduino stuff +#if 0 + #include "GDExtra.h" // more Gameduino stuff + #include "GDConst.h" // Gameduino constants + #include "CharBlocks.h" // blocks of characters in program memory + #include "CharFrame.h" // for drawing frames made of characters + #include "CharCodes.h" // character codes + #include "FrameCounter.h" // counter updated every vertical flyback +#endif + +/***************/ +/* CONSTRUCTOR */ +/***************/ +HighScoreEntry::HighScoreEntry() : + cursorPos( 0 ) +{ +} + +/**************/ +/* DESTRUCTOR */ +/**************/ +HighScoreEntry::~HighScoreEntry() { +} + +/*********************/ +/* GET A PLAYER NAME */ +/*********************/ +// Pass pointer to place to store name in name. +// Pass pointer to controls to read in controls. +#if 0 +void HighScoreEntry::GetName( PlayerName *name, PanelControls *controls ) { + UInt16 inputs; + UInt8 countdown = 0; + char *curPtr; + // Initialise name to all 'A' characters. + for( UInt8 i = 0; i < PlayerName::Length; ++i ) { + name->Name[ i ] = 'A'; + } + // Draw screen. + DrawScreen(); + // Wait until player releases all controls. + WaitControls( controls, false ); + // Start at leftmost character. + cursorPos = 0; + // Loop until cursor moves beyond rightmost character. + while( cursorPos < PlayerName::Length ) { + // Read panel controls. + controls->Read(); + inputs = controls->GetInputs(); + // Point to character under cursor. + curPtr = name->Name + cursorPos; + // Only react to controls every so often to slow things down. + if( countdown == 0 ) { + countdown = 10; + if( inputs & PanelControls::Up1 ) { + // Joystick up selects next character up. + if( *curPtr >= PlayerName::MaxChar ) { + *curPtr = PlayerName::MinChar; + } + else { + (*curPtr)++; + } + } + else if( inputs & PanelControls::Down1 ) { + // Joystick down selects previous character down. + if( *curPtr <= PlayerName::MinChar ) { + *curPtr = PlayerName::MaxChar; + } + else { + (*curPtr)--; + } + } + else if( inputs & PanelControls::Left1 ) { + // Joystick left moves cursor back. + if( cursorPos > 0 ) { + cursorPos--; + // Wait until all controls released. + WaitControls( controls, false ); + } + } + else if( inputs & PanelControls::Right1 ) { + // Joystick right moves cursor forwards. + cursorPos++; + // Wait until all controls released. + WaitControls( controls, false ); + } + } + else { + countdown--; + } + // Wait for vertical flyback. Then draw name and do animation. + GD.waitvblank(); + FrameCounter++; + DrawName( name ); + Animate(); + } + // Wait until player releases all controls before returning. + WaitControls( controls, false ); +} +#endif + +/*********************/ +/* WAIT FOR CONTROLS */ +/*********************/ +// Pass pointer to controls to read in controls. +// Pass true in waitActivate to wait for a control to be used. +// Pass false to wait for release. +#if 0 +void HighScoreEntry::WaitControls( PanelControls *controls, bool waitActivate ) { + boolean released = false; + UInt16 inputs; + while( ! released ) { + controls->Read(); + inputs = controls->GetInputs(); + released = ( waitActivate ? ( inputs != 0 ) : ( inputs == 0 ) ); + if( ! released ) { + GD.waitvblank(); + FrameCounter++; + Animate(); + } + } +} +#endif + +/*******************/ +/* DRAW THE SCREEN */ +/*******************/ +void HighScoreEntry::DrawScreen( void ) { +#if 0 + GD.waitvblank(); + // Clear the screen to zero characters. + GD.fill( RAM_PIC, 0, RAM_PIC_SIZE ); + // Turn off all the sprites. + for( UInt16 s = 0; s < SPRITE_COUNT; ++s ) { + GD.sprite( s, 0, 400, 0, 0 ); + } + // Draw border around screen. + CharFrame::Draw( 0, 0, VISIBLE_CHAR_WIDTH, VISIBLE_CHAR_HEIGHT ); + // Draw logo. + GDExtra::WriteProgCharBlock( 2, 2, CharBlocks::EnterNameInstructionText ); +#endif +} + +/********************************/ +/* DRAW THE NAME AND THE CURSOR */ +/********************************/ +// Pass player name in name. +void HighScoreEntry::DrawName( PlayerName *name ) { +#if 0 + GD.putstr( 21, 11, name->Name ); + UInt16 address = RAM_PIC + 12 * SCREEN_CHAR_WIDTH + 21; + for( UInt8 i = 0; i < PlayerName::Length; ++i ) { + GD.wr( address, ( i == cursorPos ) ? ArrowUp : ' ' ); + address++; + } +#endif +} + +/********************/ +/* UPDATE ANIMATION */ +/********************/ +void HighScoreEntry::Animate( void ) { +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HighScoreEntry.h Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,72 @@ +/* + * SOURCE FILE : HighScoreEntry.h + * + * Definition of class HighScoreEntry. + * Routine to allow player to enter their name using joysticks. + * + */ + +#ifndef HighScoreEntryDefined + + #define HighScoreEntryDefined + + #include "Types.h" + // #include "PanelControls.h" // for reading panel controls. + #include "PlayerName.h" + + class HighScoreEntry { + + public : + + /***************/ + /* CONSTRUCTOR */ + /***************/ + HighScoreEntry(); + + /**************/ + /* DESTRUCTOR */ + /**************/ + virtual ~HighScoreEntry(); + + /*********************/ + /* GET A PLAYER NAME */ + /*********************/ + // Pass pointer to place to store name in name. + // Pass pointer to controls to read in controls. + // void GetName( PlayerName *name, PanelControls *controls ); + +private : + + // Position of cursor (zero for first character). + UInt8 cursorPos; + + /*********************/ + /* WAIT FOR CONTROLS */ + /*********************/ + // Pass pointer to controls to read in controls. + // Pass true in waitActivate to wait for a control to be used. + // Pass false to wait for release. + // void WaitControls( PanelControls *controls, bool waitActivate ); + + /*******************/ + /* DRAW THE SCREEN */ + /*******************/ + void DrawScreen( void ); + + /********************************/ + /* DRAW THE NAME AND THE CURSOR */ + /********************************/ + // Pass player name in name. + void DrawName( PlayerName *name ); + + /********************/ + /* UPDATE ANIMATION */ + /********************/ + void Animate( void ); + + }; + +#endif + +/* END of HighScoreEntry.h */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HighScoreTable.cpp Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,259 @@ +/* + * SOURCE FILE : HighScoreTable.cpp + * + * Definition of class HighScoreTable. + * Maintains a table of high scores with a name and a score for each entry in the table. + * + * The table is stored in EEPROM at an address specified when you call the constructor. + * The table is structured as follows. This shows a table with a capacity of 3. + * + * Byte offset Usage + * ----------- ----- + * 0 Index of highest score in data that follows + * 1 Index of second highest score in data that follows + * 2 Index of third highest score in data that follows + * + * 3 First character of player's name + * 4 Second character of player's name + * 5 Third character of player's name + * 6 LSB of score (in BCD) + * 7 Byte 1 of score (in BCD) + * 8 Byte 2 of score (in BCD) + * 9 MSB of score (in BCD) + * 10 Unused + * + * 11 First character of player's name + * 12 Second character of player's name + * 13 Third character of player's name + * 14 LSB of score (in BCD) + * 15 Byte 1 of score (in BCD) + * 16 Byte 2 of score (in BCD) + * 17 MSB of score (in BCD) + * 18 Unused + * + * 19 First character of player's name + * 20 Second character of player's name + * 21 Third character of player's name + * 22 LSB of score (in BCD) + * 23 Byte 1 of score (in BCD) + * 24 Byte 2 of score (in BCD) + * 25 MSB of score (in BCD) + * 26 Unused + * + * So, assuming the capacity of the table is N, the first N bytes form an index which is used to locate + * items in the remaining N*8 bytes that follow. This is done so that inserting a new entry only involves + * overwriting one name/score record and updating the index. You don't have to re-write all the records + * that move down the table to make room for the new one. + * + */ + +#include "HighScoreTable.h" + +/***************/ +/* CONSTRUCTOR */ +/***************/ +// Pass pointer to an I2C EEPROM which contains the high scores. +HighScoreTable::HighScoreTable( I2CEEPROM *e ) : + eeprom( e ) +{ +} + +/**************/ +/* DESTRUCTOR */ +/**************/ +HighScoreTable::~HighScoreTable() { +} + +/****************************************/ +/* VALIDATE EEPROM USED FOR HIGH SCORES */ +/****************************************/ +// Checks EEPROM used for high scores and +// if any of it looks like nonsense it +// rewrites the whole table with defaults. +void HighScoreTable::ValidateEEPROM( void ) { + // Check if contents of EEPROM make sense. + // If not then rewrite EEPROM with defaults. + if( ! EEPROMValid() ) { + WriteEEPROMDefaults(); + } +} + +/**********************************************/ +/* DETERMINE POSITION OF A SCORE IN THE TABLE */ +/**********************************************/ +// Pass score in score. +// Returns position in table (0 is top score). +// If position returned is >= capacity of table then score is not high +// enough to place in table. +UInt8 HighScoreTable::GetPositionInTable( UInt32 score ) const { + // Look through table for a score less than the one passed. + PlayerName name; + UInt32 tableScore = (UInt32)0; + for( UInt8 i = 0; i < capacity; ++i ) { + Get( i, &name, &tableScore ); + if( tableScore < score ) { + // Found a score that is less. + // Return index at which it was found. + return i; + } + } + // No score found that is less than the one passed. + // Return capacity of table to indicate not found. + return capacity; +} + +/*********************************/ +/* ADD ENTRY TO HIGH SCORE TABLE */ +/*********************************/ +// Pass position in table to put entry in pos. +// Pass name of player in name. +// Pass score in score. +void HighScoreTable::Add( UInt8 pos, const PlayerName *name, UInt32 score ) { + // Read the entire index, high scores and names out of EEPROM. + // Going to do manipulations in RAM to minimise the number of + // writes we need to do to EEPROM. Remember every time we write + // a single byte a whole page is written so might as well + // write a whole page in one go. Only drawback is more RAM + // is required. + UInt8 buffer[ memoryUsed ]; + if( eeprom->ReadBytes( eepromAddress, buffer, memoryUsed ) ) { + // Fetch index for lowest score in the table. + UInt8 index = buffer[ capacity - 1 ]; + // Make sure index is within range. + if( index < capacity ) { + // Point to section of buffer that contains name and score for + // lowest score. + UInt8 *address = buffer + capacity + ( index << 3 ); + // Copy characters of name into buffer. + for( UInt8 i = 0; i < PlayerName::Length; ++i ) { + *address++ = name->Name[ i ]; + } + // Copy bytes of score into buffer. + *((UInt32*)address) = score; + address += 4; + // Move all entries in the index below insertion point down one + // to make room for new entry. + for( UInt8 i = capacity - 1; i > pos; --i ) { + buffer[ i ] = buffer[ i - 1 ]; + } + // Insert index of newly written record at insertion point. + buffer[ pos ] = index; + // Write the buffer back to EEPROM. + eeprom->WriteBytes( eepromAddress, buffer, memoryUsed ); + } + } +} + +/****************************/ +/* GET ENTRY FROM THE TABLE */ +/****************************/ +// Pass position to fetch from in pos. +// Player name is returned in object pointed to by name. +// Player score is returned in integer pointed to by score. +void HighScoreTable::Get( UInt8 pos, PlayerName *name, UInt32 *score ) const { + // Write default values to name and score. + for( UInt8 i = 0; i < PlayerName::Length; ++i ) { + name->Name[ i ] = (UInt8)'X'; + } + name->Name[ PlayerName::Length ] = 0; + *score = 0; + // Fetch index from EEPROM. + UInt8 index; + if( ! eeprom->ReadBytes( eepromAddress + pos, &index, 1 ) ) { + return; + } + // Point to appropriate part of data table. + UInt16 address = eepromAddress + capacity + ( index << 3 ); + // Read out characters and store in name. + if( ! eeprom->ReadBytes( address, (UInt8*)name->Name, PlayerName::Length ) ) { + return; + } + name->Name[ PlayerName::Length ] = 0; + address += PlayerName::Length; + // Read out score. + eeprom->ReadBytes( address, (UInt8*)score, 4 ); +} + +/********************************/ +/* DETERMINE IF EEPROM IS VALID */ +/********************************/ +// Returns true if EEPROM is valid. +bool HighScoreTable::EEPROMValid( void ) { + UInt8 b, b2; + // Check all entries in the index are within range and are unique. + for( UInt8 i = 0; i < capacity; ++i ) { + // Read byte from EEPROM. + if( ! eeprom->ReadBytes( eepromAddress + i, &b, 1 ) ) { + return false; + } + // Check index read is less than capacity. + if( b >= capacity ) { + return false; + } + // Check if any of the following bytes in the index have + // the same value. + for( UInt8 j = i + 1; j < capacity; ++j ) { + if( ! eeprom->ReadBytes( eepromAddress + j, &b2, 1 ) ) { + return false; + } + if( b == b2 ) { + return false; + } + } + } + // Check all entries in the data part of the table are valid. + UInt16 address = eepromAddress + capacity; + for( UInt8 i = 0; i < capacity; ++i ) { + // Check name consists only of uppercase letters. + for( UInt8 j = 0; j < PlayerName::Length; ++j ) { + // Read byte from EEPROM. + if( ! eeprom->ReadBytes( address++, &b, 1 ) ) { + return false; + } + if( ( b < PlayerName::MinChar ) || ( b > PlayerName::MaxChar ) ) { + return false; + } + } + // Check score consists only of valid BCD numbers. + for( UInt8 j = 0; j < 4; ++j ) { + // Read byte from EEPROM. + if( ! eeprom->ReadBytes( address++, &b, 1 ) ) { + return false; + } + if( ( ( b & 0x0F ) > 0x09 ) || ( ( b & 0xF0 ) > 0x90 ) ) { + return false; + } + } + // Skip over unused byte. + address++; + } + // EEPROM is valid + return true; +} + +/****************************/ +/* WRITE DEFAULTS TO EEPROM */ +/****************************/ +// This may take a second or two to execute! +void HighScoreTable::WriteEEPROMDefaults( void ) { + UInt8 buffer[ memoryUsed ]; + // Write index with ascending integers. + UInt8 *ptr = buffer; + for( UInt8 i = 0; i < capacity; ++i ) { + *ptr++ = i; + } + // Write data table with zero score entries. + for( UInt8 i = 0; i < capacity; ++i ) { + // Write a name of "AAA". + for( UInt8 j = 0; j < PlayerName::Length; ++j ) { + *ptr++ = (UInt8)'A'; + } + // Write a score of zero. + *((UInt32*)ptr) = 0; + ptr += 4; + // Write zero to unused byte. + *ptr++ = 0; + } + // Write the buffer to EEPROM. + eeprom->WriteBytes( eepromAddress, buffer, memoryUsed ); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HighScoreTable.h Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,111 @@ +/* + * SOURCE FILE : HighScoreTable.h + * + * Definition of class HighScoreTable. + * Maintains a table of high scores with a name and a score for each entry in the table. + * + */ + +#ifndef HighScoreTableDefined + + #define HighScoreTableDefined + + #include "Types.h" + #include "PlayerName.h" + #include "I2CEEPROM.h" + + class HighScoreTable { + + public : + + /***************/ + /* CONSTRUCTOR */ + /***************/ + // Pass pointer to an I2C EEPROM which contains the high scores. + HighScoreTable( I2CEEPROM *e ); + + /**************/ + /* DESTRUCTOR */ + /**************/ + virtual ~HighScoreTable(); + + /***************************************/ + /* GET EEPROM USED BY HIGH SCORE TABLE */ + /***************************************/ + I2CEEPROM *GetEEPROM( void ) const { + return eeprom; + } + + /****************************************/ + /* VALIDATE EEPROM USED FOR HIGH SCORES */ + /****************************************/ + // Checks EEPROM used for high scores and + // if any of it looks like nonsense it + // rewrites the whole table with defaults. + void ValidateEEPROM( void ); + + /********************************/ + /* DETERMINE IF EEPROM IS VALID */ + /********************************/ + // Returns true if EEPROM is valid. + bool EEPROMValid( void ); + + /*************************/ + /* GET CAPACITY OF TABLE */ + /*************************/ + // Returns capacity of table. + UInt8 GetCapacity( void ) const { + return capacity; + } + + /**********************************************/ + /* DETERMINE POSITION OF A SCORE IN THE TABLE */ + /**********************************************/ + // Pass score in score. + // Returns position in table (0 is top score). + // If position returned is >= capacity of table then score is not high + // enough to place in table. + UInt8 GetPositionInTable( UInt32 score ) const; + + /*********************************/ + /* ADD ENTRY TO HIGH SCORE TABLE */ + /*********************************/ + // Pass position in table to put entry in pos. + // Pass name of player in name. + // Pass score in score. + void Add( UInt8 pos, const PlayerName *name, UInt32 score ); + + /****************************/ + /* GET ENTRY FROM THE TABLE */ + /****************************/ + // Pass position to fetch from in pos. + // Player name is returned in object pointed to by name. + // Player score is returned in integer pointed to by score. + void Get( UInt8 pos, PlayerName *name, UInt32 *score ) const; + + private : + + // Change the following to suit. + enum { + eepromAddress = 0, // address in EEPROM where high score table starts + capacity = 10, // number of entries in table + // Amount of memory used in EEPROM. + // You need capacity bytes for the index and then + // eight bytes for each high score and name. + memoryUsed = capacity + ( capacity << 3 ), + }; + + // Pointer to EEPROM that holds high scores. + I2CEEPROM *eeprom; + + /****************************/ + /* WRITE DEFAULTS TO EEPROM */ + /****************************/ + void WriteEEPROMDefaults( void ); + + }; + +#endif + +/* END of HighScoreTable.h */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/I2CEEPROM.cpp Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,191 @@ +/* + * SOURCE FILE : I2CEEPROM.cpp + * + * Definition of class I2CEEPROM. + * Rountines for communicating with a serial EEPROM over an I2C link. + * EEPROM in question is a Microchip 24AA512. + * Remember you need pullup resistors on the SCL, SDA and WP lines. + * + */ + +#include "I2CEEPROM.h" + +/***************/ +/* CONSTRUCTOR */ +/***************/ +I2CEEPROM::I2CEEPROM() : + isOpen( false ), + // wire( (TwoWire*)NULL ), + wp( 8 ) +{ +} + +/**************/ +/* DESTRUCTOR */ +/**************/ +I2CEEPROM::~I2CEEPROM() { +} + +/**************************************/ +/* PREPARE TO COMMUNICATE WITH EEPROM */ +/**************************************/ +// Pass pointer to TwoWire object to use for I2C communications +// which should have been initialised and opened before calling this. +// Pass 3 bit address of EEPROM (as set by A0, A1 and A2 pins) in ea. +// Pass pin number to use for write protect in wp. +#if 0 +void I2CEEPROM::Open( TwoWire *wire, UInt8 ea, UInt8 wp ) { + if( ! isOpen ) { + // Save pointer to TwoWire object. + this->wire = wire; + // Calculate slave address. + slaveAddress = (UInt8)( ( ea & 0x7 ) | 0x50 ); + // Save write protect pin number. + this->wp = wp; + // Configure write protect pin as open drain output. + pinMode( wp, OUTPUT_OPEN_DRAIN ); + // Write protect EEPROM. + digitalWrite( wp, HIGH ); + isOpen = true; + } +} +#endif + +/*********************************************/ +/* SHUT DOWN COMMUNICATIONS LINK WITH EEPROM */ +/*********************************************/ +void I2CEEPROM::Close( void ) { + isOpen = false; +} + +// Size of each chunk read or written. +#define CHUNK_SIZE ( WIRE_BUFSIZ - 2 ) + +/************************************/ +/* ATTEMPT TO READ A CHUNK OF BYTES */ +/************************************/ +// Pass address within EEPROM in address. +// Pass pointer to buffer for bytes read in buffer. +// Returns false if a failure occurred whilst reading EEPROM. +// Pass number of bytes to read in count. +// Returns true if successful, false if not. +// The number of bytes read must not exceed CHUNK_SIZE. +bool I2CEEPROM::ReadChunk( UInt16 address, UInt8 *buffer, UInt8 count ) { +#if 0 + // First send address you want to read from. + wire->beginTransmission( slaveAddress ); + // Add high byte of EEPROM address to read from. + wire->send( ( address >> 8 ) & 0xFF ); + // Add low byte of EEPROM address to read from. + wire->send( address & 0xFF ); + // Send the packet and return false on failure. + if( wire->endTransmission() != SUCCESS ) { + return false; + } + // Now read back data. + wire->requestFrom( slaveAddress, count ); + UInt16 readCount = 0; + while( ( wire->available() > 0 ) && ( readCount < count ) ) { + *buffer++ = wire->receive(); + readCount++; + } + return true; +#else + return false; +#endif +} + +/*************************************/ +/* ATTEMPT TO READ A NUMBER OF BYTES */ +/*************************************/ +// Pass address within EEPROM in address. +// Pass pointer to buffer for bytes read in buffer. +// Returns false if a failure occurred whilst reading EEPROM. +// Pass number of bytes to read in count. +// Returns true if successful, false if not. +bool I2CEEPROM::ReadBytes( UInt16 address, UInt8 *buffer, UInt16 count ) { +#if 0 + bool ok = true; + // Keep reading chunks until all bytes read. + while( ( count > 0 ) && ok ) { + if( count > CHUNK_SIZE ) { + ok = ReadChunk( address, buffer, (UInt8)CHUNK_SIZE ); + address += CHUNK_SIZE; + buffer += CHUNK_SIZE; + count -= CHUNK_SIZE; + } + else { + ok = ReadChunk( address, buffer, (UInt8)count ); + count = 0; + } + } + // Return true on success. + return ok; +#else + return false; +#endif +} + +/**************************************/ +/* ATTEMPT TO WRITE A CHUNK TO EEPROM */ +/**************************************/ +// Pass address within EEPROM in address. +// Pass pointer to buffer of bytes to write in buffer. +// Pass number of bytes to write in count. +// Returns true if successful, false if not. +// The number of bytes written must not exceed CHUNK_SIZE. +bool I2CEEPROM::WriteChunk( UInt16 address, const UInt8 *buffer, UInt8 count ) { +#if 0 + // First send address you want to write to. + wire->beginTransmission( slaveAddress ); + // Add high byte of EEPROM address to write to. + wire->send( ( address >> 8 ) & 0xFF ); + // Add low byte of EEPROM address to write to. + wire->send( address & 0xFF ); + // Add buffer bytes. + wire->send( (uint8*)buffer, count ); + // Send the packet. + // Return true if successful. + return ( wire->endTransmission() == SUCCESS ); +#else + return false; +#endif +} + +/**************************************/ +/* ATTEMPT TO WRITE A NUMBER OF BYTES */ +/**************************************/ +// Pass address within EEPROM in address. +// Pass pointer to buffer of bytes to write in buffer. +// Pass number of bytes to write in count. +// Returns true if successful, false if not. +bool I2CEEPROM::WriteBytes( UInt16 address, const UInt8 *buffer, UInt16 count ) { +#if 0 + bool ok = true; + // Write enable EEPROM and wait a bit. + digitalWrite( wp, LOW ); + delay( 1 ); + // Keep writing chunks until all bytes written. + while( ( count > 0 ) && ok ) { + if( count > CHUNK_SIZE ) { + ok = WriteChunk( address, buffer, (UInt8)CHUNK_SIZE ); + address += CHUNK_SIZE; + buffer += CHUNK_SIZE; + count -= CHUNK_SIZE; + } + else { + ok = WriteChunk( address, buffer, (UInt8)count ); + count = 0; + } + // Wait a lot to allow page to be written. + delay( 6 ); + } + // Write protect EEPROM. + digitalWrite( wp, HIGH ); + // Return true on success. + return ok; +#else + return false; +#endif +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/I2CEEPROM.h Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,113 @@ +/* + * SOURCE FILE : I2CEEPROM.h + * + * Definition of class I2CEEPROM. + * Rountines for communicating with a serial EEPROM over an I2C link. + * EEPROM in question is a Microchip 24AA512. + * Remember you need pullup resistors on the SCL, SDA and WP lines. + * + */ + +#ifndef I2CEEPROMDefined + + #define I2CEEPROMDefined + + #include "Types.h" + // #include "CDFWire.h" // tweaked version of Wire library + + class I2CEEPROM { + + public : + + /***************/ + /* CONSTRUCTOR */ + /***************/ + I2CEEPROM(); + + /**************/ + /* DESTRUCTOR */ + /**************/ + virtual ~I2CEEPROM(); + + /**************************************/ + /* PREPARE TO COMMUNICATE WITH EEPROM */ + /**************************************/ + // Pass pointer to TwoWire object to use for I2C communications. + // Pass 3 bit address of EEPROM (as set by A0, A1 and A2 pins) in ea. + // Pass pin number to use for write protect in wp. + // void Open( TwoWire *wire, UInt8 ea, UInt8 wp ); + + /*********************************************/ + /* SHUT DOWN COMMUNICATIONS LINK WITH EEPROM */ + /*********************************************/ + void Close( void ); + + /*****************************************/ + /* DETERMINE IF LINK WITH EEPROM IS OPEN */ + /*****************************************/ + // Returns true if link is open. + bool IsOpen( void ) { + return isOpen; + } + + /*************************************/ + /* ATTEMPT TO READ A NUMBER OF BYTES */ + /*************************************/ + // Pass address within EEPROM in address. + // Pass pointer to buffer for bytes read in buffer. + // Pass number of bytes to read in count. + // Returns true if successful, false if not. + bool ReadBytes( UInt16 address, UInt8 *buffer, UInt16 count ); + + /**************************************/ + /* ATTEMPT TO WRITE A NUMBER OF BYTES */ + /**************************************/ + // Pass address within EEPROM in address. + // Pass pointer to buffer of bytes to write in buffer. + // Pass number of bytes to write in count. + // Returns true if successful, false if not. + bool WriteBytes( UInt16 address, const UInt8 *buffer, UInt16 count ); + +private : + + // Indicates connection with EEPROM is open. + bool isOpen; + + // Pointer to object that does I2C communications. + // TwoWire *wire; + + // Slave address consisting of three bits that identify the + // particular EEPROM on the I2C bus, plus bits 3 to 7 are + // set to 01010 binary (see 24AA512 dtasheet). + UInt8 slaveAddress; + + // Pin number for write protect pin. + UInt8 wp; + + /************************************/ + /* ATTEMPT TO READ A CHUNK OF BYTES */ + /************************************/ + // Pass address within EEPROM in address. + // Pass pointer to buffer for bytes read in buffer. + // Returns false if a failure occurred whilst reading EEPROM. + // Pass number of bytes to read in count. + // Returns true if successful, false if not. + // The number of bytes read must not exceed CHUNK_SIZE. + bool ReadChunk( UInt16 address, UInt8 *buffer, UInt8 count ); + + /**************************************/ + /* ATTEMPT TO WRITE A CHUNK TO EEPROM */ + /**************************************/ + // Pass address within EEPROM in address. + // Pass pointer to buffer of bytes to write in buffer. + // Pass number of bytes to write in count. + // Returns true if successful, false if not. + // The number of bytes written must not exceed WIRE_BUFSIZ - 2. + bool WriteChunk( UInt16 address, const UInt8 *buffer, UInt8 count ); + + }; + +#endif + +/* END of I2CEEPROM.h */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Level.cpp Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,26 @@ +/* + * SOURCE FILE : Level.cpp + * + * Definition of class Level. + * Base class for all levels. + * + */ + +#include "Level.h" + +/***************/ +/* CONSTRUCTOR */ +/***************/ +Level::Level() : + LevelNumber( 0 ), + highScores( (HighScoreTable*)NULL ) + // player( (PlayerObject*)NULL ) +{ +} + +/**************/ +/* DESTRUCTOR */ +/**************/ +Level::~Level() { +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Level.h Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,102 @@ +/* + * SOURCE FILE : Level.h + * + * Definition of class Level. + * Base class for all levels. + * + */ + +#ifndef LevelDefined + + #define LevelDefined + + #include "Types.h" + #include "Gameduino.h" // Gameduino library + #include "HighScoreTable.h" +#if 0 + #include "GDConst.h" // a few more Gameduino constants + #include "GDExtra.h" // a few more Gameduino related functions + #include "ArenaConst.h" + #include "SpriteImageId.h" + #include "CharCodes.h" // character codes + #include "GameObject.h" + #include "GruntObject.h" + #include "BlueMeanyObject.h" + #include "CrusherObject.h" + #include "BrainObject.h" + #include "StringData.h" + #include "CharFrame.h" + #include "SoundManager.h" + #include "Sounds.h" +#endif + + class Level { + + public : + + // Number of this level. + UInt8 LevelNumber; + + /***************/ + /* CONSTRUCTOR */ + /***************/ + Level(); + + /**************/ + /* DESTRUCTOR */ + /**************/ + virtual ~Level(); + + /************************/ + /* SET HIGH SCORE TABLE */ + /************************/ + // Pass pointer to EEPROM in e. + void SetHighScores( HighScoreTable *hst ) { + highScores = hst; + } + + /***************************************/ + /* SET PLAYER WHO IS PLAYING THE LEVEL */ + /***************************************/ + // Pass pointer to player in p. +#if 0 + void SetPlayer( PlayerObject *p ) { + player = p; + } +#endif + + // Enumeration of reasons why level ended. + enum LevelExitCode { + Completed, // level was completed + GameOver, // player has no more lives + }; + + /**************/ + /* PLAY LEVEL */ + /**************/ + // Returns code indicating how level ended. + virtual LevelExitCode Play( void ) = 0; + + protected : + + // Pointer to high score table. + HighScoreTable *highScores; + + // Player playing the level. + // PlayerObject *player; + + /*************/ + /* PLAY LOOP */ + /*************/ + // Returns code indicating how level ended. + // This method should be called from the Play method after the + // level data has been initialised and the return value returned + // by the Play method. + virtual LevelExitCode PlayLoop( void ) = 0; + + }; + +#endif + +/* END of Level.h */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Level0.cpp Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,107 @@ +/* + * SOURCE FILE : Level0.cpp + * + * Definition of class Level0. + * + */ + +#include "Level0.h" +#if 0 + #include "CharBlocks.h" + #include "EEPROMDump.h" +#endif + +/***************/ +/* CONSTRUCTOR */ +/***************/ +Level0::Level0() { +} + +/**************/ +/* DESTRUCTOR */ +/**************/ +Level0::~Level0() { +} + +/**************/ +/* PLAY LEVEL */ +/**************/ +// Returns code indicating how level ended. +Level::LevelExitCode Level0::Play( void ) { + return PlayLoop(); +} + +static const char startText[] = "OPERATE EITHER JOYSTICK TO START GAME"; + +/********************/ +/* DRAW HIGH SCORES */ +/********************/ +void Level0::DrawHighScores( void ) { +#if 0 + // Draw high score table. + GDExtra::WriteProgString( 16, 11, StringData::HighScoresString ); + PlayerName name; + UInt32 score; + UInt8 y = 13; + for( UInt8 i = 0; i < highScores->GetCapacity(); ++i ) { + highScores->Get( i, &name, &score ); + GD.putstr( 18, y, name.Name ); + GDExtra::WriteBCDNumber( 22, y, score, 8 ); + y += 2; + } +#endif +} + +/*************/ +/* PLAY LOOP */ +/*************/ +// Returns code indicating how level ended. +// This method should be called from the Play method after the +// level data has been initialised and the return value returned +// by the Play method. +Level::LevelExitCode Level0::PlayLoop( void ) { +#if 0 + // Set screen background to black. + GD.wr( BG_COLOR, RGB( 0, 0, 0 ) ); + GDExtra::ClearScreen( TransparentChar ); + GDExtra::HideAllSprites(); + SoundManager::Instance.SilenceAll(); + // Draw border around screen. + CharFrame::Draw( 0, 0, VISIBLE_CHAR_WIDTH, VISIBLE_CHAR_HEIGHT ); + // Draw big block of characters that read "ROBOTRIC". + GDExtra::WriteProgCharBlock( 2, 2, CharBlocks::RobotRicText ); + // Write message telling user how to start game. + GDExtra::WriteProgString( 2, VISIBLE_CHAR_HEIGHT - 3, startText ); + // Validate high scores in EEPROM which will re-write the entire + // table with default data if it finds nonsense in the EEPROM. + // Then check if EEPROM now makes sense. If it does then display + // the high score table. If it does not then chances are there + // is no EEPROM connected so don't bother with high scores. + if( highScores != (HighScoreTable*)NULL ) { + highScores->ValidateEEPROM(); + if( highScores->EEPROMValid() ) { + DrawHighScores(); + } + } + // Must have a player with non-NULL controls. + PanelControls *controls; + if( + ( player != (PlayerObject*)NULL ) && + ( ( controls = player->GetControls() ) != (PanelControls*)NULL ) + ) { + // Wait until all panel controls are released. + UInt16 inputs; + do { + controls->Read(); + inputs = controls->GetInputs(); + } while( inputs != 0 ); + // Wait until a panel control is activated. + do { + controls->Read(); + inputs = controls->GetInputs(); + } while( inputs == 0 ); + } +#endif + return Completed; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Level0.h Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,55 @@ +/* + * SOURCE FILE : Level0.h + * + * Definition of class Level0. + * + */ + +#ifndef Level0Defined + + #define Level0Defined + + #include "Level.h" + + class Level0 : public Level { + + public : + + /***************/ + /* CONSTRUCTOR */ + /***************/ + Level0(); + + /**************/ + /* DESTRUCTOR */ + /**************/ + virtual ~Level0(); + + /**************/ + /* PLAY LEVEL */ + /**************/ + // Returns code indicating how level ended. + virtual LevelExitCode Play( void ); + + protected : + + /********************/ + /* DRAW HIGH SCORES */ + /********************/ + void DrawHighScores( void ); + + /*************/ + /* PLAY LOOP */ + /*************/ + // Returns code indicating how level ended. + // This method should be called from the Play method after the + // level data has been initialised and the return value returned + // by the Play method. + LevelExitCode PlayLoop( void ); + + }; + +#endif + +/* END of Level0.h */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Level1.cpp Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,53 @@ +/* + * SOURCE FILE : Level1.cpp + * + * Definition of class Level1. + * Machine written by program MapleRobotRicLevelGenerator. + * DO NOT EDIT BY HAND! + * + */ + +#include "Level1.h" + +/**************/ +/* PLAY LEVEL */ +/**************/ +// Returns code indicating how level ended. +Level::LevelExitCode Level1::Play( void ) { +#if 0 + LevelData dataForThisLevel; + GameObject **ptr = dataForThisLevel.Enemies; + UInt8 i, humanCount, enemyCount = 0; + // Enemies of type GruntObject. + #define GRUNTOBJECTCOUNT 5 + GruntObject gruntobjects[ GRUNTOBJECTCOUNT ]; + for( i = 0; i < GRUNTOBJECTCOUNT; ++i ) { + if( enemyCount < LevelData::MaxEnemies ) { + *ptr++ = gruntobjects + i; + enemyCount++; + } + } + // Enemies of type CrusherObject. + #define CRUSHEROBJECTCOUNT 5 + CrusherObject crusherobjects[ CRUSHEROBJECTCOUNT ]; + for( i = 0; i < CRUSHEROBJECTCOUNT; ++i ) { + if( enemyCount < LevelData::MaxEnemies ) { + *ptr++ = crusherobjects + i; + enemyCount++; + } + } + // Humans. + #define HUMANCOUNT 5 + HumanObject humans[ HUMANCOUNT ]; + ptr = dataForThisLevel.Humans; + humanCount = ( HUMANCOUNT > LevelData::MaxHumans ) ? LevelData::MaxHumans : HUMANCOUNT; + for( i = 0; i < humanCount; ++i ) { + *ptr++ = humans + i; + } + DataForLevel = &dataForThisLevel; + return PlayLoop(); +#else + return Completed; +#endif +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Level1.h Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,30 @@ +/* + * SOURCE FILE : Level1.h + * + * Definition of class Level1. + * Machine written by program MapleRobotRicLevelGenerator. + * DO NOT EDIT BY HAND! + * + */ + +#ifndef Level1Defined + + #define Level1Defined + + #include "LevelNormal.h" + + class Level1 : public LevelNormal { + + public : + + /**************/ + /* PLAY LEVEL */ + /**************/ + // Returns code indicating how level ended. + virtual LevelExitCode Play( void ); + + }; + +#endif + +/* END of Level1.h */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Level2.cpp Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,52 @@ +/* + * SOURCE FILE : Level2.cpp + * + * Definition of class Level2. + * Machine written by program MapleRobotRicLevelGenerator. + * DO NOT EDIT BY HAND! + * + */ + +#include "Level2.h" + +/**************/ +/* PLAY LEVEL */ +/**************/ +// Returns code indicating how level ended. +Level::LevelExitCode Level2::Play( void ) { +#if 0 + LevelData dataForThisLevel; + GameObject **ptr = dataForThisLevel.Enemies; + UInt8 i, humanCount, enemyCount = 0; + // Enemies of type GruntObject. + #define GRUNTOBJECTCOUNT 10 + GruntObject gruntobjects[ GRUNTOBJECTCOUNT ]; + for( i = 0; i < GRUNTOBJECTCOUNT; ++i ) { + if( enemyCount < LevelData::MaxEnemies ) { + *ptr++ = gruntobjects + i; + enemyCount++; + } + } + // Enemies of type BrainObject. + #define BRAINOBJECTCOUNT 2 + BrainObject brainobjects[ BRAINOBJECTCOUNT ]; + for( i = 0; i < BRAINOBJECTCOUNT; ++i ) { + if( enemyCount < LevelData::MaxEnemies ) { + *ptr++ = brainobjects + i; + enemyCount++; + } + } + // Humans. + #define HUMANCOUNT 6 + HumanObject humans[ HUMANCOUNT ]; + ptr = dataForThisLevel.Humans; + humanCount = ( HUMANCOUNT > LevelData::MaxHumans ) ? LevelData::MaxHumans : HUMANCOUNT; + for( i = 0; i < humanCount; ++i ) { + *ptr++ = humans + i; + } + DataForLevel = &dataForThisLevel; + return PlayLoop(); +#else + return Completed; +#endif +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Level2.h Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,30 @@ +/* + * SOURCE FILE : Level2.h + * + * Definition of class Level2. + * Machine written by program MapleRobotRicLevelGenerator. + * DO NOT EDIT BY HAND! + * + */ + +#ifndef Level2Defined + + #define Level2Defined + + #include "LevelNormal.h" + + class Level2 : public LevelNormal { + + public : + + /**************/ + /* PLAY LEVEL */ + /**************/ + // Returns code indicating how level ended. + virtual LevelExitCode Play( void ); + + }; + +#endif + +/* END of Level2.h */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LevelCollection.cpp Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,128 @@ +/* + * SOURCE FILE : LevelCollection.cpp + * + * Definition of class LevelCollection. + * + */ + +#include <stdio.h> +#include "LevelCollection.h" +#include "Level0.h" +#include "Level1.h" +#include "Level2.h" +#if 0 + #include "Level3.h" + #include "Level4.h" + #include "Level5.h" + #include "Level6.h" + #include "Level7.h" + #include "Level8.h" + #include "Level9.h" + #include "Level10.h" + #include "Level10.h" + #include "Level11.h" + #include "Level12.h" + #include "Level13.h" + #include "Level14.h" + #include "Level15.h" + #include "Level16.h" + #include "Level17.h" + #include "Level18.h" + #include "Level19.h" + #include "Level20.h" +#endif + +/***************/ +/* CONSTRUCTOR */ +/***************/ +LevelCollection::LevelCollection() { +} + +/**************/ +/* DESTRUCTOR */ +/**************/ +LevelCollection::~LevelCollection() { +} + +#define LEVELCOUNT 3 + +/************************/ +/* GET NUMBER OF LEVELS */ +/************************/ +UInt8 LevelCollection::GetLevelCount( void ) const { + return LEVELCOUNT; +} + +// Individual levels. +static Level0 level0; // Not a real level. This is attract mode. +static Level1 level1; +static Level2 level2; +#if 0 +static Level3 level3; +static Level4 level4; +static Level5 level5; +static Level6 level6; +static Level7 level7; +static Level8 level8; +static Level9 level9; +static Level10 level10; +static Level11 level11; +static Level12 level12; +static Level13 level13; +static Level14 level14; +static Level15 level15; +static Level16 level16; +static Level17 level17; +static Level18 level18; +static Level19 level19; +static Level20 level20; +#endif + +// Pointers to all the levels. +// If I have got this right then each pointer in the array points to a +// Level that is NOT const, but the array itself is const and cannot be modified. +static Level* const levels[ LEVELCOUNT ] = { + &level0, + &level1, + &level2, +#if 0 + &level3, + &level4, + &level5, + &level6, + &level7, + &level8, + &level9, + &level10, + &level11, + &level12, + &level13, + &level14, + &level15, + &level16, + &level17, + &level18, + &level19, + &level20, +#endif +}; + +/***************/ +/* GET A LEVEL */ +/***************/ +// Pass level number in levelNumber. +// Returns pointer to level or NULL if no such level. +Level *LevelCollection::GetLevel( UInt8 levelNumber ) { + if( levelNumber >= LEVELCOUNT ) { + return (Level*)NULL; + } + else { + // Fetch pointer to level from array and tag it with the + // correct level number before returning it. + Level *level = levels[ levelNumber ]; + level->LevelNumber = levelNumber; + return level; + } +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LevelCollection.h Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,51 @@ +/* + * SOURCE FILE : LevelCollection.h + * + * Definition of class LevelCollection. + * + */ + +#ifndef LevelCollectionDefined + + #define LevelCollectionDefined + + #include "Types.h" + #include "Level.h" + + class LevelCollection { + + public : + + enum { + AttractLevel = 0, // just ticking over encouraging player to start + FirstNormalLevel = 1, // first real level excluding attract mode + }; + + /***************/ + /* CONSTRUCTOR */ + /***************/ + LevelCollection(); + + /**************/ + /* DESTRUCTOR */ + /**************/ + virtual ~LevelCollection(); + + /************************/ + /* GET NUMBER OF LEVELS */ + /************************/ + UInt8 GetLevelCount( void ) const; + + /***************/ + /* GET A LEVEL */ + /***************/ + // Pass level number in levelNumber. + // Returns pointer to level or NULL if no such level. + Level *GetLevel( UInt8 levelNumber ); + + }; + +#endif + +/* END of LevelCollection.h */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LevelNormal.cpp Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,376 @@ +/* + * SOURCE FILE : LevelNormal.cpp + * + * Definition of class LevelNormal. + * Base class for all "normal" levels. + * i.e. Levels that are not special attract modes + * but have enemies who are trying to kill you + * and so on. + * + */ + +#include "LevelNormal.h" +#if 0 + #include "GameObjectLocator.h" + #include "FrameCounter.h" + #include "SpriteNumber.h" +#endif + +// Current instance being processed. +LevelNormal *LevelNormal::currentInstance; + +/***************/ +/* CONSTRUCTOR */ +/***************/ +LevelNormal::LevelNormal() { +} + +/**************/ +/* DESTRUCTOR */ +/**************/ +LevelNormal::~LevelNormal() { +} + +/********************/ +/* INITIALISE LEVEL */ +/********************/ +void LevelNormal::InitialiseLevel( void ) { +#if 0 + // Note that if you re-arrange the following code you may need to adjust the + // SpriteNumber enumeration in SpriteNumber.h. + UInt8 spriteNumber = FirstEnemySprite; + // Initialise enemies. + GameObject::InitialiseAll( DataForLevel->Enemies, LevelData::MaxEnemies, &spriteNumber ); + // Initialise humans. + spriteNumber = FirstHumanSprite; + GameObject::InitialiseAll( DataForLevel->Humans, LevelData::MaxHumans, &spriteNumber ); + // Use next free sprite number for player. + player->SpriteNumber = PlayerSprite; + // Do futher initialisation for all enemies. + EnemyObject *object; + for( UInt8 e = 0; e < LevelData::MaxEnemies; ++e ) { + object = (EnemyObject*)DataForLevel->Enemies[ e ]; + if( object != (EnemyObject*)NULL ) { + // Get enemy to chase the player. + object->SetChaseObject( player ); + // Pass array of all enemies to this enemy. + object->Enemies = DataForLevel->Enemies; + // If enemy is a brain then tell it about the humans to chase. + if( object->GetEnemyType() == Brain ) { + ((BrainObject*)object)->HumansToChase = DataForLevel->Humans; + } + } + } + // Put player in the centre of the arena. + player->Xco = PLAYER_START_X; + player->Yco = PLAYER_START_Y; + // Kill off all player's bullets. + player->KillAllBullets(); + // Kill off all explosions. + ExplosionManager::Instance.KillAllExplosions(); +#endif +} + +/**********************************/ +/* DRAW SCORE AND NUMBER OF LIVES */ +/**********************************/ +void LevelNormal::DrawScoreAndLives( void ) { +#if 0 + GDExtra::WriteBCDNumber( 16, 0, player->Score, 8 ); + // Display number of lives but limit this to 20 lives displayed. + UInt8 lives = ( player->Lives > 20 ) ? 20 : player->Lives; + GD.fill( RAM_PIC + VISIBLE_CHAR_WIDTH - lives, MiniPlayer, lives ); +#endif +} + +/******************/ +/* DRAW THE LEVEL */ +/******************/ +void LevelNormal::DrawLevel( void ) { +#if 0 + // Set screen background to black. + GD.wr( BG_COLOR, RGB( 0, 0, 0 ) ); + // Clear the screen to zero characters. + GDExtra::ClearScreen( TransparentChar ); + // Hide all sprties. + GDExtra::HideAllSprites(); + // Display level number. + GDExtra::WriteProgString( 0, 0, StringData::LevelString ); + GDExtra::WriteUInt16( 6, 0, LevelNumber, 10, 2 ); + // Display score. + GDExtra::WriteProgString( 10, 0, StringData::ScoreString ); + // Update score and lives. + DrawScoreAndLives(); + // Draw border around screen. + CharFrame::Draw( + ARENA_BORDER_X, + ARENA_BORDER_Y, + ARENA_BORDER_WIDTH, + ARENA_BORDER_HEIGHT + ); +#endif +} + +/************************************************/ +/* HANDLE COLLISIONS BETWEEN HUMANS AND ENEMIES */ +/************************************************/ +// Pass index of human in level's humans array in humanIndex. +// Pass sprite number of sprite that it hit in spriteNumber. +void LevelNormal::HandleHumanCollision( UInt8 humanIndex, UInt8 spriteNumber ) { +#if 0 + // Point to array of enemy object pointers. + GameObject **enemies = currentInstance->DataForLevel->Enemies; + EnemyObject *enemy; + UInt8 enemyIndex, mutantIndex; + // Find an enemy with given sprite number. + if( GameObject::FindSpriteNumber( enemies, LevelData::MaxEnemies, spriteNumber, &enemyIndex ) ) { + // Found enemy. Check if it squashes humans. + enemy = (EnemyObject*)enemies[ enemyIndex ]; + // Get hold of the human that is doomed. + GameObject **humans = currentInstance->DataForLevel->Humans; + HumanObject *human = (HumanObject*)humans[ humanIndex ]; + // Human must be walking around. Not rescued or already dead. + if( human->CurrentState == HumanObject::WalkingAbout ) { + if( enemy->SquashesHumans ) { + // Change human to dead state. + human->CurrentState = HumanObject::Dead; + // Make a noise. + SoundManager::Instance.PlaySound( Sounds::HumanDies, 0, 0 ); + } + else if( enemy->GetEnemyType() == Brain ) { + // Kill human by inserting a null into humans array. + humans[ humanIndex ] = (GameObject*)NULL; + // Find a free slot for a new enemy. + if( GameObject::FindUnusedObject( enemies, LevelData::MaxEnemies, &mutantIndex ) ) { + // Write a pointer to a mutant with the same index as the human that just died + // into the enemy array. + MutantObject *mutant = currentInstance->DataForLevel->Mutants + humanIndex; + enemies[ mutantIndex ] = mutant; + // Initialise mutant at coordinates of human and chasing the player. + mutant->Start( human, currentInstance->player ); + // Make a noise. + // TODO : SoundManager::Instance.PlaySound( Sounds::HumanMutates, 0, 0 ); + } + else { + // Could not find a free slot for a new enemy so just erase the human sprite. + GDExtra::HideSprite( human->SpriteNumber ); + } + } + } + } +#endif +} + +/********************************************************/ +/* HANDLE COLLISIONS BETWEEN PLAYER BULLETS AND ENEMIES */ +/********************************************************/ +// Pass index of bullet in player's bullet array in bulletIndex. +// Pass sprite number of sprite that it hit in spriteNumber. +void LevelNormal::HandleBulletCollision( UInt8 bulletIndex, UInt8 spriteNumber ) { +#if 0 + // Point to array of enemy object pointers. + GameObject **enemies = currentInstance->DataForLevel->Enemies; + EnemyObject *enemy; + UInt8 enemyIndex; + // Find an enemy with given sprite number. + if( GameObject::FindSpriteNumber( enemies, LevelData::MaxEnemies, spriteNumber, &enemyIndex ) ) { + // Found enemy. Check if it is indestructable. + enemy = (EnemyObject*)enemies[ enemyIndex ]; + if( enemy->HitPoints != EnemyObject::Indestructable ) { + // Enemy is not indestructable. Decrement hit points and die when it reaches zero. + enemy->HitPoints--; + if( enemy->HitPoints == 0 ) { + // Kill enemy by inserting a NULL into enemies array. + enemies[ enemyIndex ] = (GameObject*)NULL; + // Hide the enemy sprite. + GDExtra::HideSprite( enemy->SpriteNumber ); + // Add points to player's score. + currentInstance->player->AddToScore( enemy->GetPoints() ); + } + } + // Tell enemy it has been hit by a bullet. + enemy->RegisterHitByBullet(); + // Kill off the bullet. + currentInstance->player->KillBullet( bulletIndex ); + // Make a noise. + SoundManager::Instance.PlaySound( Sounds::Explosion, 0, 0 ); + // Start explosion animation using coordinates of enemy. + ExplosionManager::Instance.StartExplosion( enemy->Xco, enemy->Yco ); + } +#endif +} + +/*********************************************************/ +/* CHECK FOR COLLISIONS BETWEEN PLAYER AND OTHER OBJECTS */ +/*********************************************************/ +// Pass pointer to a flag that will be set true if player is dead in isDead parameter. +void LevelNormal::CheckPlayerCollisions( bool *isDead ) { +#if 0 + UInt8 enemyIndex, humanIndex; + // Check if player sprite has hit another sprite. + UInt8 hitSpriteNumber = GD.rd( COLLISION + player->SpriteNumber ); + // If you get 0xFF then no collision found. + if( hitSpriteNumber != 0xFF ) { + // Check for collision with an enemy. + if( + GameObject::FindSpriteNumber( + DataForLevel->Enemies, LevelData::MaxEnemies, hitSpriteNumber, &enemyIndex + ) + ) { + // Hit an enemy. Player is dead. + *isDead = true; + } + // Check for collision with a human that has not already been rescued or killed. + else if( + GameObject::FindSpriteNumber( + DataForLevel->Humans, LevelData::MaxHumans, hitSpriteNumber, &humanIndex + ) + ) { + HumanObject *human = (HumanObject*)DataForLevel->Humans[ humanIndex ]; + if( human->CurrentState == HumanObject::WalkingAbout ) { + // Change human state to rescued. + human->CurrentState = HumanObject::Rescued; + // Give player 50 points (in BCD!). + player->AddToScore( 0x50 ); + // Make a noise. + SoundManager::Instance.PlaySound( Sounds::RescueHuman, 0, 0 ); + } + } + } +#endif +} + +/***********************************************************************************/ +/* WAIT UNTIL SLOT FREE FOR A NEW SOUND, PLAY IT AND WAIT FOR ALL SOUNDS TO FINISH */ +/***********************************************************************************/ +// Pass sound to play in soundToPlay parameter. +void LevelNormal::PlaySoundAndWait( const UInt8 *soundToPlay ) { +#if 0 + // Keep trying to play sound until it works and meanwhile + // keep currently playing sounds going. + while( ! SoundManager::Instance.PlaySound( soundToPlay, 0, 0 ) ) { + // Update sound manager. + SoundManager::Instance.Update(); + // Wait for frame flyback. + GD.waitvblank(); + } + // Now wait until all sounds have finished. + while( SoundManager::Instance.CountSoundsPlaying() > 0 ) { + // Update sound manager. + SoundManager::Instance.Update(); + // Wait for frame flyback. + GD.waitvblank(); + } +#endif +} + +/*************/ +/* PLAY LOOP */ +/*************/ +// Returns code indicating how level ended. +// This method should be called from the Play method after the +// level data has been initialised and the return value returned +// by the Play method. +Level::LevelExitCode LevelNormal::PlayLoop( void ) { +#if 0 + // Do nothing if level data is NULL or player has not been specified. + if( ( DataForLevel != (LevelData*)NULL ) || ( player == (PlayerObject*)NULL ) ) { + // Point static pointer to current instance. + currentInstance = this; + // Do some initialisation first. + InitialiseLevel(); + // Redraw the screen. + DrawLevel(); + // Wait for frame flyback once before entering loop so collision data is recalculated. + // At this point there should not be any sprites on the screen so no collisions + // should be found. + GD.waitvblank(); + // Repeat until all enemies are dead or player is dead. + bool allEnemiesAreDead = false; + bool playerIsDead = false; + bool gameIsOver = false; + bool firstDraw = true; + while( ! allEnemiesAreDead && ! gameIsOver ) { + // Update sound manager. + SoundManager::Instance.Update(); + // Wait for frame flyback. + GD.waitvblank(); + // Check for collisions between player and other objects. + CheckPlayerCollisions( &playerIsDead ); + // Check for collisions between humans and enemies that squash. + GameObject::FindCollisions( DataForLevel->Humans, LevelData::MaxHumans, &LevelNormal::HandleHumanCollision ); + // Check for collisions between player bullets and enemies. + GameObject::FindCollisions( player->GetBullets(), BulletManager::MaxBullets, &LevelNormal::HandleBulletCollision ); + // Redraw the player's score and number of lives. + DrawScoreAndLives(); + // Draw all the enemies. + GameObject::DrawAll( DataForLevel->Enemies, LevelData::MaxEnemies ); + // Draw all the humans. + GameObject::DrawAll( DataForLevel->Humans, LevelData::MaxHumans ); + // Draw all the explosions. + GameObject::DrawAll( ExplosionManager::Instance.GetExplosions(), ExplosionManager::MaxExplosions ); + // Draw the player. + player->Draw(); + // Draw the player's bullets. + GameObject::DrawAll( player->GetBullets(), BulletManager::MaxBullets ); + // Increment the frame counter. + FrameCounter++; + // After first redraw play level start sound and wait for it to end. + if( firstDraw ) { + PlaySoundAndWait( Sounds::StartLevel ); + firstDraw = false; + } + // If player was killed then play death march and wait for it to finish. + if( playerIsDead ) { + // Player got killed. + PlaySoundAndWait( Sounds::PlayerDead ); + // One less life for player. + if( player->Lives > 0 ) { + player->Lives--; + } + // Game is over when player has no more lives. + gameIsOver = ( player->Lives == 0 ); + // If game is not over then re-initialise level using any remaining enemies. + if( ! gameIsOver ) { + // Remove all objects that do not survive a level restart (like enemy bullets). + GameObject::RemoveUnretainedObjects( DataForLevel->Enemies, LevelData::MaxEnemies ); + InitialiseLevel(); + DrawLevel(); + GD.waitvblank(); + playerIsDead = false; + firstDraw = true; + } + } + else { + // Move all the enemies and check if all dead. + allEnemiesAreDead = ! GameObject::MoveAll( DataForLevel->Enemies, LevelData::MaxEnemies ); + // If there are still some enemies alive then check if those that remain are indestructable. + // If only indestructable enemies survive then act as if all enemies are dead. + // You need to do this or you would never be able to complete a level that had indestructable + // enemies on it. + if( ! allEnemiesAreDead ) { + allEnemiesAreDead = EnemyObject::AreAllIndestructable( + (const EnemyObject**)DataForLevel->Enemies, + LevelData::MaxEnemies + ); + } + // Move all the humans. + GameObject::MoveAll( DataForLevel->Humans, LevelData::MaxHumans ); + // Move (update) all the explosions. + GameObject::MoveAll( ExplosionManager::Instance.GetExplosions(), ExplosionManager::MaxExplosions ); + // Read the player's controls. + player->ReadControls(); + // Move the player. + player->Move(); + // Move the player's bullets. + GameObject::MoveAll( player->GetBullets(), BulletManager::MaxBullets ); + } + } + // Player completed level or game is over. + return gameIsOver ? GameOver : Completed; + } +#endif + // Level data or player were not specified. + return Completed; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LevelNormal.h Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,102 @@ +/* + * SOURCE FILE : LevelNormal.h + * + * Definition of class LevelNormal. + * Base class for all "normal" levels. + * i.e. Levels that are not special attract modes + * but have enemies who are trying to kill you + * and so on. + * + */ + +#ifndef LevelNormalDefined + + #define LevelNormalDefined + + #include "Level.h" +#if 0 + #include "LevelData.h" + #include "ExplosionManager.h" +#endif + + class LevelNormal : public Level { + + public : + + /***************/ + /* CONSTRUCTOR */ + /***************/ + LevelNormal(); + + /**************/ + /* DESTRUCTOR */ + /**************/ + virtual ~LevelNormal(); + + protected : + + // Data defining the level. + // You should write to this before calling PlayLoop. + // LevelData *DataForLevel; + + /*************/ + /* PLAY LOOP */ + /*************/ + // Returns code indicating how level ended. + // This method should be called from the Play method after the + // level data has been initialised and the return value returned + // by the Play method. + virtual LevelExitCode PlayLoop( void ); + + private : + + // Current instance being processed. + static LevelNormal *currentInstance; + + /********************/ + /* INITIALISE LEVEL */ + /********************/ + void InitialiseLevel( void ); + + /**********************************/ + /* DRAW SCORE AND NUMBER OF LIVES */ + /**********************************/ + void DrawScoreAndLives( void ); + + /******************/ + /* DRAW THE LEVEL */ + /******************/ + void DrawLevel( void ); + + /************************************************/ + /* HANDLE COLLISIONS BETWEEN HUMANS AND ENEMIES */ + /************************************************/ + // Pass index of human in level's humans array in humanIndex. + // Pass sprite number of sprite that it hit in spriteNumber. + static void HandleHumanCollision( UInt8 humanIndex, UInt8 spriteNumber ); + + /********************************************************/ + /* HANDLE COLLISIONS BETWEEN PLAYER BULLETS AND ENEMIES */ + /********************************************************/ + // Pass index of bullet in player's bullet array in bulletIndex. + // Pass sprite number of sprite that it hit in spriteNumber. + static void HandleBulletCollision( UInt8 bulletIndex, UInt8 spriteNumber ); + + /*********************************************************/ + /* CHECK FOR COLLISIONS BETWEEN PLAYER AND OTHER OBJECTS */ + /*********************************************************/ + // Pass pointer to a flag that will be set true if player is dead in isDead parameter. + void CheckPlayerCollisions( bool *isDead ); + + /***********************************************************************************/ + /* WAIT UNTIL SLOT FREE FOR A NEW SOUND, PLAY IT AND WAIT FOR ALL SOUNDS TO FINISH */ + /***********************************************************************************/ + // Pass sound to play in soundToPlay parameter. + static void PlaySoundAndWait( const UInt8 *soundToPlay ); + + }; + +#endif + +/* END of LevelNormal.h */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PlayerName.cpp Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,38 @@ +/* + * SOURCE FILE : PlayerName.cpp + * + * Definition of class PlayerName. + * Contains the name that appears in a high score table for example. + * + */ + +#include "PlayerName.h" + +/***************/ +/* CONSTRUCTOR */ +/***************/ +PlayerName::PlayerName() { + // Initialise name to all 'X' characters. + for( UInt8 i = 0; i < Length; ++i ) { + Name[ i ] = 'X'; + } + Name[ Length ] = (char)0; +} + +/**************/ +/* DESTRUCTOR */ +/**************/ +PlayerName::~PlayerName() { +} + +/************************************************************/ +/* COPY ONE NAME TO ANOTHER WITHOUT CREATING A NEW INSTANCE */ +/************************************************************/ +// Pass pointer to name to copy to in dest. +void PlayerName::CopyTo( PlayerName *dest ) const { + for( UInt8 i = 0; i < Length; ++i ) { + dest->Name[ i ] = Name[ i ]; + } + dest->Name[ Length ] = (char)0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PlayerName.h Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,50 @@ +/* + * SOURCE FILE : PlayerName.h + * + * Definition of class PlayerName. + * Contains the name that appears in a high score table for example. + * + */ + +#ifndef PlayerNameDefined + + #define PlayerNameDefined + + #include "Types.h" + + class PlayerName { + + public : + + enum { + Length = 3, // Number of characters used for name. + MinChar = ' ', // First valid character in a name. + MaxChar = 'Z', // Last valid character in a name. + }; + + // Characters that make up the name, plus a zero char at the end. + char Name[ Length + 1 ]; + + /***************/ + /* CONSTRUCTOR */ + /***************/ + PlayerName(); + + /**************/ + /* DESTRUCTOR */ + /**************/ + virtual ~PlayerName(); + + /************************************************************/ + /* COPY ONE NAME TO ANOTHER WITHOUT CREATING A NEW INSTANCE */ + /************************************************************/ + // Pass pointer to name to copy to in dest. + void CopyTo( PlayerName *dest ) const; + + }; + + +#endif + +/* END of PlayerName.h */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,21 @@ +/* + * SOURCE FILE : main.cpp + * + * Main program for the RobotRic game. Uses the Gameduino board (for Arduino) to generate graphics and sound. + * + */ + +#include "mbed.h" +#include "GameRobotRic.h" // RobotRic game object + +/** + * Main program. + * Never returns. + */ +int main() { + // Create an instance of the game and then play it. + // Play method never exits. + GameRobotRic game; + game.Play(); + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Tue Jun 04 20:16:33 2013 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/b3110cd2dd17 \ No newline at end of file