Adventure game written for ECE2035 at the Georgia Institute of Technology

Dependencies:   mbed wave_player 4DGL-uLCD-SE MMA8452

main.cpp

Committer:
trmontgomery
Date:
2019-10-26
Revision:
5:93a4c396c1af
Parent:
4:cdc54191ff07

File content as of revision 5:93a4c396c1af:

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

// 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_house_map();
int main ();

Player p1;
int omni_en = 0;
int in_house;
int house_enabled = false;
int door_open = false;
int game_over = false;
int switch_axis = 0;


/*Functions to for player to interact with the map
*/

void interact_npc(MapItem* object){
    if (p1.rock_pushed && p1.gems >= 3){
        p1.quest_complete = true;
    }
    NonPlayer* n = (NonPlayer*)object -> data;
    if(n -> quest_requested == false){
        speech("Find 3 red gems", "and move the rock", "in the backyard", "then I will give you gold!");
        n -> quest_requested = true;
    } else if (p1.quest_complete == false){ //should quest complete be in the player struct?
        speech("get it boi! What", "are u waitin for?", "You must finish ", "the quest!!!!");
    } else if (n -> has_key == true) {
        speech("Congrats!", "Thank you for", "completing the", "quest! Here's the key!");
        n -> has_key = false;
        p1.has_key = true;
    } else if(n -> has_key == false){
        speech("Congrats!", "Quest complete.", "", "");
    }
}

void player_hurt(){
    //lower health bar
        p1.health+=10;   
        for(int i = 0; i < 2; i++){ 
        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.1);
        }
}


/**
 * Given the game inputs, determine what kind of update needs to happen.
 * Possbile return values are defined below.
 */
#define NO_ACTION 0
#define BUTTON1 1 //omni button
#define BUTTON2 2 //toggle/action button
#define BUTTON3 3 //scroll/select?
#define MENU_BUTTON 4
#define GO_LEFT 5
#define GO_RIGHT 6
#define GO_UP 7
#define GO_DOWN 8
int get_action(GameInputs inputs)
{
    double y_axis;
    double x_axis; 
    if(switch_axis){
        y_axis = inputs.ax;
        x_axis = inputs.ay;
    }else{
        y_axis = inputs.ay;
        x_axis = inputs.ax;
    }
    if(!inputs.b1){
        return BUTTON1;
    }
    if(!inputs.b2){
        return BUTTON2;
    }
    if (!inputs.b3){
        return MENU_BUTTON;
    }
    if ((y_axis > 0.05 && y_axis < 0.9) && abs(x_axis) < .9){
        return GO_LEFT;    
    } else if ((y_axis < -0.05 && y_axis > -0.9) && abs(x_axis) < .9){
        return GO_RIGHT;
    }else if (x_axis > 0.05 && x_axis < 0.9){
        return GO_DOWN;
    }else if (x_axis < -0.05 && x_axis > -0.9){
        return GO_UP;
    }
    return NO_ACTION;
}

void update_npcs(){
    //Map* m = get_active_map();
    /*int h = map_height();
    int w = map_width();
    
    for(int i = 0; i < w; i++){
        for(int j = 0; j < h; j++){
            if (MapItem* object = get_here(i, j)){
                if (object -> type == EXTRA){
                    Extra* d = (Extra*)object -> data;
                    d -> e = true;
                    add_extra(i+1, j);
                }
            } 
        }
    }
    for(int i = 0; i < w; i++){
        for(int j = 0; j < h; j++){
            if (MapItem* object = get_here(i, j)){
                if (object -> type == EXTRA){
                    Extra* d = (Extra*)object -> data;
                    if(d -> e == true) map_erase(i, j);
                }
            } 
        }
    }*/
}

