A simple game

Dependencies:   4DGL-uLCD-SE mbed-rtos mbed

Fork of rtos_signals by mbed official

Revision:
4:2bb5deb83b81
Parent:
1:6a8fcc666593
--- a/main.cpp	Tue Jun 04 15:54:12 2013 +0100
+++ b/main.cpp	Fri Nov 04 13:45:06 2016 +0000
@@ -1,21 +1,422 @@
 #include "mbed.h"
 #include "rtos.h"
+#include <mpr121.h>
+#include "uLCD_4DGL.h"
+#include "Speaker.h"
 
-DigitalOut led(LED1);
+uLCD_4DGL uLCD(p9,p10,p11);
+int curr_x_u, curr_y_u, old_x_u, old_y_u; //user's current and old x,y coordinates
+int curr_x_e, curr_y_e, old_x_e, old_y_e; //enemy's current and old x,y coordinates
+float dx_e, dy_e, fx_e, fy_e; //x,y velocity of enemy and floating point representation of x,y coordinates
+int curr_x_b, curr_y_b, old_x_b, old_y_b; //payload's current and old x,y coordinates
+int curr_x_p, curr_y_p, old_x_p, old_y_p; //portal's current and old x,y coordinates
+Mutex lcd_mutex;
+
+Thread box; //handles user-payload collision logic
+Thread portal; //handles portal logic
+
+Speaker amp(p21);
+
+Serial pc(USBTX, USBRX); //debugging
+
+InterruptIn interrupt(p26);
+I2C i2c(p28, p27);
+Mpr121 mpr121(&i2c, Mpr121::ADD_VSS);
+
+int score = 0;
+int oldscore = 0;
+
+volatile int key = -1; //key pressed from keypad; set via interrupts -> volatile so compiler doesn't optimize out value reads
+
+enum State { START, LIVE, PAUSE, DEAD }; //game status
+volatile State s = START;
+
+//interrupt method that runs when keypad has a new value
+void fallInterrupt() {
+    int key_code=0;
+    int i=0;
+    int value=mpr121.read(0x00);
+    value +=mpr121.read(0x01)<<8;
+    i=0;
+    for (i=0; i<12; i++) {
+        if (((value>>i)&0x01)==1) key_code=i+1;
+    }
+    key = key_code;
+}
+
+//handles portal logic
+void portal_thread() {
+    while(s == LIVE || s == PAUSE) {
+        while(s == PAUSE);
+        //if payload and portal collide, score a point and randomize portal location
+        if (((curr_x_p-3 <= curr_x_b+3 && curr_x_p >= curr_x_b) || (curr_x_p+3 >= curr_x_b-3 && curr_x_p <= curr_x_b)) && ((curr_y_p+3 >= curr_y_b-3 && curr_y_p <= curr_y_b) || (curr_y_p-3 <= curr_y_b+3 && curr_y_p >= curr_y_b))) {
+            score++;
+            curr_x_p = (rand() % 106) + 10;
+            curr_y_p = (rand() % 106) + 10;
+        } else if (((curr_y_p-3 <= curr_y_b+3 && curr_y_p >= curr_y_b) || (curr_y_p+3 >= curr_y_b-3 && curr_y_p <= curr_y_b)) && ((curr_x_p+3 >= curr_x_b-3 && curr_x_p <= curr_x_b) || (curr_x_p-3 <= curr_x_b+3 && curr_x_p >= curr_x_b))) {
+            score++;
+            curr_x_p = (rand() % 106) + 10;
+            curr_y_p = (rand() % 106) + 10;
+        }
+        lcd_mutex.lock();
+        //redraw portal and play note if score has changed
+        uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
+        if (score != oldscore) {
+            amp.PlayNote(1000.0,0.025,0.5);
+            oldscore = score;
+            uLCD.rectangle(old_x_p-5, old_y_p-5, old_x_p+5, old_y_p+5, BLACK);
+            old_x_p = curr_x_p;
+            old_y_p = curr_y_p;
+        }
+        //print score
+        uLCD.locate(1,15);
+        uLCD.color(0xFF00FF);
+        uLCD.printf("%d", score);
+        lcd_mutex.unlock();
+    }
+}
 
