"Lost treasure of mBedungu" 100 level puzzle game for RETRO

Dependencies:   LCD_ST7735 RetroPlatform mbed

Into Level 0 Menu Level 99

Game/GameScreen.cpp

Committer:
Architect
Date:
2015-03-01
Revision:
1:dcea5500a32d
Parent:
0:f5f961973d01

File content as of revision 1:dcea5500a32d:

/*
 * (C) Copyright 2015 Valentin Ivanov. All rights reserved.
 *
 * This file is part of the "Lost treasure of mBedungu" game application for Retro
 *
 * The "Lost treasure of mBedungu" application is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 */
 
#include "GameScreen.h"
#include "Retro.h"
#include "Sprites.h"
#include "Levels.h"

#include "Utils.h"

extern Retro retro;


const int soundfx[][8] = {
    {0,24,1,1,1,4,4,9}, // gameover
    {0,27,53,1,1,1,4,5}, // pickup
    {0,23,60,1,1,1,4,5} // drop
};

void sfx(int fxno, int channel)
{
    sfx(soundfx[fxno], channel);
};

void sfx( const int * effect, int channel)
{
    retro.sound.command(0, effect[6], 0, channel); // set volume
    retro.sound.command(1, effect[0], 0, channel); // set waveform
    retro.sound.command(2, effect[5], -effect[4], channel); // set volume slide
    retro.sound.command(3, effect[3], effect[2] - 58, channel); // set pitch slide
    retro.sound.playNote(effect[1], effect[7], channel); // play note
    
}

Image sprites[] = {
    {false, false, NULL},
    {false, false, wall},
    {false, false, barrel},
    {false, false, ladder},
    {false, false, key},
    {false, false, door},
    {false, false, left},
    {true, false, left},
    {false, false, crate},
    {false, false, totem},
    {false, true, rick},
    {false, false, rick_top},
    {false, false, rick_happy}
};

GameScreen::GameScreen()
{
    _level = 0;
    _state = 0;
    _pocket[0] = 0;
    _pocket[1] = 0;
    _rickDirection = 0;
    _gameOver = false;
    
    //for some reason writing to eeprom sometime hangs the program                
//    _gameData[0] = 0;
//    _gameData[1] = 0;
//    _gameData[2] = 0;
//    _gameData[3] = 0;
//    _gameData[4] = 0;
//    _gameData[5] = 0;
//    
//    read_eeprom((char *)64, _gameData, 8);
//    if( _gameData[0]=='R' && _gameData[1]=='I' && _gameData[2]=='C' && _gameData[3]=='K' )
//    {
//        _level = _gameData[4];
//        _gameComplete = _gameData[5];
//    }
//    
//    _gameData[0]='R';
//    _gameData[1]='I';
//    _gameData[2]='C';
//    _gameData[3]='K';

}

bool GameScreen::isPocketFull()
{
    return (_pocket[0] != 0 && _pocket[1] != 0);
}

bool GameScreen::isPocketEmpty()
{
    return (_pocket[0] == 0 && _pocket[1] == 0);
}

bool GameScreen::putInPocket( int item )
{
    if( item != CELL_BARREL && item != CELL_KEY )
        return false;

    bool bRet = true;

    if( _pocket[0] == 0 )
        _pocket[0] = item;
    else if( _pocket[1] == 0 )
        _pocket[1] = item;
    else
        bRet = false;

    return bRet;
}

bool GameScreen::getKeyFromPocket()
{
    bool bRet = true;

    if( _pocket[0] == CELL_KEY )
        _pocket[0] = 0;
    else if( _pocket[1] == CELL_KEY )
        _pocket[1] = 0;
    else
        bRet = false;

    return bRet;
}

bool GameScreen::getBarrelFromPocket()
{
    bool bRet = true;

    if( _pocket[0] == CELL_BARREL )
        _pocket[0] = 0;
    else if( _pocket[1] == CELL_BARREL )
        _pocket[1] = 0;
    else
        bRet = false;

    return bRet;
}

void GameScreen::moveleft()
{
    if( _rickDirection == 0 ) {
        _rickDirection = 1;
        sprites[CELL_RICK].Mirrored = true;//(_rickDirection == 1);
        drawSprite(_rickX,_rickY, CELL_RICK);
    }

    if( _rickX == 0 )
        return;

    uint8_t cell = _maze[(_rickY<<3)+_rickX-1];


    if( cell == CELL_KEY || cell == CELL_BARREL ) {
        if( putInPocket(cell) ) {
            sfx(1,0);
            _maze[(_rickY<<3)+_rickX-1] = CELL_EMPTY;
            drawSprite((_rickX-1),_rickY, CELL_EMPTY);
            drawPocket();
            
            //Fall any crates on top of that key or barrel
            int y = _rickY;
            while( y >= 0 ) {
                fall(_rickX-1,y--);
            }

        }
        return;
    }

    if( (cell == CELL_CRATE) && ((_rickX-1)>0) ) {
        if( moveto(_rickX-1,_rickY, _rickX-2,_rickY) ) {
            fall(_rickX-2,_rickY);

            int y = _rickY;
            while( y >= 0 ) {
                fall(_rickX-1,y--);
            }

        }

        return;
    }


    if( moveto(_rickX,_rickY,_rickX-1,_rickY) )
        fall(_rickX,_rickY);

}