/**
 * 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 NO_DRAW   3
int update_game(int action)
{
    // Save player previous location before updating
    p1.px = p1.x;
    p1.py = p1.y;
    
    // Do different things based on the each action.
    // You can define functions like "go_up()" that get called for each case.
    switch(action) //have a function called for each type of object. Pass a pointer to the object to the interact function
    {
        case GO_UP: 
            if (MapItem* object = get_north(p1.x, p1.y)){
                //if (object -> type == NPC){
                    //interact_npc(object);
                  //  return NO_RESULT;
                 if (object -> type == LBUSH){
                    p1.y = p1.y - 1;
                    return NO_DRAW;
                } else if(object -> type == SPIKE){
                    player_hurt(); 
                } else if(object -> type == GEM){
                    //add to gem score
                    p1.gems++;    
                    if (p1.gems >= 3 && p1.quest_complete < 1){
                        p1.quest_complete += 0.5;
                    }
                    map_erase(p1.x, p1.y-1);
                } else if (!omni_en && object -> walkable == false){
                    return NO_RESULT;
                }
            }
            p1.y = p1.y - 1;
            return FULL_DRAW;
        case GO_LEFT:   
            if (MapItem* object = get_west(p1.x, p1.y)){
                if (object -> type == ROCK){
                    add_rock(p1.x + 2, p1.y);
                    map_erase(p1.x+1, p1.y);
                    p1.x = p1.x + 1;
                    p1.rock_pushed = true;
                    return FULL_DRAW;
                    
                }else if (object -> type == LBUSH){
                    p1.x = p1.x + 1;
                    return NO_DRAW; 
                }else if(object -> type == SPIKE){
                    player_hurt();   
                } else if(object -> type == GEM){
                    //lower health bar
                    p1.gems++;  
                    if (p1.gems >= 3 && p1.quest_complete < 1){
                        p1.quest_complete += 0.5;
                    }
                    map_erase(p1.x+1, p1.y);
                }else if(object -> type == DOOR){
                    if (in_house){
                        set_active_map(0);
                        p1.x = 3;
                        p1.y = 7;
                        in_house = 0;
                    }else{
                        set_active_map(1);
                        p1.x = 6;
                        p1.y = 7; 
                        in_house = 1;
                    }
               } if (!omni_en && object -> walkable == false){
                     return NO_RESULT;
                }
            }
            p1.x = p1.x + 1;
            return FULL_DRAW; 
        case GO_DOWN:   
            if (MapItem* object = get_south(p1.x, p1.y)){
                if (object -> type == DOOR){
                    if (p1.has_key == true){
                        map_erase(p1.x, p1.y + 1);
                        p1.y = p1.y + 1;
                        door_open = true;
                        return FULL_DRAW;
                    }
                    return NO_RESULT;
                }if (object -> type == LBUSH){
                    p1.y = p1.y + 1;
                    return NO_DRAW;
                }else if(object -> type == SPIKE){
                    player_hurt(); 
                } else if(object -> type == GEM){
                    //add to gem score
                    p1.gems++;   
                    if (p1.gems >= 3 && p1.quest_complete < 1){
                        p1.quest_complete += 0.5;
                    } 
                    map_erase(p1.x, p1.y+1);
                }
                if (!omni_en && object -> walkable == false){
                    return NO_RESULT;
                }    
            }
            p1.y = p1.y + 1;
            return FULL_DRAW;
            
        case GO_RIGHT:  
            if (MapItem* object = get_east(p1.x, p1.y)){
                if (object -> type == GOAL){
                    if(door_open == true){
                        game_over = true;
                        uLCD.locate(1,1);
                        uLCD.printf("Goal reached");
                        return NO_RESULT;
                    }
                    
                }//else if(object -> type == GDOOR){
                    
                //} //add a bit of a wait here to simulate going into the house
                else if(object -> type == SPIKE){
                    player_hurt();
    
                } if (object -> type == LBUSH){
                    p1.x = p1.x - 1;
                    return NO_DRAW;
                    }else if(object -> type == GEM){
                    //add to gem score
                    p1.gems++; 
                    if (p1.gems >= 3 && p1.quest_complete < 1){
                        p1.quest_complete += 0.5;
                    }   
                    map_erase(p1.x-1, p1.y);
                }
                if (!omni_en && object -> walkable == false){
                    return NO_RESULT;
                }
            }
            p1.x = p1.x - 1;
            return FULL_DRAW;
           
        case BUTTON1: 
            omni_en = !omni_en;
            break;
        case BUTTON2: 
            MapItem* object1 = get_north(p1.x, p1.y); 
            MapItem* object2 = get_south(p1.x, p1.y); 
            MapItem* object3 = get_east(p1.x, p1.y); 
            MapItem* object4 = get_west(p1.x, p1.y);
            if(object1 ->type == NPC){ 
                interact_npc(object1);
            } else if (object2->type ==NPC){
                interact_npc(object2);
            }else if (object3->type ==NPC){
                interact_npc(object3);
            } else if (object4->type ==NPC){
                interact_npc(object4);
            } 
            else if (object1 ->type == GDOOR|| object2->type == GDOOR || object3 ->type == GDOOR|| object4 ->type  == GDOOR){ 
                if (p1.has_key){
                    door_open = true;
                    map_erase(p1.x-1, p1.y);
                } else{
                    speech("Door Locked.", "Must get key.", "Door Locked.", "Must get key.");
                }
            }
            break;
        case MENU_BUTTON:
            PauseMenu pause_m(&p1);
            pause_m.display();
            switch_axis = pause_m.get_config();
            //switch_axis = ingamemenu(&p1);
            
        break;
        default: break;
    }
    return NO_RESULT;
}



/**
 * 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)
{
   // int param1;
    //int param2; 
    // 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 + p1.x;
            int y = j + p1.y;
            
            // Compute the previous map (px, py) of this tile
            int px = i + p1.px;
            int py = j + p1.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 && init != NO_DRAW) // Only draw the player on init (this is when the loop hits the center of the screen)
            {
                draw_player(u, v, p1.has_key);
            
                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
                    {
                        //if (curr_item -> type == ROCK){
                            //set parameters
                          //  Rock* r = (Rock*) curr_item -> data;
                            //param1 = r -> x;
                            //param2 = r -> y;
                            //}
                        draw = curr_item->draw;
                    }
                    else // There used to be something, but now there isn't
                    {
                        if (in_house){
                            draw = draw_house_floor;
                        }else{
                        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
            //MapItem* curr_item = get_here(x, y);
            //MapItem* prev_item = get_here(px, py);
            if (draw){
               // if(curr_item){
                 //   if(curr_item -> type == ROCK){
                   // draw(param1, param2);
                    //}
                //}
                draw(u,v);
            }
        }
    }

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


/**
 * 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()
{
    Map* map = set_active_map(0);
    // "Random" plants
    
    maps_init(50, 50, 5);
    for(int i = map_width() + 3; i < map_area(); i += 39)
    {
        add_plant(i % map_width(), i / map_width());
    }
    
    srand(time(NULL));
    for(int i = 0; i < 30; i++)
    {
        int x = rand()%((map_width()+1)-1) + 1;
        int y = rand()%((map_height()+1)-1) + 1;
        add_spike(x, y);
    }
    
    for(int i = 0; i < 30; i++)
    {
        int x = rand()%((map_width()+1)-1) + 1;
        int y = rand()%((map_height()+1)-1) + 1;
        add_gem(x, y, 1);
    }
    
    for(int i = 0; i < 15; i++)
    {
        int x = rand()%((map_width()+1)-1) + 1;
        int y = rand()%((map_height()+1)-1) + 1;
        add_gem(x, y, 2);
    }
    
    for(int i = 0; i < 9; i++)
    {
        int x = rand()%((map_width()+1)-1) + 1;
        int y = rand()%((map_height()+1)-1) + 1;
        add_gem(x, y, 3);
    }
    
    pc.printf("plants\r\n");
        
    p1.x = 41;
    p1.y = 10;
    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());
    add_rock(13, 2);
    add_redhouse(9, 1, 1);
    add_bush_rect(1, 19, 11, 38);
    add_bush_rect(20, 17, 23, 21);
    pc.printf("Walls done!\r\n");

    print_map();
}

void init_house_map()
{
    
    Map* map = set_active_map(1); //one is a house map
    //in_house = 1;
    maps_init(10, 10, 5);
    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());
    
    add_wall(1, 4, HORIZONTAL, 3);
    add_wall(4, 4, VERTICAL, 4);
    add_gdoor(4,8);
    
    add_npc(6, 2); //place NPC in the house
    add_door(8, 8);
    add_goal(1, 8);
}

/**
 * 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()
{
    
    StartMenu start_m = StartMenu();   
    int st = start_m.display();
    // First things first: initialize hardware
    ASSERT_P(hardware_init() == ERROR_NONE, "Hardware init failed!");

    // Initialize the maps
    
    init_house_map();
    init_main_map();
    
    
    // Initialize game state
    //set_active_map(0); //this doesn't need to be here twice
    p1.health = 0;
    p1.lives = 3;
    p1.x = p1.y = 5;
    p1.quest_complete = false;

    // Initial drawing
    draw_game(true);

    // Main game loop
    while(p1.lives)
    {
        // Timer to measure game update speed
        Timer t; t.start();
        
        // Actually do the game update:
        // 1. Read inputs  
            GameInputs in = read_inputs();
        // 2. Determine action (get_action) 
            int a = get_action(in);       
        // 3. Update game (update_game)
            int u = update_game(a);
            update_npcs();
        // 3b. Check for game over
        draw_game(u);
        if (p1.health >= 120){
            int result = gameover(--p1.lives);
            if (result){
                init_main_map();
                p1.health = 0;
                //Player.lives--;
                p1.x = p1.y = 5;
                p1.quest_complete = false;
            }} 
        if (game_over == true){
            break;
        }
            
        //else if(house_enabled == true){
            
            //Player.x = Player.y = 5; //idk if this should be here...
        //}
        // 4. Draw frame (draw_game)
        
        
        
        // 5. Frame delay
        t.stop();
        int dt = t.read_ms();
        if (dt < 100) wait_ms(100 - dt);
    }
    draw_game_over();
}