-void led_thread(void const *argument) {
-    while (true) {
-        // Signal flags that are reported as event are automatically cleared.
-        Thread::signal_wait(0x1);
-        led = !led;
+//handles user-payload collisions
+void box_thread() {
+    while(s == LIVE) {
+        old_x_u = curr_x_u;
+        old_y_u = curr_y_u;
+        old_x_b = curr_x_b;
+        old_y_b = curr_y_b;
+        //if 3 pressed, then go into pause state
+        if (key==3+1) {
+            s = PAUSE;
+            lcd_mutex.lock();
+            uLCD.locate(1, 2);
+            uLCD.color(0xFFFF00);
+            uLCD.printf("PAUSE");
+            //wait until 3 is unpressed
+            while(key==3+1);
+            //wait until 3 is pressed
+            while(key!=3+1);
+            //wait until 3 is unpressed
+            while(key==3+1);
+            uLCD.locate(1, 2);
+            uLCD.color(BLACK);
+            uLCD.printf("PAUSE");
+            uLCD.color(WHITE);
+            //countdown from 3
+            for (int i=3; i>=1; i--) {
+                uLCD.locate(1, 2);
+                uLCD.printf("%2D", i);
+                amp.PlayNote(800.0,0.025,0.5);
+                wait(1);
+            }
+            uLCD.locate(1,2);
+            uLCD.color(BLACK);
+            uLCD.printf("  ");
+            lcd_mutex.unlock();
+            //play on
+            s = LIVE;
+        }
+        //really complicated collision logic
+        //basically checks each way the boxes could be colliding based on which key is pressed (which direction user is moving)
+        if (key==0+1) {
+
+            if ((curr_x_b+6 > curr_x_u-6 && curr_x_u > curr_x_b) && ((curr_y_u+6 > curr_y_b-6 && curr_y_u <= curr_y_b) || (curr_y_u-6 < curr_y_b+6 && curr_y_u >= curr_y_b))) {
+                curr_x_b-=1;
+                curr_x_u = curr_x_b+12;
+                if (curr_x_b < 7) {
+                    curr_x_b = 25;
+                    curr_y_b = 63;
+                }
+            } else {
+                curr_x_u-=1;
+            }
+            if (curr_x_u < 7) curr_x_u = 7;
+        } else if (key==8+1) {
+
+            if ((curr_x_b-6 < curr_x_u+6 && curr_x_u < curr_x_b) && ((curr_y_u+6 > curr_y_b-6 && curr_y_u <= curr_y_b) || (curr_y_u-6 < curr_y_b+6 && curr_y_u >= curr_y_b))) {
+                curr_x_b+=1;
+                curr_x_u = curr_x_b-12;
+                if (curr_x_b > 120) {
+                    curr_x_b = 25;
+                    curr_y_b = 63;
+                }
+            } else {
+                curr_x_u+=1;
+            }
+            if (curr_x_u > 120) curr_x_u = 120;
+        } else if (key==4+1) {
+
+            if ((curr_y_b-6 < curr_y_u+6 && curr_y_u < curr_y_b) && ((curr_x_u+6 > curr_x_b-6 && curr_x_u <= curr_x_b) || (curr_x_u-6 < curr_x_b+6 && curr_x_u >= curr_x_b))) {
+                curr_y_b+=1;
+                curr_y_u = curr_y_b-12;
+                if (curr_y_b > 120) {
+                    curr_x_b = 25;
+                    curr_y_b = 63;
+                }
+            } else {
+                curr_y_u+=1;
+            }
+            if (curr_y_u > 120) curr_y_u = 120;
+        } else if (key==5+1) {
+
+            if ((curr_y_b+6 > curr_y_u-6 && curr_y_u > curr_y_b) && ((curr_x_u+6 > curr_x_b-6 && curr_x_u <= curr_x_b) || (curr_x_u-6 < curr_x_b+6 && curr_x_u >= curr_x_b))) {
+                curr_y_b-=1;
+                curr_y_u = curr_y_b+12;
+                if (curr_y_b < 7) {
+                    curr_x_b = 25;
+                    curr_y_b = 63;
+                }
+            } else {
+                curr_y_u-=1;
+            }
+            if (curr_y_u < 7) curr_y_u = 7;
+        }
+        //redraw user and payload
+        lcd_mutex.lock();
+        uLCD.rectangle(20, 58, 30, 68, WHITE);
+        uLCD.filled_rectangle(old_x_u-5, old_y_u-5, old_x_u+5, old_y_u+5, BLACK);
+        uLCD.filled_rectangle(curr_x_u-5, curr_y_u-5, curr_x_u+5, curr_y_u+5, GREEN);
+        uLCD.filled_rectangle(old_x_b-5, old_y_b-5, old_x_b+5, old_y_b+5, BLACK);
+        uLCD.filled_rectangle(curr_x_b-5, curr_y_b-5, curr_x_b+5, curr_y_b+5, DGREY);
+        lcd_mutex.unlock();
     }
 }
 
-int main (void) {
-    Thread thread(led_thread);
-    
-    while (true) {
-        Thread::wait(1000);
-        thread.signal_set(0x1);
+//main thread handles setup of game, angry ball collision logic, gameover, and game resets
+int main() {
+    //setup keypad interrupts
+    interrupt.fall(&fallInterrupt);
+    interrupt.mode(PullUp);
+    uLCD.baudrate(3000000);
+    //load in Angry Balls image from SD
+    uLCD.media_init();
+    uLCD.set_sector_address(0x003B, 0xD400);
+    uLCD.display_image(0,0);
+    //wait 3 seconds to allow keypad time to set up while player can look at the pretty image
+    wait(3);
+    uLCD.textbackground_color(WHITE);
+    uLCD.text_width(1);
+    uLCD.text_height(1);
+    uLCD.color(RED);
+    uLCD.locate(0, 0);
+    uLCD.printf("Angry Balls!");
+    uLCD.locate(0, 1);
+    uLCD.color(BLACK);
+    uLCD.printf("Press 3 to play");
+    //start tone
+    amp.PlayNote(700.0,0.2,0.5);
+    amp.PlayNote(1000.0,0.2,0.0);
+    amp.PlayNote(600.0,0.1,0.5);
+    amp.PlayNote(1000.0,0.1,0.0);
+    amp.PlayNote(700.0,0.5,0.5);
+    //wait until 3 is pressed
+    while(key != 3+1);
+    //wait until 3 is unpressed
+    while(key == 3+1);
+    //clear screen
+    uLCD.cls();
+    uLCD.textbackground_color(BLACK);
+    uLCD.background_color(BLACK);
+    //setup initial state for every object
+    curr_x_u = 9;
+    curr_y_u = 63;
+    curr_x_b = 25;
+    curr_y_b = 63;
+    curr_x_e = 75;
+    curr_y_e = 75;
+    dx_e = -0.75;
+    dy_e = 0.75;
+    curr_x_p = 110;
+    curr_y_p = 15;
+    old_x_p = curr_x_p;
+    old_y_p = curr_y_p;
+    //draw everything
+    uLCD.filled_rectangle(curr_x_u-5, curr_y_u-5, curr_x_u+5, curr_y_u+5, GREEN);
+    uLCD.filled_rectangle(curr_x_b-5, curr_y_b-5, curr_x_b+5, curr_y_b+5, DGREY);
+    uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
+    uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
+    uLCD.color(WHITE);
+    uLCD.locate(1,2);
+    uLCD.printf("Place ball!");
+    //logic for placing ball before game starts based on key presses; prevents ball from going through walls & middle boundary
+    while(key != 3+1) {
+        old_x_e = curr_x_e;
+        old_y_e = curr_y_e;
+        if (key==0+1) {
+            curr_x_e-=2;
+            if (curr_x_e < 71) curr_x_e = 71;
+        }
+        if (key==8+1) {
+            curr_x_e+=2;
+            if (curr_x_e > 120) curr_x_e = 120;
+        }
+        if (key==4+1) {
+            curr_y_e+=2;
+            if (curr_y_e > 120) curr_y_e = 120;
+        }
+        if (key==5+1) {
+            curr_y_e-=2;
+            if (curr_y_e < 7) curr_y_e = 7;
+        }
+        //draw ball
+        uLCD.locate(1,2);
+        uLCD.printf("Place ball!");
+        uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
+        uLCD.filled_circle(old_x_e, old_y_e, 6, BLACK);
+        uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
+    }
+    //wait until 3 is unpressed
+    while(key==3+1);
+    uLCD.locate(1,2);
+    uLCD.color(BLACK);
+    uLCD.printf("Place ball!");
+    fx_e = curr_x_e;
+    fy_e = curr_y_e;
+    uLCD.color(WHITE);
+    //countdown til start
+    for (int i=3; i>=1; i--) {
+        uLCD.locate(1, 2);
+        uLCD.printf("%2D", i);
+        amp.PlayNote(800.0,0.025,0.5);
+        wait(1);
     }
-}
+    uLCD.locate(1,2);
+    uLCD.color(BLACK);
+    uLCD.printf("  ");
+    //game is live, start other two threads
+    s = LIVE;
+    box.start(box_thread);
+    portal.start(portal_thread);
+    //collision logic for angry ball
+    //check if ball has collided with a wall, the user, or the payload (gameover)
+    while(s == LIVE || s == PAUSE) {
+        while (s==PAUSE);
+        old_x_e = curr_x_e;
+        old_y_e = curr_y_e;
+        if (curr_x_e < 7) {
+            curr_x_e = 7;
+            dx_e = -dx_e;
+        } else if (curr_x_e > 120) {
+            curr_x_e = 120;
+            dx_e = -dx_e;
+        } else if (curr_y_e > 120) {
+            curr_y_e = 120;
+            dy_e = -dy_e;
+        } else if (curr_y_e < 7) {
+            curr_y_e = 7;
+            dy_e = -dy_e;
+        } else if (((dx_e < 0 && curr_x_e-6 < curr_x_u+6 && curr_x_e > curr_x_u) || (dx_e > 0 && curr_x_e+6 > curr_x_u-6 && curr_x_e < curr_x_u)) && ((curr_y_e+6 > curr_y_u-6 && curr_y_e < curr_y_u) || (curr_y_e-6 < curr_y_u+6 && curr_y_e > curr_y_u))) {
+            dx_e = -dx_e;
+        } else if (((dy_e < 0 && curr_y_e-6 < curr_y_u+6 && curr_y_e > curr_y_u) || (dy_e > 0 && curr_y_e+6 > curr_y_u-6 && curr_y_e < curr_y_u)) && ((curr_x_e+6 > curr_x_u-6 && curr_x_e < curr_x_u) || (curr_x_e-6 < curr_x_u+6 && curr_x_e > curr_x_u))) {
+            dy_e = -dy_e;
+        }
+        if (((dx_e < 0 && curr_x_e-6 < curr_x_b+6 && curr_x_e > curr_x_b) || (dx_e > 0 && curr_x_e+6 > curr_x_b-6 && curr_x_e < curr_x_b)) && ((curr_y_e+6 > curr_y_b-6 && curr_y_e < curr_y_b) || (curr_y_e-6 < curr_y_b+6 && curr_y_e > curr_y_b))) {
+            s = DEAD;
+        } else if (((dy_e < 0 && curr_y_e-6 < curr_y_b+6 && curr_y_e > curr_y_b) || (dy_e > 0 && curr_y_e+6 > curr_y_b-6 && curr_y_e < curr_y_b)) && ((curr_x_e+6 > curr_x_b-6 && curr_x_e < curr_x_b) || (curr_x_e-6 < curr_x_b+6 && curr_x_e > curr_x_b))) {
+            s = DEAD;
+        }
+        //update floating point coordinates with velocity values
+        fx_e += dx_e;
+        fy_e += dy_e;
+        //cast floating coordinates to integer coordinates
+        curr_x_e = (int) fx_e;
+        curr_y_e = (int) fy_e;
+        //redraw ball
+        lcd_mutex.lock();
+        uLCD.filled_circle(old_x_e, old_y_e, 6, BLACK);
+        uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
+        lcd_mutex.unlock();
+        //if ball-payload collision has occurred, game is now dead
+        if (s == DEAD) {
+            //print gameover, score, instructions to start over, and play gameover sound
+            lcd_mutex.lock();
+            uLCD.cls();
+            uLCD.locate(1,2);
+            uLCD.color(RED);
+            uLCD.printf("GAMEOVER");
+            uLCD.locate(1,4);
+            uLCD.color(0x4B0082);
+            uLCD.printf("Score:%d", score);
+            uLCD.locate(1,8);
+            uLCD.color(WHITE);
+            uLCD.printf("Press 3 to retry");
+            amp.PlayNote(300.0,0.5,0.5);
+            amp.PlayNote(300.0,0.05,0.0);
+            amp.PlayNote(280.0,0.5,0.5);
+            amp.PlayNote(300.0,0.05,0.0);
+            amp.PlayNote(260.0,1.0,0.5);
+            //wait until 3 is pressed
+            while(key != 3+1);
+            //wait until 3 is unpressed
+            while(key == 3+1);
+            //reinitialize game state for new game; same code as above
+            uLCD.cls();
+            score = 0;
+            oldscore = 0;
+            curr_x_u = 9;
+            curr_y_u = 63;
+            curr_x_b = 25;
+            curr_y_b = 63;
+            curr_x_e = 75;
+            curr_y_e = 75;
+            dx_e = -0.75;
+            dy_e = 0.75;
+            curr_x_p = 110;
+            curr_y_p = 15;
+            old_x_p = curr_x_p;
+            old_y_p = curr_y_p;
+            uLCD.filled_rectangle(curr_x_u-5, curr_y_u-5, curr_x_u+5, curr_y_u+5, GREEN);
+            uLCD.filled_rectangle(curr_x_b-5, curr_y_b-5, curr_x_b+5, curr_y_b+5, DGREY);
+            uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
+            uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
+            uLCD.color(WHITE);
+            uLCD.locate(1,2);
+            uLCD.printf("Place ball!");
+            while(key != 3+1) {
+                old_x_e = curr_x_e;
+                old_y_e = curr_y_e;
+                if (key==0+1) {
+                    curr_x_e-=2;
+                    if (curr_x_e < 71) curr_x_e = 71;
+                }
+                if (key==8+1) {
+                    curr_x_e+=2;
+                    if (curr_x_e > 120) curr_x_e = 120;
+                }
+                if (key==4+1) {
+                    curr_y_e+=2;
+                    if (curr_y_e > 120) curr_y_e = 120;
+                }
+                if (key==5+1) {
+                    curr_y_e-=2;
+                    if (curr_y_e < 7) curr_y_e = 7;
+                }
+                uLCD.locate(1,2);
+                uLCD.printf("Place ball!");
+                uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
+                uLCD.filled_circle(old_x_e, old_y_e, 6, BLACK);
+                uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
+            }
+            while(key==3+1);
+            uLCD.locate(1,2);
+            uLCD.color(BLACK);
+            uLCD.printf("Place ball!");
+            fx_e = curr_x_e;
+            fy_e = curr_y_e;
+            uLCD.color(WHITE);
+            for (int i=3; i>=1; i--) {
+                uLCD.locate(1, 2);
+                uLCD.printf("%2D", i);
+                amp.PlayNote(800.0,0.025,0.5);
+                wait(1);
+            }
+            uLCD.locate(1,2);
+            uLCD.color(BLACK);
+            uLCD.printf("  ");
+            lcd_mutex.unlock();
+            //game is live again
+            //restart the other threads
+            s = LIVE;
+            box.start(box_thread);
+            portal.start(portal_thread);
+        }
+    }
+}
\ No newline at end of file