void GameScreen::moveright()
{
    if( _rickDirection == 1 ) {
        _rickDirection = 0;
        sprites[CELL_RICK].Mirrored = false;
        drawSprite(_rickX,_rickY, CELL_RICK);
    }

    if( _rickX == 7 )
        return;

    uint8_t cell = _maze[(_rickY<<3)+_rickX+1];

    if( cell == CELL_KEY || cell == CELL_BARREL ) {
        if( putInPocket(cell) ) {
            sfx(1,0);
            _maze[(_rickY<<3)+_rickX+1] = CELL_EMPTY;
            drawSprite((_rickX+1),_rickY, CELL_EMPTY);
            drawPocket();
            
            //Fall any crates on top of that key or barrel
            int y = _rickY;
            while( y >= 0 ) {
                fall(_rickX+1,y--);
            }
        }
        return;
    }

    if( (cell == CELL_CRATE) && ((_rickX+1)<7) ) {
        if( moveto(_rickX+1,_rickY, _rickX+2,_rickY) ) {
            fall(_rickX+2,_rickY);

            int y = _rickY;
            while( y >= 0 ) {
                fall(_rickX+1,y--);
            }
        }
        return;
    }

    if( moveto(_rickX,_rickY,_rickX+1,_rickY) )
        fall(_rickX,_rickY);

}

void GameScreen::moveup()
{
    if( _rickY == 0 )
        return;

    uint8_t currentCell = _maze[((_rickY)<<3)+_rickX];
    uint8_t aboveCell = _maze[((_rickY-1)<<3)+_rickX];

    //if already on ladder
    if( currentCell == CELL_LADDER )
        moveto(_rickX,_rickY,_rickX,_rickY-1);
    else if( currentCell == CELL_EMPTY && !isPocketEmpty()) {
        uint8_t x = _rickX;
        uint8_t y = _rickY;
        if( moveto(_rickX,_rickY,_rickX,_rickY-1) ) {
            if( getBarrelFromPocket() ) {
                sfx(2,0);
                _maze[(y<<3)+x] = CELL_BARREL;
                drawSprite(x,y,CELL_BARREL);
            } else if( getKeyFromPocket() ) {
                sfx(2,0);
                _maze[(y<<3)+x] = CELL_KEY;
                drawSprite(x,y,CELL_KEY);
            }
            drawPocket();
        }
    }
}

void GameScreen::movedown()
{
    if( _rickY == 7 )
        return;


    if( moveto(_rickX,_rickY,_rickX,_rickY+1) ) {
        fall(_rickX,_rickY);
    }

}


bool GameScreen::moveto(uint8_t fromX, uint8_t fromY, uint8_t toX, uint8_t toY)
{

    bool isRick = false;

    if( fromX == _rickX && fromY == _rickY )
        isRick = true;

    uint8_t fromCell = _maze[((fromY)<<3)+fromX];
    uint8_t toCell = _maze[((toY)<<3)+toX];

    if( isRick ) {
        if( toCell == CELL_EMPTY || toCell == CELL_LADDER ) {
            drawSprite(fromX,fromY, fromCell);
            _rickX = toX;
            _rickY = toY;
            drawSprite(_rickX,_rickY, CELL_RICK);
            return true;
        }
        if( toCell == CELL_TOTEM )
        {
            sfx(0,0);
            _gameOver = true;
            drawSprite(fromX,fromY, fromCell);
            _rickX = toX;
            _rickY = toY;
            drawSprite(_rickX,_rickY, CELL_RICK);
            return true;
        }
        if( toCell == CELL_DOOR ) {
            if( getKeyFromPocket() ) {
                drawSprite(fromX,fromY, fromCell);
                _maze[((toY)<<3)+toX] = CELL_EMPTY;
                _rickX = toX;
                _rickY = toY;
                drawSprite(_rickX,_rickY, CELL_RICK);
                drawPocket();
                return true;
            }
        }
        if( (toCell == CELL_RIGHT && fromX < toX) || (toCell == CELL_LEFT && fromX > toX)) {
            drawSprite(fromX,fromY, fromCell);
            _rickX = toX;
            _rickY = toY;
            drawSprite(_rickX,_rickY, CELL_RICK);
            return true;
        }
    } else if( fromCell == CELL_CRATE && toCell == CELL_EMPTY ) {
        _maze[((toY)<<3)+toX] = CELL_CRATE;
        _maze[((fromY)<<3)+fromX] = CELL_EMPTY;

        drawSprite(fromX,fromY, toCell);
        drawSprite(toX,toY, fromCell);
        return true;
    }

    return false;
}

