SNAKE GAME
Dependencies: mbed wave_player 4DGL-uLCD-SE MMA8452
main.cpp
- Committer:
- congvu
- Date:
- 2020-11-25
- Revision:
- 0:24041b847eb5
File content as of revision 0:24041b847eb5:
//================================================================= // The main program file. // // Copyright 2020 Georgia Tech. All rights reserved. // The materials provided by the instructor in this course are for // the use of the students currently enrolled in the course. // Copyrighted course materials may not be further disseminated. // This file must not be made publicly available anywhere. //================================================================== // Project includes #include "globals.h" #include "hardware.h" #include "map.h" #include "graphics.h" #include "snake.h" #include "mbed.h" #include "Speaker.h" //#include "wave_player.h" #include "SDFileSystem.h" #include <math.h> #include<stdio.h> #define CITY_HIT_MARGIN 1 #define CITY_UPPER_BOUND (SIZE_Y-(LANDSCAPE_HEIGHT+MAX_BUILDING_HEIGHT)) int go_right(int x, int y); int go_left(int x, int y); int go_up(int x, int y); int go_down(int x, int y); // Helper function declarations void playSound(char* wav); void makenoise(); /** * The main game state. Must include snake locations and previous locations for * drawing to work properly. Other items can be added as needed. */ /** * Given the game inputs, determine what kind of update needs to happen. * Possbile return values are defined below. */ Snake snake; Speaker mySpeaker(p25); //define mySpeaker and pin // Function prototypes /** * Given the game inputs, determine what kind of update needs to happen. * Possible return values are defined below. */ #define NO_RESULT 0 #define NO_ACTION 0 #define ACTION_BUTTON 1 #define MENU_BUTTON 2 #define GO_LEFT 3 #define GO_RIGHT 4 #define GO_UP 5 #define GO_DOWN 6 #define GAME_OVER 7 #define FULL_DRAW 8 #define WON 9 #define FOODN 10 #define FOODS 11 #define FOODW 12 #define FOODE 13 // Get Actions from User (push buttons & accelerometer) // Based on push button and accelerometer inputs, determine which action // needs to be performed (may be no action). int get_action(GameInputs inputs) { MapItem* N=get_north(snake.head_x,snake.head_y); MapItem* S=get_south(snake.head_x,snake.head_y); MapItem* W=get_east(snake.head_x, snake.head_y); MapItem* E=get_west(snake.head_x, snake.head_y); if (button3 == 0) { //pause game while(button3 == 0) {} } if (button2 == 1) { // keep living if (snake.invincTimer == 0) snake.invincible = !snake.invincible; else snake.invincible = 1; } if ((E->type==WALL || N->type==WALL || S->type==WALL ||W->type==WALL) && !snake.invincible) return GAME_OVER; if (inputs.ay >= 0.2) return GO_DOWN; if (inputs.ay < -0.2) return GO_UP; if (inputs.ax < -0.2) return GO_RIGHT; if (inputs.ax >= 0.2) return GO_LEFT; else return NO_ACTION; } void checkCollisions() { //Check Objects here if (get_here(snake.head_x,snake.head_y)->type == RANDOM) { makenoise(); void* d = get_here(snake.head_x,snake.head_y)->data; if (((int)d % 4) + 13 == 13) snake.pointLockTime = 8000; if (((int)d % 4) + 13 == 14) snake.speedupTime = 16000; if (((int)d % 4) + 13 == 15) snake.slowdownTime = 10000; if (((int)d % 4) + 13 == 16) { if (snake.length > 1) { map_erase(snake.locations[snake.length - 2].x, snake.locations[snake.length - 2].y); map_erase(snake.locations[snake.length - 1].x, snake.locations[snake.length - 1].y); snake.length -= 2; } } else snake.pointLockTime = 10000; return; } if (get_here(snake.head_x,snake.head_y)->type == INVINC) { makenoise(); snake.invincTimer = 10000; snake.invincible = 1; } //Check Goodie if (get_here(snake.head_x,snake.head_y)->type == GOODIE) { // Speaker makenoise(); snake.length++; if (snake.pointLockTime == 0) snake.score++; } if (get_here(snake.head_x,snake.head_y)->type == INCLENGTH) { // Speaker makenoise(); snake.length += 6; } if (get_here(snake.head_x,snake.head_y)->type == MOVING) { makenoise(); snake.score += 5; //hit moving will add 5 score } if (get_here(snake.head_x,snake.head_y)->type == POISON) { makenoise(); snake.pointLockTime += 8000; //8 seccong } if (get_here(snake.head_x,snake.head_y)->type == SPEEDUP) { makenoise(); snake.speedupTime += 16000; //16 sec } if (get_here(snake.head_x,snake.head_y)->type == SLOWDOWN) { makenoise(); snake.slowdownTime += 10000; //10 sec } if (get_here(snake.head_x,snake.head_y)->type == DECLENGTH) { makenoise(); if (snake.length > 1) { map_erase(snake.locations[snake.length - 2].x, snake.locations[snake.length - 2].y); map_erase(snake.locations[snake.length - 1].x, snake.locations[snake.length - 1].y); snake.length -= 2; } } } /** * 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 snake 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 snake has not moved. */ int update_game(int action) { snake.head_px = snake.head_x; snake.head_py = snake.head_y; int oldx = snake.head_px; int oldy = snake.head_py; map_erase(oldx, oldy); switch(action) { case GO_UP: snake.head_y -= 1; checkCollisions(); //Check un-walkable object if (get_here(snake.head_x, snake.head_y)->type != SNAKE_HEAD && get_here(snake.head_x, snake.head_y)->walkable == 0 && !snake.invincible) { action = GAME_OVER; break; } //Add snake head now add_snake_head(snake.head_x, snake.head_y); //Check max length if (snake.length == SNAKE_MAX_LENGTH && !snake.invincible) { action = GAME_OVER; break; } for (int i = 0; i < snake.length; i++) { map_erase(oldx, oldy); int tempy = snake.locations[i].y; snake.locations[i].y = oldy; int tempx = snake.locations[i].x; snake.locations[i].x = oldx; oldy = tempy; oldx = tempx; map_erase(oldx, oldy); if (i < snake.length - 1) add_snake_body(snake.locations[i].x, snake.locations[i].y); else add_snake_tail(snake.locations[i].x, snake.locations[i].y); } return ACTION_BUTTON; case GO_LEFT: snake.head_x -= 1; checkCollisions(); //Check un-walkable object if (get_here(snake.head_x, snake.head_y)->type != SNAKE_HEAD && get_here(snake.head_x, snake.head_y)->walkable == 0 && !snake.invincible) { action = GAME_OVER; break; } add_snake_head(snake.head_x, snake.head_y); //Check max length if (snake.length == SNAKE_MAX_LENGTH && !snake.invincible) { action = GAME_OVER; break; } for (int i = 0; i < snake.length; i++) { map_erase(oldx, oldy); int tempy = snake.locations[i].y; snake.locations[i].y = oldy; int tempx = snake.locations[i].x; snake.locations[i].x = oldx; oldy = tempy; oldx = tempx; map_erase(oldx, oldy); if (i < snake.length - 1) add_snake_body(snake.locations[i].x, snake.locations[i].y); else add_snake_tail(snake.locations[i].x, snake.locations[i].y); } return ACTION_BUTTON; case GO_DOWN: snake.head_y += 1; checkCollisions(); //Check un-walkable object if (get_here(snake.head_x, snake.head_y)->type != SNAKE_HEAD && get_here(snake.head_x, snake.head_y)->walkable == 0 && !snake.invincible) { action = GAME_OVER; break; } add_snake_head(snake.head_x, snake.head_y); //Check max length if (snake.length == SNAKE_MAX_LENGTH && !snake.invincible) { action = GAME_OVER; break; } for (int i = 0; i < snake.length; i++) { int tempy = snake.locations[i].y; snake.locations[i].y = oldy; int tempx = snake.locations[i].x; snake.locations[i].x = oldx; oldy = tempy; oldx = tempx; map_erase(oldx, oldy); if (i < snake.length - 1) add_snake_body(snake.locations[i].x, snake.locations[i].y); else add_snake_tail(snake.locations[i].x, snake.locations[i].y); } return ACTION_BUTTON; case GO_RIGHT: snake.head_x += 1; checkCollisions(); //Over if hit un-walkable object or not invinvible if (get_here(snake.head_x, snake.head_y)->type != SNAKE_HEAD && get_here(snake.head_x, snake.head_y)->walkable == 0 && !snake.invincible) { action = GAME_OVER; break; } add_snake_head(snake.head_x, snake.head_y); //Check max length if (snake.length == SNAKE_MAX_LENGTH && !snake.invincible) { action = GAME_OVER; break; } for (int i = 0; i < snake.length; i++) { int tempy = snake.locations[i].y; snake.locations[i].y = oldy; int tempx = snake.locations[i].x; snake.locations[i].x = oldx; oldy = tempy; oldx = tempx; map_erase(oldx, oldy); if (i < snake.length - 1) add_snake_body(snake.locations[i].x, snake.locations[i].y); else add_snake_tail(snake.locations[i].x, snake.locations[i].y); } return ACTION_BUTTON; case GAME_OVER: {uLCD.color(RED); uLCD.cls(); uLCD.text_width(3); uLCD.text_height(3); uLCD.printf("GAME\n OVER"); uLCD.color(GREEN); uLCD.text_width(2); uLCD.text_height(2); uLCD.printf("\n\n\n\n Score %d", snake.score); uLCD.color(GREEN);} while(1 == 1); } if (action == GAME_OVER) { uLCD.color(RED); uLCD.cls(); uLCD.text_width(3); uLCD.text_height(3); uLCD.printf("GAME\n OVER"); uLCD.color(GREEN); uLCD.text_width(2); uLCD.text_height(2); uLCD.printf("\n\n\n\n Score %d", snake.score); uLCD.color(GREEN); while(1 == 1); } return NO_RESULT; } /** * Draw the upper status bar. */ void draw_upper_status() { uLCD.line(0, 9, 127, 9, GREEN); } /** * Draw the lower status bar. */ void draw_lower_status() { uLCD.line(0, 118, 127, 118, GREEN); } /** * Draw the border for the map. */ void draw_border() { uLCD.filled_rectangle(0, 9, 127, 14, WHITE); // Top uLCD.filled_rectangle(0, 13, 2, 114, WHITE); // Left uLCD.filled_rectangle(0, 114, 127, 117, WHITE); // Bottom uLCD.filled_rectangle(124, 14, 127, 117, WHITE); // Right } /** * 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 draw_option) { uLCD.locate(0, 0); uLCD.text_width(1.5); uLCD.text_height(1.5); uLCD.color(RED); uLCD.printf("Pos:%d %d", snake.head_x, snake.head_y); uLCD.locate(10, 0); uLCD.text_width(1.5); uLCD.text_height(1.5); //uLCD.printf("Loc: %d,%d ", snake.head_x, snake.head_y); uLCD.color(RED); uLCD.printf("Score:%d", snake.score); // Draw game border first if(draw_option == FULL_DRAW) { draw_border(); int u = 58; int v = 56; draw_snake_head(u, v); draw_snake_body(u-11, v); draw_snake_tail(u-22, v); int i = 1; for (; i < snake.length; i++) { int x = u - (11 * (snake.head_x - snake.locations[i].x)); int y = v - (11 * (snake.head_y - snake.locations[i].y)); if (i < snake.length - 1) { add_snake_body(snake.locations[i].x, snake.locations[i].y); draw_snake_body(x, y); } else { add_snake_tail(snake.locations[i].x, snake.locations[i].y); draw_snake_tail(x, y); } } } // 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 + snake.head_x; int y = j + snake.head_y; // Compute the previous map (px, py) of this tile int px = i + snake.head_px; int py = j + snake.head_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 (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 (draw_option || 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 (curr_item && curr_item->type == CLEAR) { // This is a special case for erasing things like doors. draw = curr_item->draw; // i.e. draw_nothing } } else if (draw_option) { // 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(); draw_lower_status(); } /** * 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() { Timer t; t.start(); Map* map = set_active_map(0); 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_snake_head(snake.locations[0].x, snake.locations[0].y); add_snake_body(snake.locations[1].x, snake.locations[1].y); add_snake_tail(snake.locations[2].x, snake.locations[2].y); pc.printf("Add extra chamber\r\n"); add_wall(30, 0, VERTICAL, 10); add_wall(30, 10, HORIZONTAL, 10); add_wall(39, 0, VERTICAL, 10); pc.printf("Added!\r\n"); //ADD POISON, ANY OTHER BUFFS/DEBUFFS HERE!!******************************************************* //Buffs: Decrement length, Slow (slows time) //Debuffs: Poison (prevents gaining points for certain time), Speedup thing (literally just 1.5x time or something), //Extra: Random, Strength (prevent death once or something), Pause button (just pause time), Moving object, multiple lives //Also: Button or sprite to invert controls srand(time(0)); // reset seedtime for(int i = map_width() + 4; i < map_area(); i += 29) { // debuff add_poison(i % map_width() + 4, i / map_width()); } for (int i=0; i<3;i++){ add_slowdown(rand()%28+1, rand()%49); //buff add_slowdown(rand()%9+40, rand()%49); add_decrease_length(rand()%28+1, rand()%49); //buff add_decrease_length(rand()%9+40, rand()%49); add_speedup(rand()%28+1, rand()%49); //debuff add_speedup(rand()%9+40, rand()%49); add_random(rand()%9+40, rand()%49, t.read_ms()); //extra randon add_random(rand()%28+1, rand()%49, t.read_ms()); add_moving(rand()%28+1, rand()%49); add_moving(rand()%9+40, rand()%49); add_inclength(rand()%28+1, rand()%49); add_inclength(rand()%9+40, rand()%49); } add_invinc(9, 5); // mulitple life add_slowdown(10, 5); add_moving(8, 6); add_inclength(5,7); for(int i = map_width() + 3; i < map_area(); i += 39) { //add_goodie(i % map_width(), i / map_width()); add_goodie(rand()%map_width(), rand()% map_height()); } // Add stairs to chamber (map 1) //add_stairs(15, 5, 1, 5, 5); // profile_hashtable(); print_map(); } /** * Program entry point! This is where it all begins. * This function or 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!"); snake_init(&snake); // 0. Initialize the maps -- implement this function: maps_init(); init_main_map(); playSound("/sd/snakeplay.wav"); // Initialize game state set_active_map(0); //snake.head_x = snake.head_y = 5 ; GameInputs inputs = read_inputs(); // Start page int difficulty = 0; draw_welcome(difficulty, true); while (1) { inputs = read_inputs(); if (!inputs.b2) { makenoise(); difficulty++; difficulty = difficulty % 3; draw_welcome(difficulty, false); snake.speedupTime += 16000; //16 sec wait_ms(200); } if (!inputs.b1) { // draw_game_over(1); makenoise(); break; } } // Initial drawing draw_game(FULL_DRAW); // Main game loop while(1) { // Timer to measure game update speed Timer t; t.start(); // 1. Read inputs -- implement this function: GameInputs inputs = read_inputs(); // 2. Determine action (move, act, menu, etc.) -- implement this function: int action = get_action(inputs); // 3. Update game -- implement this function: int result = update_game(action); // 3b. Check for game over based on result // and if so, handle game over -- implement this. // 4. Draw screen -- provided: draw_game(result); // Compute update time t.stop(); int dt = t.read_ms(); if (dt % 8 == 0 || dt % 8 == 1) { map_erase(5, 5); add_moving(10, 10); } else if (dt % 8 == 2 || dt % 8 == 3) { map_erase(10, 10); add_moving(5, 5); } else if (dt % 8 == 4 || dt % 8 == 5) { map_erase(5, 10); add_moving(10, 5); } else { map_erase(10, 5); add_moving(5, 10); } if (snake.invincTimer > 0) { snake.invincTimer -= 100; if (snake.invincTimer == 0) snake.invincible = 0; } // Display and wait if (snake.speedupTime > 0 && snake.slowdownTime > 0) { if (dt < 100) wait_ms(100 - dt); snake.speedupTime -= 100; snake.slowdownTime -= 100; } else if (snake.speedupTime > 0) { if (dt < 80) wait_ms(80 - dt); snake.speedupTime -= 100; } else if (snake.slowdownTime > 0) { if (dt < 125) wait_ms(1000 - dt); snake.slowdownTime -= 100; } else if (dt < 100) wait_ms(100 - dt); //Subtract from point lock time, if it's on if (snake.pointLockTime > 0) snake.pointLockTime -= 100; } } // Plays a wavfile void playSound(char* wav) { } // Play speaker void makenoise() { mySpeaker.PlayNote(368.0,0.16,0.1); mySpeaker.PlayNote(565.0,0.25,0.3); mySpeaker.PlayNote(400.0,0.18,0.2); }