ECE 2035 final project

Dependencies:   mbed wave_player 4DGL-uLCD-SE MMA8452

main.cpp

Committer:
npatel387
Date:
2019-04-15
Revision:
2:22d36e7740f1
Parent:
0:35660d7952f7

File content as of revision 2:22d36e7740f1:

// Project includes
#include "globals.h"
#include "hardware.h"
#include "map.h"
#include "graphics.h"
#include "speech.h"

int omnipotent = 0;
int mapToggle = 0;
int finished = 0;
int walk = 0;

// Functions in this file
int get_action (GameInputs inputs);
int update_game (int action);
void draw_game (int init);
void init_main_map ();
void init_second_map();
int go_up(int x, int y, int i);
int go_down(int x, int y, int i);
int go_right(int x, int y, int i);
int go_left(int x, int y, int i);
int interact(int x, int y);
int NPC_move(int x, int y);
int main ();

/**
 * The main game state. Must include Player locations and previous locations for
 * drawing to work properly. Other items can be added as needed.
 */
struct {
    int x,y;    // Current locations
    int px, py; // Previous locations
    int has_key;
    int omnipotent;
    int has_sword;
    int has_shield;
    int has_platebody;
    int health;
    int num_phats;
} Player;

/**
 * Given the game inputs, determine what kind of update needs to happen.
 * Possbile return values are defined below.
 */
#define NO_ACTION 0
#define ACTION_BUTTON 1
#define MENU_BUTTON 2
#define OMNIPOTENT 7
#define GO_LEFT 3
#define GO_LEFT2 10
#define GO_RIGHT 4
#define GO_RIGHT2 11
#define GO_UP 5
#define GO_UP2 12
#define GO_DOWN 6
#define GO_DOWN2 13
#define BAD_END 8
#define GOOD_END 9

char *questStartLine1 = "Wow a knight!what";
char *questStartLine2 = "perfect timing!";
char *questStartLine3 = "I beg you,please";
char *questStartLine4 = "defeat the evil";
char *questStartLine5 = "dragon Elvarg!";
char *questStartLine6 = "He resides in a";
char *questStartLine7 = "cave to the NEast";
char *questStartLine8 = "You'll need an";
char *questStartLine9 =  "antifire shield";
char *questStartLine10 = "and Excalibur!";
char *questStartLine11 = "I hear Excalibur";
char *questStartLine12 = "can be found in";
char *questStartLine13 = "the same cave.";
char *questStartLine14 = "It'll be embedded";
char *questStartLine15 = "in a stone there.";
char *questStartLine16 = "Take my antifire";
char *questStartLine17 = "shield, man.";
char *questStartLine18 = "If you do defeat";
char *questStartLine19 = "Elvarg, take";
char *questStartLine20 = "the key he drops.";
char *questStartLine21 = "It unlocks his";
char *questStartLine22 = "treasure room";
char *questStartLine23 = "to the south.";
char *questStartLine24 = "Good luck!";
char *questStartLine25 = "Come back when";
char *questStartLine26 = "you're done!";

char *questHasShield1 = "Go to the cave,";
char *questHasShield2 = "find Excalibur,";
char *questHasShield3 = "and defeat Elvarg";

char *questHasSword1 = "Go fight Evlarg!";

char *questHasPlatebody1 = "Thank you!!!!";
char *questHasPlatebody2 = "You have helped";
char *questHasPlatebody3 = "me out.I am";
char *questHasPlatebody4 = "very grateful!";

char *findSword1 = "You pull and";
char *findSword2 = "wield Excalibur.";

