A project that creates falling asteroids at random positions along the top of the screen for a space ship at the bottom to shoot, using a Nokia N5110 LCD, joystick and pushbuttons - A revised name from my earlier project L2_2645

Dependencies:   DebounceIn N5110 PowerControl mbed

Fork of L2_2645_project by Louis Wray

main.cpp

Committer:
wray2303
Date:
2015-05-11
Revision:
4:51f8b719f671
Parent:
3:39c95d65bee5

File content as of revision 4:51f8b719f671:

/**
@file main.cpp

@brief Program implementation

@author Louis Wray

@date 14th April 2015
*/

#include "main.h"

/// timer to regularly read the joystick
Ticker pollJoystick;
/// timer to regularly call the read bullet function
Ticker readShoot;
/// timer to regularly call new asteroids
Ticker readTheAsteroid;
/// timer to create the asteroids
Timer shootSoundtimer;
///timeout used to stop button skip
Timeout EndGame;

/**
create enumerated type (0,1,2,3 etc. for direction)
*/
enum DirectionName {
    UP,
    DOWN,
    LEFT,
    RIGHT,
    CENTRE,
    UNKNOWN
};

/**
struct for Joystick
*/
typedef struct JoyStick Joystick;
struct JoyStick {
    float x;    /// current x value
    float x0;   /// 'centred' x value
    float y;    /// current y value
    float y0;   /// 'centred' y value
    int button; /// button state (assume pull-down used, so 1 = pressed, 0 = unpressed)
    DirectionName direction;  /// current direction
};
/// create struct variable
Joystick joystick;

int printFlag =0; /**< flag for calling joystick value printing */
int state = 0; /**< integer value for what state the game is in.
for use of the switch statement */
int GenAsteroidFlag = 0; /// asteroid generation flag
int AsteroidCreatedFlag = 0; /// flag to note asteroid is created
int BulletExistsFlag = 0; /// flag to note bullet has been fired and is on screen
int PauseNumber = 0; /// pause flag
int HighScore = 0; /// integer set for highscore

int main()
{
    asteroid.nscore = 0;
    srand(15000*randomPin); /// seeding random function
    calibrateJoystick();  /// set's centred values of joystick
    pollJoystick.attach(&updateJoystick,1/50.0);  /// read joystick 50 times per second
    ship.SetValues(42,39); /// initialise ships starting x,y coordinates

    PHY_PowerDown(); /// power down ethernet connection

    while(1) {

        switch(state) { /// switch statement this enables the game to switch between states giving a start state
                /// a game play state and subsequently a game over state giving choice to start again.

            case 0:
                startUpDisplay(); /// call start up display function
                state=1;
                break;

            case 1:
                Game(); /// game play
                break;

            case 2:
                lcd.clear(); /// clears display
                PauseDisplay(); /// applies pause screen
                break;

            case 3:
                EndDisplay(); /// end of game display
                break;
        }

        Sleep(); /// sleep function when nothings happening mbed can sleep and save power consumption
    }
}


void Read()
{
    FILE *fp = fopen("/local/HSCORES.txt","r"); /// open file or create then open if not already made. and read value
    fscanf(fp, "%i", &HighScore); /// look in the local file system
    fclose(fp); /// close file
}

void Write()
{
    FILE *fp = fopen("/local/HSCORES.txt","w"); /// open file or create then open if not already made. make ready for writing to the file
    fprintf(fp, "%i\r\n", HighScore); /// print value to the local file
    fclose(fp); /// close file
}


void startUpDisplay()
{
    lcd.init(); /// initialise the display
    lcd.clear();
    lcd.printString("Space-Teroid",6,1); /// opening message
    lcd.printString("Press S5 to",12,3);
    lcd.printString("Play",30,4);
    lcd.drawLine(0,0,0,47,1); /// draw outline
    lcd.drawLine(83,0,83,47,1);
    lcd.drawLine(0,0,83,0,1);
    lcd.drawLine(0,47,83,47,1);
    lcd.refresh(); /// refesh LCD to create this display

    while(!buttonP) { /// while the pause flag or pause button has not been set or used , the mbed will sleep saving power and allowing
        /// the user to start when they are ready
        sleep();
    }
    GenAsteroidFlag = 1;
    lcd.clear(); /// when flag has been set while loop has been broken and start up display will be escaped
    ship.shape(); /// the ships shape is drawn and  and the game can begin
}


