Race around the city collecting the flags while avoiding those that stand in the way of your mission. Make no mistake you will need to be quick to outwit your opponents, they are smart and will try to box you in. I wrote this game to prove that writing a game with scrolling scenery is possible even with the limited 6kB of RAM available. I had to compromise sound effects for features, I wanted multiple opponents, I wanted to be able to drop smoke bombs to trap the opponents but all this required memory so the sound effects had to take a back seat.

Dependencies:   mbed

Player.h

Committer:
taylorza
Date:
2015-02-01
Revision:
1:1b8125937f28
Parent:
0:d85c449aca6d

File content as of revision 1:1b8125937f28:

#include "Constants.h"
#include "RallyCar.h"
#include "Flag.h"
#include "Smoke.h"
#include "Beeper.h"

#ifndef __PLAYER_H__
#define __PLAYER_H__

class Player : public RallyCar
{
public:
    Player(Point startPosition) :
        _startPosition(startPosition),
        _smokeIndex(255),
        _lives(MAX_LIVES),
        _score(0)
    {            
        reset();
    }    
    
    virtual void reset()
    {
        setPosition(_startPosition);
        setDirection(Up);
        setDesiredDirection(Up);
        setState(RallyCar::Idle);
        setSpriteId(4);
        _fuel = 100;   
        _updateCounter = 0;     
        
        if (_flagCount == MAX_FLAGS)
        {
            _flagCount = 0;
        }
        
        _stateCounter = 60;
    }
    
    void setCars(RallyCar **cars) { _cars = cars; }
    void setFlags(Flag *flags) { _flags = flags; }
    
    static uint16_t lfsr_rand()
    {
        static uint16_t lfsr = 0xACE1u;
        lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xB400u);
        return lfsr;
    }
 
    virtual void update()
    {                 
        if (_smokeIndex == 255)
        {
            for(int i = 0; i < MAX_SMOKE; ++i)
            {
                _smoke[i].setCars(_cars);
                getParent()->addGameObject(&_smoke[i]);
            }    
            _smokeIndex = 0;
        }
        
        if (getState() == RallyCar::Idle)
        {
            if (--_stateCounter == 0)
            {
                setState(RallyCar::Driving);
            }
        }
        else if (getState() == RallyCar::Driving)
        {
            ++_updateCounter;
            if (_updateCounter % FUEL_COUNTER == 0) --_fuel;
            if (_fuel == 0) setState(RallyCar::StartCrash);
        
            Point &position = getPosition();
            
            bool allowLeftRightTurn = position.Y % 8 == 0;
            bool allowUpDownTurn = position.X % 8 == 0;
            
            if (GameInput::isLeftPressed()) { setDesiredDirection(Left); }
            if (GameInput::isRightPressed()) { setDesiredDirection(Right); }
            if (GameInput::isUpPressed()) { setDesiredDirection(Up); }
            if (GameInput::isDownPressed()) { setDesiredDirection(Down); }
            if (GameInput::isCirclePressed() && !_dropSmoke) {_dropSmoke = true; }
            
            bool forceTurn = false;
            do
            {
                if (getDirection() != getDesiredDirection())
                {            
                    if ((getDesiredDirection() == Left && allowLeftRightTurn && canGoLeft())
                    || (getDesiredDirection() == Right && allowLeftRightTurn && canGoRight())
                    || (getDesiredDirection() == Up && allowUpDownTurn && canGoUp())
                    || (getDesiredDirection() == Down && allowUpDownTurn && canGoDown()))
                    {
                         setDirection(getDesiredDirection());
                    }
                }
            
                switch(getDirection())
                {
                    case Left : setSpriteId(7); forceTurn = !moveLeft(); if (_dropSmoke) smoke(Right); break;
                    case Right : setSpriteId(5); forceTurn = !moveRight(); if (_dropSmoke) smoke(Left); break;
                    case Up : setSpriteId(4); forceTurn = !moveUp(); if (_dropSmoke) smoke(Down); break;
                    case Down : setSpriteId(6); forceTurn = !moveDown(); if (_dropSmoke) smoke(Up); break;
                }
                
                if (forceTurn)
                {
                    switch (getDirection())
                    {
                        case Left:
                        case Right:
                            if (lfsr_rand() & 1) 
                                setDesiredDirection(Up);
                            else 
                                setDesiredDirection(Down);
                        break;
                        
                        case Up:
                        case Down:
                            if (lfsr_rand() & 1) 
                                setDesiredDirection(Left);
                            else 
                                setDesiredDirection(Right);
                        break;
                    }                
                }
            } while (forceTurn);
            
            for(int i = 0; i < MAX_CARS; ++i)
            {
                RallyCar *car = _cars[i];
                if (car != this)
                {
                    if (detectCollision(car))
                    {
                        setState(RallyCar::StartCrash);
                    }
                }
            }
            
            for (int i = 0; i < MAX_FLAGS; ++i)
            {
                Flag *flag = &_flags[i];
                if (flag->getActive() == true && detectCollision(flag))
                {
                    flag->setActive(false);
                    getParent()->removeGameObject(flag);
                    _score += 100;                    
                    ++_flagCount;
                    Beeper::beep(500, 3);                    
                    Beeper::beep(2000, 4);                    
                    Beeper::beep(1000, 2);                    
                }
            }                        
        }
        else if (getState() == RallyCar::StartCrash)
        {
            _stateCounter = 30;   
            setState(RallyCar::Crashed);         
        }
        else if (getState() == RallyCar::Crashed)
        {
            setSpriteId(10);                        
            if (--_stateCounter == 0)
            {
                --_lives;
                for (int i = 0; i < MAX_CARS; ++i)
                {
                    _cars[i]->reset();
                }
            }
            Beeper::noise(2000, 2);
        }
        
        RallyCar::update();
    }
    
    inline uint8_t getLives() { return _lives; }
    inline uint32_t getScore() { return _score; }
    inline uint8_t getFuel() { return _fuel; }
    inline uint8_t getFlagCount() { return _flagCount; }

    inline void decreaseFuel() { if (_fuel > 0) --_fuel; }
    inline void increaseScore(int score) { _score += score; }    