char *elvarg1 = "You approach";
char *elvarg2 = "Elvarg attacks...";
char *elvarg3a1 = "With no shield,";
char *elvarg3a2 = "you are burnt to";
char *elvarg3a3 = "a crisp.(-10 HP)";
char *elvarg3b1 = "You have no sword";
char *elvarg3b2 = "Your punches have";
char *elvarg3b3 = "no effect so you";
char *elvarg3b4 = "are badly wounded";
char *elvarg3b5 = "(-9 HP)";
char *elvarg3c1 = "Using Excalibur &";
char *elvarg3c2 = "the shield you";
char *elvarg3c3 = "defeat Elvarg w/";
char *elvarg3c4 = "only minor wounds";
char *elvarg3c5 = "(-3 HP).He drops";
char *elvarg3c6 = "the key to his";
char *elvarg3c7 = "treasure room!";


int get_action(GameInputs inputs)
{
    if(Player.health <= 0)
    {
        return BAD_END;   
    }
    else if(finished)
    {
        return GOOD_END;   
    }
    else if(!inputs.b1){
        return ACTION_BUTTON;
    }
    else if(!inputs.b2){
        return OMNIPOTENT;
    }
    else if(!inputs.b3){
        return MENU_BUTTON;   
    }
    else if(inputs.ax >= .2){
        if(inputs.ax >= .4)
            return GO_LEFT2;
        else
            return GO_LEFT;
    }
    else if(inputs.ax <= -.3){
        if(inputs.ax <= -.5)
            return GO_RIGHT2;
        else
            return GO_RIGHT;
    }
    else if(inputs.ay >= .3){
        if(inputs.ay >= .5)
            return GO_DOWN2;
        else
            return GO_DOWN;
    }
    else if(inputs.ay <= -.2){
        if(inputs.ay <= -.4)
            return GO_UP2;
        else
            return GO_UP;
    }
    else{
        return NO_ACTION;
    }
}

/**
 * Update the game state based on the user action. For example, if the user
 * requests GO_UP, then this function should determine if that is possible by
 * consulting the map, and update the Player position accordingly.
 * 
 * Return values are defined below. FULL_DRAW indicates that for this frame,
 * draw_game should not optimize drawing and should draw every tile, even if
 * the player has not moved.
 */
#define NO_RESULT 0
#define GAME_OVER 1
#define FULL_DRAW 2
#define RUN 3
int update_game(int action)
{
    // Save player previous location before updating
    Player.px = Player.x;
    Player.py = Player.y;
    
    // Do different things based on the each action.
    // You can define functions like "go_up()" that get called for each case.
    switch(action)
    {
        case GO_UP: 
            if(go_up(Player.x, Player.y, 0))
                return FULL_DRAW;
            break;
        case GO_UP2:
            if(go_up(Player.x, Player.y,1))
                return RUN;
            break;
        case GO_LEFT: 
            if(go_left(Player.x, Player.y, 0))
                return FULL_DRAW;
            break;
        case GO_LEFT2:
            if(go_left(Player.x, Player.y, 1))
                return RUN;
            break;          
        case GO_DOWN: 
            if(go_down(Player.x, Player.y, 0))
                return FULL_DRAW;  
            break;
        case GO_DOWN2:
            if(go_down(Player.x, Player.y, 1))
                return RUN;
            break;
        case GO_RIGHT: 
            if(go_right(Player.x, Player.y, 0))
                return FULL_DRAW; 
            break;
        case GO_RIGHT2:
            if(go_right(Player.x, Player.y, 1))
                return RUN;
            break;
        case ACTION_BUTTON: 
            if(interact(Player.x, Player.y))
                return FULL_DRAW; 
            break;
        case OMNIPOTENT: //toggle omnipotent mode
            Player.omnipotent = !omnipotent;
            omnipotent = !omnipotent;
            break;
        case MENU_BUTTON: //open menu
            draw_menu(Player.omnipotent, Player.has_sword, Player.has_shield, Player.has_platebody, Player.has_key);
            return FULL_DRAW;
        case BAD_END:
            return BAD_END;
        case GOOD_END:
            return GOOD_END;
        default:
            return NPC_move(Player.x, Player.y);
    }
    return NO_RESULT;
}