void Game()
{
    lcd.drawLine(0,0,0,47,1); /// draw outline repeatedly when asteroid is falling
    lcd.drawLine(83,0,83,47,1);
    lcd.drawLine(0,0,83,0,1);
    lcd.drawLine(0,47,83,47,1);

    if (printFlag) {  /// if print flag set, clear flag and use joysticks horizontal position
        printFlag = 0;

        if (joystick.direction == LEFT && ship.xs>=7) { /// 7 represents the left most boundary
            ship.ShipLeft(); /// if joystick moves left, call ship moving left function
        }
        if (joystick.direction == RIGHT && ship.xs<=76) { /// 76 represents the right most boundary
            ship.ShipRight(); /// if joystick moves right, call the ship moving right function
        } else {} /// else do nothing
    }

    if(button) {
        AsteroidCreatedFlag = 0;
        GenAsteroidFlag = 0;
        asteroid.destroyAsteroid();
        readTheAsteroid.detach();
        PWM1 = 0;
        state = 2;

    } else {}

    if(BulletExistsFlag==0) { /// check if there is already a bullet on the screen, a rule of the game one bullet at a time!
        if(buttonS) { /// if shoot button is pressed

            shootSoundtimer.start(); /// start timer for shooting sound
            PWM1.period(2e-3); /// set the period of the PWM
            PWM1 = 0.5; /// set the duty cycle
            BulletExistsFlag=1; /// bullet exists flag set
            bullet.SetValues(ship.xs,ship.ys-1); /// set the bullets values according to where the ship is

            bullet.collision(); /// chec for bullet collision
            if(bullet.collision()==true) {
                readShoot.detach();
                bullet.clearBullet();
                BulletExistsFlag=0;
                /** if bullet colision happens during sound being made then
                *buzzer doesnt stop, thisis because of the interrupt the button makes
                *here we reset the buzzer manually */
                shootSoundtimer.stop(); /// stop the timer
                shootSoundtimer.reset(); /// reset the timer
                PWM1 = 0.0; /// turn of the PWM
            }

            else if(bullet.collision()==false) { /// if bullet co-ordinates are not off the screen
                bullet.createBullet(); /// create the bullet
                readShoot.attach(&ReadBullet,0.045); /// set ticker to read the position of the bullet
            } else {}

            if(AsteroidCreatedFlag==1) { /// check if an asteroid has been created
                readTheAsteroid.attach(&ReadAsteroid,0.045); /// set ticker for asteroid movement
            } else {}
        } else {}
    }

    if(GenAsteroidFlag == 1) { /// check if an asteroid is to be generated
        int A = rand() % 75 + 6; /// returns random value between 6 and 7
        asteroid.SetAsteroidValue(A,0); /// set asteroid position according to randomly generated value
        asteroid.createAsteroid(); /// create asteroid
        AsteroidCreatedFlag = 1; /// set flag to state an asteroid has been created and is on screen
        GenAsteroidFlag=0; /// reset Generate asteroid flag so no more asteroid are created
        if(AsteroidCreatedFlag==1) { /// if asteroid has been created
            readTheAsteroid.attach(&ReadAsteroid,0.045); /// set ticker to read and move asteroid
        } else {}
    } else {}
}