private:    
    void smoke(Direction direction)
    {
        Point &position = getPosition();
        TileViewer* parent = getParent();
        
        if (_fuel < 10) 
        {
            _dropSmoke = false;
            return;
        }
        
        bool onLeftRightBoundary = position.X % 16 == 0 && position.X > 16 && position.X < (parent->getMapTilesX() * 16);
        bool onUpDownBoundary = position.Y % 16 == 0 && position.Y > 16 && position.Y < (parent->getMapTilesY() * 16);
        int x = 0;
        int y = 0;
        bool drop = false;
        if (direction == Left && onLeftRightBoundary)
        {            
            x = position.X - 16;
            y = position.Y;            
            drop = true;
        }
        else if (direction == Right && onLeftRightBoundary)
        {
            x = position.X + 16;
            y = position.Y;                        
            drop = true;
        }
        else if (direction == Up && onUpDownBoundary)
        {
            x = position.X;
            y = position.Y - 16;                        
            drop = true;
        }
        else if (direction == Down && onUpDownBoundary)
        {
            x = position.X;
            y = position.Y + 16;                        
            drop = true;            
        }
        
        if (drop)
        {
            _dropSmoke = false;
            Smoke &smoke = _smoke[_smokeIndex];
            smoke.setPosition(Point(x, y));
            smoke.setActive(true);            
            _smokeIndex = (_smokeIndex + 1) % MAX_SMOKE;
            _fuel -= 2;
            
            Beeper::beep(500, 2);
            Beeper::beep(1000, 3);
            Beeper::beep(600, 2);
        }
    }

private:
    Point       _startPosition;    
    bool        _dropSmoke;
    uint8_t     _smokeIndex;
    RallyCar   **_cars;
    Flag       *_flags; 
    Smoke       _smoke[MAX_SMOKE];
    uint16_t    _stateCounter;
        
    uint8_t     _lives;
    uint32_t    _score;
    uint8_t     _fuel;
    uint16_t    _updateCounter;
    uint8_t     _flagCount;
};
#endif //__PLAYER_H__