//Functions to move player
int go_up(int x, int y, int i)
{
    MapItem *item = get_north(x, y);
    MapItem *item2 = get_north(x,y-1);
    if((item->walkable && item->type != PLANT2) || Player.omnipotent)
    {
        if((i && item2->walkable && item2->type != PLANT2) || Player.omnipotent)
        {
            Player.y = Player.y - 2;
            return RUN;   
        }
        Player.y--;
        return 1;    
    }
    else if(item->type == PLANT2)
    {
        Player.health--;
        speaker.period(1.0/150.0); // 500hz period
        speaker =0.25; //25% duty cycle - mid range volume
        wait(.02);
        speaker=0.0; // turn off audio
        wait(0.5);
        return 1;
    }
    else if(item->type == ROLLING && item2 == NULL)
    {
        map_erase(x,y-1);
        add_rolling(x,y-2);
        Player.y--;  
        return 1;
    }
    return 0;
}

int go_down(int x, int y, int i)
{
    MapItem *item = get_south(x, y);
    MapItem *item2 = get_south(x, y+1);
    if((item->walkable && item->type != PLANT2) || Player.omnipotent)
    {
        if((i && item2->walkable && item2->type != PLANT2) || Player.omnipotent)
        {
            Player.y = Player.y + 2;
            return RUN;   
        }
        Player.y++;
        return 1;    
    }
    else if(item->type == PLANT2)
    {
        Player.health--;
        speaker.period(1.0/150.0); // 500hz period
        speaker =0.25; //25% duty cycle - mid range volume
        wait(.02);
        speaker=0.0; // turn off audio
        wait(0.5);
        return 1;   
    }
    else if(item->type == ROLLING && item2 == NULL)
    {
        map_erase(x,y+1);
        add_rolling(x,y+2);
        Player.y++;  
        return 1;
    }
    return 0;
}

int go_left(int x, int y, int i)
{
    MapItem *item = get_west(x, y);
    MapItem *item2 = get_west(x-1,y);
    if((item->walkable && item->type != PLANT2) || Player.omnipotent)
    {
        if((i && item2->walkable && item2->type != PLANT2) || Player.omnipotent)
        {
            Player.x = Player.x - 2;
            return RUN;   
        }
        Player.x--;
        return 1;    
    }
    else if(item->type == PLANT2)
    {
        Player.health--;
        speaker.period(1.0/150.0); // 500hz period
        speaker =0.25; //25% duty cycle - mid range volume
        wait(.02);
        speaker=0.0; // turn off audio
        wait(0.5);
        return 1;   
    }
    else if(item->type == ROLLING && item2 == NULL)
    {
        map_erase(x-1,y);
        add_rolling(x-2,y);
        Player.x--;  
        return 1;
    }
    return 0;
}

int go_right(int x, int y, int i)
{
    MapItem *item = get_east(x, y);
    MapItem *item2 = get_east(x+1, y);
    if((item->walkable && item->type != PLANT2) || Player.omnipotent)
    {
        if((i && item2->walkable && item2->type != PLANT2) || Player.omnipotent)
        {
            Player.x = Player.x + 2;
            return RUN;   
        }
        Player.x++;
        return 1;    
    }
    else if(item->type == PLANT2)
    {
        Player.health--;
        speaker.period(1.0/150.0); // 500hz period
        speaker =0.25; //25% duty cycle - mid range volume
        wait(.02);
        speaker=0.0; // turn off audio
        wait(0.5);
        return 1;   
    }
    else if(item->type == ROLLING && item2 == NULL)
    {
        map_erase(x+1,y);
        add_rolling(x+2,y);
        Player.x++;  
        return 1;
    }
    return 0;
}