void ReadAsteroid()
{
    bullet.collision(); /// check if the bullet has made a collision this must mean an asteroid has been shot
    if(bullet.collision()==false) { /// if no collision and the asteroid y position is less than the end of the screen move the asteroid
        asteroid.ya=asteroid.ya+1; /// move asteroids y position down the screen
        asteroid.erasePrevAsteroid(); /// erase the previous asteroid
        asteroid.createAsteroid(); /// create the asteroid in its new position
    } else {}

    ////////// using a double check seems to make the game run smoother //////////////
    bullet.collision(); /// check if the bullet has made a collision this must mean an asteroid has been shot
    if(bullet.collision()==true) { /// if a collision is made
        BulletExistsFlag=0; /// reset the bullet exists flag
        readShoot.detach();
        readTheAsteroid.detach(); /// detach the read asteroid function from the ticker
        bullet.clearBullet(); /// clear the bullet from the screen
        AsteroidCreatedFlag=0; /// reset Asteroid created Flag to state there is no longer an asteroid on screen
        GenAsteroidFlag=1; /// set the Generate asteroid flag
        asteroid.destroyAsteroid(); /// destroy on screen asteroid

        /** if bullet colision happens during sound being made then
        buzzer doesnt stop, thisis because of the interrupt the button makes
        here we reset the buzzer manually*/
        shootSoundtimer.stop(); /// stop the timer
        shootSoundtimer.reset(); /// reset the timer
        PWM1 = 0.0; /// turn of the PWM
    } else {}

    asteroid.armageddon(); /// check if asteroid has reached the bottom of the screen
    if(asteroid.armageddon() == true) { /// if asteroid has it the bottom of the screen ... RUN!!!
        /// armageddon takes place and the game is over
        bullet.clearBullet(); /// clear the existing bullet
        readTheAsteroid.detach(); /// detach the read asteroid function
        asteroid.destroyAsteroid(); /// destroy asteroid in place
        EndGame.attach(&endState,0.3);
    } else {}

    lcd.drawLine(0,0,83,0,1); /// keep re-drawing top line as this is damaged while asteroid moves accross it
}


void ReadBullet()
{
        if(shootSoundtimer.read() > 0.1) { /// check when the sound timer reaches 0.1 sec
        shootSoundtimer.stop(); /// stop the timer
        shootSoundtimer.reset(); /// reset the timer
        PWM1 = 0.0; /// turn of the PWM
    } else {}

    if(button) {
        AsteroidCreatedFlag = 0;
        GenAsteroidFlag = 0;
        asteroid.destroyAsteroid();
        readTheAsteroid.detach();
        PWM1 = 0;
        state = 2;

    } else {}
    bullet.collision(); /// check if the bullet has collided with anything
    bullet.offScreen(); /// check if the bullet is off screen
    if (bullet.collision() == true||bullet.offScreen()==true) { /// if a collision is made or the bullet goes off the screen
        BulletExistsFlag=0;
        bullet.clearBullet(); /// clear the bullet
        readShoot.detach(); /// detach the ticker

        /** if bullet colision happens during sound being made then
        buzzer doesnt stop, thisis because of the interrupt the button makes
        here we reset the buzzer manually*/
        shootSoundtimer.stop(); /// stop the timer
        shootSoundtimer.reset(); /// reset the timer
        PWM1 = 0.0; /// turn of the PWM
    } else {}

    bullet.y=bullet.y-1; /// move the bullet up i.e. move its y coordinate
    bullet.createBullet(); /// draw new bullet
    bullet.erasePrevBullet(); /// erase previous position

////////// double check this seems to help rid some bugs ////////////
    bullet.offScreen(); /// check if the bullet is off screen
    bullet.collision(); /// check if the bullet has collided with anything
    if (bullet.collision() == true||bullet.offScreen()==true) { /// if a collision is made or the bullet goes off the screen
        BulletExistsFlag=0;
        bullet.clearBullet(); /// clear the bullet
        readShoot.detach(); /// detach the ticker
    } else {}

    if (printFlag) {  /// here we are checking the position of the joystick, so the user can move while the bullet is shooting else where
        printFlag = 0;
        if (joystick.direction == LEFT && ship.xs>=7) {
            ship.ShipLeft();
        }
        if (joystick.direction == RIGHT && ship.xs<=76) {
            ship.ShipRight();
        } else {}
    }
}