bool GameScreen::fall(uint8_t fromX, uint8_t fromY)
{
    if( fromY == 7 )
        return false;

    uint8_t curCell = _maze[(fromY<<3)+fromX];

    if( curCell == CELL_LADDER )
        return false;

    uint8_t cell = _maze[((fromY+1)<<3)+fromX];



    while( cell == CELL_EMPTY || cell == CELL_TOTEM ) {
        if( moveto(fromX,fromY,fromX,fromY+1) ) {
            fromY++;

            if( fromY == 7 )
                break;

            cell = _maze[((fromY+1)<<3)+fromX];
        } else {
            break;
        }
    }

    return true;
}




void GameScreen::unpackLevel( int level )
{
    if( level < 0 || level > 99 )
        level = 0;

    _level = level;

    const uint32_t * pLevel = &levels[level*8];

    for( int y = 0; y < 8; y++ )
        for( int x = 0; x < 8; x++ ) {
            _maze[(y<<3) + x] = (pLevel[y]>>(28-(x<<2))) & 0x0F;

            switch( _maze[(y<<3) + x] ) {
                case CELL_RICK:
                    //The level data has specialy marked cell for Rick
                    //During the game Rick position is tracked separately
                    //This allows us to put Rick on none EMPTY cells
                    _maze[(y<<3) + x] = CELL_EMPTY;
                    _rickX = x;
                    _rickY = y;
                    break;
                case CELL_TOTEM:
                    _totemX = x;
                    _totemY = y;
                    break;
                default:
                    break;
            }
        }

    _state = 0;
    _pocket[0] = 0;
    _pocket[1] = 0;    
    _rickDirection = 0;
    _gameOver = false;
        
    sprites[CELL_RICK].Mirrored = false;
    
    frame = 0;    
}

void GameScreen::resetLevel()
{
    unpackLevel(_level);
}

void GameScreen::drawPocket()
{
    if( _pocket[0] == 0 )
        retro.display.fillRect(136,80,151,95,0);
    else
        retro.display.drawBitmapIndexed(136,80, 16,16,sprites[_pocket[0]].Sprite, palette);

    if( _pocket[1] == 0 )
        retro.display.fillRect(136,96,151,111,0);
    else
        retro.display.drawBitmapIndexed(136,96, 16,16,sprites[_pocket[1]].Sprite, palette);
}


void GameScreen::drawLevel()
{
    //Draw the maze
    for( int y = 0; y < 8; y++ )
        for( int x = 0; x < 8; x++ ) {
            drawSprite(x, y, _maze[(y<<3)+x]);
        }

    //Put Rick on the map
    drawSprite(_rickX, _rickY, CELL_RICK);

    //Draw the statistics and "pocket" area
    for( int y = 0; y < 8; y++ ) {
        retro.display.drawBitmapIndexed(128,y<<4, 16,16,wall, palette);
        retro.display.drawBitmapIndexed(144,y<<4, 16,16, wall, palette);
    }
    retro.display.drawBitmapIndexed(136,16, 16,16,totem,palette);
    retro.display.fillRect(132,32,155,39,0);

    drawLevelNumber(136,32,_level);

    drawPocket();
}



Screen GameScreen::Update()
{
    frame++;
    
    if( _state == 0 ) {
        drawLevel();
        _state = 1;
    }
    
    if( _gameOver )
    {
        if( (frame % 3) == 0 )
        {
            sprites[12].Mirrored = !sprites[12].Mirrored;
            drawSprite(_rickX,_rickY,12);
        }        
    }

    if(retro.pressed(BTN_LEFT)) {
        //sfx(0,0);
        if( !_gameOver )
            moveleft();
    }
    if(retro.pressed(BTN_RIGHT)) {
        //sfx(1,0);
        if( !_gameOver )
        moveright();
    }
    if(retro.pressed(BTN_UP)) {
        //sfx(2,0);
        if( !_gameOver )
            moveup();
    }
    if(retro.pressed(BTN_DOWN)) {
        //sfx(3,0);
        if( !_gameOver )
        movedown();
    }
    if(retro.pressed(BTN_ROBOT)) {
        _state = 0;
        return Menu;
    }
    
    if(retro.pressed(BTN_SHIP)) {
        if( _gameOver )
        {
            if( _level < 99 )
            {
                _level++;
                resetLevel();

//for some reason writing to eeprom sometime hangs the program                
//                _gameData[4]= _level;
//                _gameData[5] = _gameComplete;
//    
//                write_eeprom(_gameData, (char *)64, 8 );
            }
        }
    }

    return Game;
}


void drawSprite(int x, int y, int sprite)
{
    int screenx = x<<4;
    int screeny = y<<4;
    if( sprite == 0 )
        retro.display.fillRect(screenx,screeny, screenx+15,screeny+15,0);
    else {
        retro.display.drawBitmapIndexed(screenx,screeny, 16,16,sprites[sprite].Sprite, palette, sprites[sprite].Mirrored);
    }
}

void drawLevelNumber(int x, int y, int level)
{
    char buf[3];
    if( (level/10) > 0 )
        sprintf(buf,"%d",level);
    else
        sprintf(buf,"@%d",level);
    drawString(x,y,buf, palette_orange);
}