int interact(int x, int y)
{
    MapItem *north = get_north(x,y);
    MapItem *south = get_south(x,y);
    MapItem *east = get_east(x,y);
    MapItem *west = get_west(x,y);
    if(north->type == STARTNPC || south->type == STARTNPC || east->type == STARTNPC || west->type == STARTNPC)
    {
        if(Player.has_platebody)
        {
            speech(questHasPlatebody1, questHasPlatebody2, questHasPlatebody3, questHasPlatebody4);
            finished = 1;
            return 1;
        }
        else if(!Player.has_platebody && Player.has_key)
        {
            speech("Search the", "treasure room", "first! Then", "come talk");
            return 1;   
        }
        else if(Player.has_sword)
        {
            speech(questHasSword1, "", "", "");
            return 1;
        }
        else if(Player.has_shield )
        {
            speech(questHasShield1, questHasShield2, questHasShield3, "");
            return 1;
        }
        else
        {
            speech(questStartLine1, questStartLine2, questStartLine3, questStartLine4);
            speech(questStartLine5, questStartLine6, questStartLine7, questStartLine8);
            speech(questStartLine9, questStartLine10, questStartLine11, questStartLine12);
            speech(questStartLine13, questStartLine14, questStartLine15, questStartLine16);
            speech(questStartLine17, questStartLine18, questStartLine19, questStartLine20);
            speech(questStartLine21, questStartLine22, questStartLine23, questStartLine24);
            speech(questStartLine25, questStartLine26,"","");
            Player.has_shield = 1;
            return 1;
        }
    } 
    else if(north->type == CAVE || south->type == CAVE || east->type == CAVE || west->type == CAVE)
    {
        if(mapToggle == 0)
        {
            mapToggle = !mapToggle;
            set_active_map(1);
            Player.x = 5;
            Player.y = 5;
        }
        else
        {
            mapToggle = !mapToggle;
            set_active_map(0);
            Player.x = 39;
            Player.y = 4;
        }
        return 1;
    }
    else if(north->type == SWORDINSTONE || south->type == SWORDINSTONE || east->type == SWORDINSTONE || west->type == SWORDINSTONE)
    {
        if(Player.has_shield)
        {
            Player.has_sword = 1;
            speech(findSword1, findSword2, "", "");
            map_erase(15,15);
            draw_plant(15,15);
        }
        else
        {
            speech("You should", "talk to the man", "where you started", "first");   
        }
        return 1;
    }
    else if(north->type == ELVARG || south->type == ELVARG || east->type == ELVARG || west->type == ELVARG)
    {
        speech(elvarg1, elvarg2, "", "");
        if(!Player.has_shield)
        {
            speech(elvarg3a1, elvarg3a2, elvarg3a3, "");
            Player.health = Player.health - 10;
            return 1;
        }
        else if(!Player.has_sword)
        {
            speech(elvarg3b1, elvarg3b2, elvarg3b3, elvarg3b4);
            speech(elvarg3b5, "", "", "");   
            Player.health = Player.health - 9;
            return 1;
        }
        else
        {
            Player.has_key = 1;
            speech(elvarg3c1, elvarg3c2, elvarg3c3, elvarg3c4);
            speech(elvarg3c5, elvarg3c6, elvarg3c7, "");  
            map_erase(10,10);
            Player.health = Player.health - 3; 
            return 1;
        }
    }
    else if(north->type == GATE || south->type == GATE || east->type == GATE || west->type == GATE)
    {
        if(Player.has_key)
        {
            speech("You unlock the", "gate with the key", "", "");
            map_erase(10,45);   
            return 1;
        }  
        else
        {
            speech("Requires Elvarg's", "key to open", "", "");
            return 1;
        }
    }
    else if(north->type == TREASURE || south->type == TREASURE || east->type == TREASURE || west->type == TREASURE)
    {
           Player.has_platebody = 1;
           speech("You are amazed", "to find a", "legendary rune", "platebody.");
           speech("you equip the", "legendary armor.", "Time to return", "to that weird");
           speech("guy. His request", "has been", "fulfilled.", "");
           return 1;
    }
    else if(north->type == PHAT || south->type == PHAT || east->type == PHAT || west->type == PHAT)
    {
            if(Player.num_phats == 0)
                speech("find all 5", "hidden party", "hats!", "");
            Player.num_phats++;
            if(Player.num_phats == 5)
            {
                speech("you found all", "5 party hats!", "You are very", "special!");  
            }
            if(north->type == PHAT)
                map_erase(x, y-1);
            if(south->type == PHAT)
                map_erase(x, y+1);
            if(east->type == PHAT)
                map_erase(x+1, y);
            if(west->type == PHAT)
                map_erase(x-1, y);
            return 1;
    }
    
    return 0;  
}