void EndDisplay()
{
    lcd.clear(); /// clear entire display to make way for end screen
    asteroid.nscore = asteroid.nscore-PauseNumber; /// takes points off for the asteroid destroyed when pausing game and destroy asteroid command sent
    asteroid.nscore = asteroid.nscore-1;
    char buffer[14]; /// set buffer to 14, screen 84 pixels long , 6 pixel per char , 84/6 = 14
    int score = sprintf(buffer,"Score: %d",asteroid.nscore); /// print formatted data to buffer
    if (score <= 14) { /// if string will fit on display
        lcd.printString(buffer,20,5);   /// display on screen
    }

    Read(); /// read file to compare values
    if(asteroid.nscore>HighScore) { /// compare current score against highscore
        HighScore = asteroid.nscore; /// if current score is larger than highscore = current score
        Write(); /// write this to file
    }
    Read(); /// read the file to then print to lcd
    char buffer1[14]; /// set buffer to 14, screen 84 pixels long , 6 pixel per char , 84/6 = 14
    int Hscore = sprintf(buffer1,"HighScore:%d",HighScore); /// print formatted data to buffer

    if (Hscore <= 14) { /// if string will fit on display
        lcd.printString(buffer1,7,4);   /// display on screen
    }
    lcd.printString("game over !",12,0); /// closing message
    lcd.printString("Press S5 to",10,1);
    lcd.printString("Play again",12,2);
    lcd.drawLine(0,0,0,47,1); /// draw outline
    lcd.drawLine(83,0,83,47,1);
    lcd.drawLine(0,0,83,0,1);
    lcd.drawLine(0,47,83,47,1);
    lcd.refresh(); /// refesh LCD to create this display

    while(!buttonP) { /// while button b has not been pressed sleep (save power)
        Sleep();
    }
    /// when button s pressed
    state = 0; /// set the state to 0 (intital game display
    asteroid.nscore=0; /// reset the score
}


void PauseDisplay()
{
    lcd.printString("PAUSE",28,1); /// print pause display message
    lcd.printString("press S5 to",11,3);
    lcd.printString("resume",24,4);
    lcd.drawLine(0,0,0,47,1); /// draw outline
    lcd.drawLine(83,0,83,47,1);
    lcd.drawLine(0,0,83,0,1);
    lcd.drawLine(0,47,83,47,1);
    lcd.refresh();
    while(!buttonP) { /// while button P is not pressed Sleep() to save power
        Sleep();
        if(buttonP) { /// if button P is pressed
            lcd.clear(); /// clear LCD
            ship.shape(); /// draw the ship back on in position it was left
            GenAsteroidFlag = 1; /// reset the Generate asteroid flag
            PauseNumber = PauseNumber+1; /// increment pause amount counter
            state=1; /// state = 1 means back to the game in the switch statement
        }
    }
}

void endState()
{
    state = 3;
    }


void calibrateJoystick()
{
    button.mode(PullDown);
    /// must not move during calibration
    joystick.x0 = xPot;  /// initial positions in the range 0.0 to 1.0 (0.5 if centred exactly)
    joystick.y0 = yPot;
}


void updateJoystick()
{
    /// reading the joystick position 0.0 is centered returns a value in the range -0.5 and 0.5
    joystick.x = xPot - joystick.x0;
    joystick.y = yPot - joystick.y0;
    /// read button state
    joystick.button = button;

    /// calculate direction depending on x,y values
    /// tolerance allows a little lee-way in case joystick not exactly in the stated direction
    if ( fabs(joystick.y) < DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) {
        joystick.direction = CENTRE;
    } else if ( joystick.y > DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) {
        joystick.direction = UP;
    } else if ( joystick.y < DIRECTION_TOLERANCE && fabs(joystick.x) < DIRECTION_TOLERANCE) {
        joystick.direction = DOWN;
    } else if ( joystick.x > DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) {
        joystick.direction = RIGHT;
    } else if ( joystick.x < DIRECTION_TOLERANCE && fabs(joystick.y) < DIRECTION_TOLERANCE) {
        joystick.direction = LEFT;
    } else {
        joystick.direction = UNKNOWN;
    }
    /// set flag for use of the joystick position
    printFlag = 1;
}