int NPC_move(int x, int y)
{
            if(!((Player.x == (7+!walk)) && (Player.y == 7)) && (map_width() == 50))
            {
            wait_ms(200);
            map_erase(7+walk, 7);
            walk = !walk;
            add_startNPC(7+walk,7);
            wait_ms(200);
            return FULL_DRAW;
            }
            return 0;
}

/**
 * Entry point for frame drawing. This should be called once per iteration of
 * the game loop. This draws all tiles on the screen, followed by the status 
 * bars. Unless init is nonzero, this function will optimize drawing by only 
 * drawing tiles that have changed from the previous frame.
 */
void draw_game(int init)
{   
    if(init == BAD_END)
    {
        speaker.period(1.0/150.0); // 500hz period
        speaker =0.25; //25% duty cycle - mid range volume
        wait(.5);
        speaker=0.0; // turn off audio
        wait(0.5);
        draw_bad();
        wait(10000);
    }
    if(init == GOOD_END)
    {
        draw_good();
        wait(10000);
    }
    

    // Draw game border first
    if(init) draw_border();
    
    // Iterate over all visible map tiles
    for (int i = -5; i <= 5; i++) // Iterate over columns of tiles
    {
        for (int j = -4; j <= 4; j++) // Iterate over one column of tiles
        {
            // Here, we have a given (i,j)
            
            // Compute the current map (x,y) of this tile
            int x = i + Player.x;
            int y = j + Player.y;
            
            // Compute the previous map (px, py) of this tile
            int px = i + Player.px;
            int py = j + Player.py;
                        
            // Compute u,v coordinates for drawing
            int u = (i+5)*11 + 3;
            int v = (j+4)*11 + 15;
            
            // Figure out what to draw
            DrawFunc draw = NULL;
            if (init && i == 0 && j == 0 && get_here(Player.x, Player.y)->walkable != 2) // Only draw the player on init
            {
                if(init == RUN)
                {
                    draw_player(u,v,4);
                    wait_ms(100);
                }
                if(Player.has_platebody)
                    draw_player(u, v, 3);
                else if(Player.has_sword)
                    draw_player(u, v, 2);
                else if(Player.has_shield)
                    draw_player(u ,v, 1);
                else
                    draw_player(u ,v, 0);
                continue;
            }
            else if (x >= 0 && y >= 0 && x < map_width() && y < map_height()) // Current (i,j) in the map
            {
                MapItem* curr_item = get_here(x, y);
                MapItem* prev_item = get_here(px, py);
                if (init || curr_item != prev_item) // Only draw if they're different
                {
                    if (curr_item) // There's something here! Draw it
                    {
                        draw = curr_item->draw;
                    }
                    else // There used to be something, but now there isn't
                    {
                        draw = draw_nothing;
                    }
                }
            }
            else if (init) // If doing a full draw, but we're out of bounds, draw the walls.
            {
                draw = draw_wall;
            }

            // Actually draw the tile
            if (draw) draw(u, v);
        }
    }

    // Draw status bars    
    draw_upper_status(Player.x,Player.y);
    draw_lower_status(Player.health, Player.num_phats);
}


/**
 * Initialize the main world map. Add walls around the edges, interior chambers,
 * and plants in the background so you can see motion.
 */
void init_main_map()
{
    // "Random" plants
    Map* map = set_active_map(0);
    for(int i = map_width() + 3; i < map_area(); i += 39)
    {
        add_plant(i % map_width(), i / map_width());
    }
    pc.printf("plants\r\n");
        
    pc.printf("Adding walls!\r\n");
    add_wall(0,              0,              HORIZONTAL, map_width());
    add_wall(0,              map_height()-1, HORIZONTAL, map_width());
    add_wall(0,              0,              VERTICAL,   map_height());
    add_wall(map_width()-1,  0,              VERTICAL,   map_height());
    pc.printf("Walls done!\r\n");

    //draw in treasure room
    add_wall(6, 45, VERTICAL, 4);
    add_wall(7, 45, HORIZONTAL, 3);
    add_gate(10,45);
    add_wall(11, 45, HORIZONTAL, 3);
    add_wall(14, 45, VERTICAL, 4);
    
    
    add_startNPC(7,7);
    pc.printf("NPCs!\r\n");
    
    add_boulder(3,3);
    
    add_cave(40,4);
    pc.printf("Cave!\r\n");
    add_rolling(39,4);
    add_rolling(41,4);
    add_rolling(40,5);
    add_rolling(40,3);
    
    add_treasure(10,47);
    pc.printf("Treasure!\r\n");
    
    add_phat(45,45);
    add_phat(25,25);
    add_phat(10,48);
    
    print_map();
}

void init_second_map()
{
    // "Random" plants
    Map* map = set_active_map(1);
    for(int i = map_width() + 3; i < map_area(); i += 39)
    {
        add_plant2(i % map_width(), i / map_width());
    }
    pc.printf("plants\r\n");
        
    pc.printf("Adding walls!\r\n");
    add_wall(0,              0,              HORIZONTAL, map_width());
    add_wall(0,              map_height()-1, HORIZONTAL, map_width());
    add_wall(0,              0,              VERTICAL,   map_height());
    add_wall(map_width()-1,  0,              VERTICAL,   map_height());
    pc.printf("Walls done!\r\n");

    //add sprites
        add_cave(4,5);
        add_elvarg(10,10);
        add_swordInStone(15,15);
        add_phat(3,10);
        add_phat(10,17);
    print_map();
}

/**
 * Program entry point! This is where it all begins.
 * This function orchestrates all the parts of the game. Most of your
 * implementation should be elsewhere - this holds the game loop, and should
 * read like a road map for the rest of the code.
 */
int main()
{
    // First things first: initialize hardware
    ASSERT_P(hardware_init() == ERROR_NONE, "Hardware init failed!");

    draw_start();
    
    // Initialize the maps
    maps_init();
    init_second_map();
    init_main_map();
    
    // Initialize game state
    set_active_map(0);
    Player.x = Player.y = 5;
    Player.has_key = 0;
    Player.has_sword = 0;
    Player.has_shield = 0;
    Player.has_platebody = 0;
    Player.health = 10;

    // Initial drawing
    uLCD.filled_rectangle(0, 0, 127, 8, BLACK);
    uLCD.filled_rectangle(0, 119, 127, 127, BLACK);
    draw_game(true);
    
    // Variables
    GameInputs in;
    int action;
    int draw;
    // Main game loop
    while(1)
    {
        // Timer to measure game update speed
        Timer t; t.start();
        
        // Actuall do the game update:
        // 1. Read inputs     
        in = read_inputs();   
        // 2. Determine action (get_action)
        action = get_action(in);        
        // 3. Update game (update_game)
        draw = update_game(action);
        // 3b. Check for game over
        // 4. Draw frame (draw_game)
        draw_game(draw);
        
        // 5. Frame delay
        t.stop();
        int dt = t.read_ms();
        if (dt < 100) wait_ms(100 - dt);
    }
}