Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed mbed-rtos 4DGL-uLCD-SE
Diff: main.cpp
- Revision:
- 12:f49bae848977
- Parent:
- 11:0309bef74ba8
--- a/main.cpp Wed Feb 15 14:04:02 2017 -0600
+++ b/main.cpp Sun May 02 19:01:00 2021 +0000
@@ -1,22 +1,1087 @@
+// Copyright (c) 2015 Devon Cooper and Sidak Dhillon
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
#include "mbed.h"
+#include "uLCD_4DGL.h"
+
+// LCD
+uLCD_4DGL uLCD(p28, p27, p30); // serial tx, serial rx, reset pin;
+
+// Input Devices
+DigitalIn fire_button(p22, PullUp);
+// Note: p15 is attached to VERT and p16 is attached to HORZ, however horizontal movement
+// triggers VERT and vertical movement triggers HORZ, so they are switched here.
+AnalogIn horizontal_in(p15);
+AnalogIn vertical_in(p16);
+
+#define SCREEN_MAX_X 128
+#define SCREEN_MAX_Y 128
+
+#define BLACK 0x000000
+#define SHIP_COLOR 0xFFFFFF
+#define SHIP_COLOR2 0xFFF00F
+#define SHIP_DAMAGED_COLOR 0xFF0000
+#define BARRICADE_COLOR 0xFF00FF
+#define BULLET_COLOR 0x00FF00
+#define ALIEN_COLOR 0x00FFFF
+
+#define SHIP_WIDTH 16
+#define SHIP_HEIGHT 8
+#define SHIP_GUN_WIDTH 3
+#define SHIP_GUN_HEIGHT 3
+
+#define BULLET_WIDTH 1
+#define BULLET_HEIGHT 3
+
+#define BARRICADES_START_Y (SCREEN_MAX_Y - 32)
+#define BARRICADES_END_Y (SCREEN_MAX_Y - 16)
+#define BARRICADE_SIZE 16
+
+#define ALIEN_HEIGHT 8
+#define ALIEN_WIDTH 11
+#define ALIEN_SCORE_AMOUNT 10
+
+/***** Sprite Data ***********************************************************/
+
+#define _ BLACK
+#define X BARRICADE_COLOR
+const int barricade_sprite[BARRICADE_SIZE * BARRICADE_SIZE] = {
+ _,_,_,X,X,X,X,X,X,X,X,X,X,_,_,_,
+ _,_,X,X,X,X,X,X,X,X,X,X,X,X,_,_,
+ _,X,X,X,X,X,X,X,X,X,X,X,X,X,X,_,
+ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
+ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
+ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
+ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
+ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
+ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
+ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
+ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
+ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
+ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
+ X,X,X,X,X,X,X,_,_,X,X,X,X,X,X,X,
+ X,X,X,X,X,_,_,_,_,_,_,X,X,X,X,X,
+ X,X,X,X,_,_,_,_,_,_,_,_,X,X,X,X,
+};
+#undef _
+#undef X
+
+#define _ 0x000000
+#define X 0x00FFFF
+#define Q 0xFF0F0F
+const int alien_sprite_1[ALIEN_HEIGHT * ALIEN_WIDTH] = {
+ _,_,X,_,_,_,_,_,X,_,_,
+ _,_,_,X,_,_,_,X,_,_,_,
+ _,_,X,X,X,X,X,X,X,_,_,
+ _,X,X,_,X,X,X,_,X,X,_,
+ X,X,X,X,X,X,X,X,X,X,X,
+ X,_,X,X,X,X,X,X,X,_,X,
+ X,_,X,_,_,_,_,_,X,_,X,
+ _,_,_,X,X,_,X,X,_,_,_,
+};
+
+const int alien_sprite_flip[ALIEN_HEIGHT * ALIEN_WIDTH] = {
+ _,_,_,Q,Q,_,Q,Q,_,_,_,
+ Q,_,Q,_,_,_,_,_,Q,_,Q,
+ Q,_,Q,Q,Q,Q,Q,Q,Q,_,Q,
+ Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,
+ _,Q,Q,_,Q,Q,Q,_,Q,Q,_,
+ _,_,Q,Q,Q,Q,Q,Q,Q,_,_,
+ _,_,_,Q,_,_,_,Q,_,_,_,
+ _,_,Q,_,_,_,_,_,Q,_,_,
+};
+
+const int alien_sprite_2[ALIEN_HEIGHT * ALIEN_WIDTH] = {
+ _,_,_,_,X,X,X,_,_,_,_,
+ _,X,X,X,X,X,X,X,X,X,_,
+ X,X,X,X,X,X,X,X,X,X,X,
+ X,X,X,_,_,X,_,_,X,X,X,
+ X,X,X,X,X,X,X,X,X,X,X,
+ _,_,_,X,X,_,X,X,_,_,_,
+ _,_,X,X,_,_,_,X,X,_,_,
+ X,X,_,_,_,X,_,_,_,X,X,
+};
+
+const int alien_sprite_3[ALIEN_HEIGHT * ALIEN_WIDTH] = {
+ _,_,_,_,_,X,X,_,_,_,_,
+ _,_,_,_,X,X,X,X,_,_,_,
+ _,_,_,X,X,X,X,X,X,_,_,
+ _,_,X,X,_,X,X,_,X,X,_,
+ _,_,X,X,X,X,X,X,X,X,_,
+ _,_,_,_,X,_,_,X,_,_,_,
+ _,_,_,X,_,X,X,_,X,_,_,
+ _,_,X,_,X,_,_,X,_,X,_,
+};
+
+const int blank[ALIEN_HEIGHT * ALIEN_WIDTH] = {
+ _,_,_,_,_,_,_,_,_,_,_,
+ _,_,_,_,_,_,_,_,_,_,_,
+ _,_,_,_,_,_,_,_,_,_,_,
+ _,_,_,_,_,_,_,_,_,_,_,
+ _,_,_,_,_,_,_,_,_,_,_,
+ _,_,_,_,_,_,_,_,_,_,_,
+ _,_,_,_,_,_,_,_,_,_,_,
+ _,_,_,_,_,_,_,_,_,_,_,
+};
+#undef _
+#undef X
+
+enum AlienSpecies {
+ ALIEN_SPECIES_1, ALIEN_SPECIES_2, ALIEN_SPECIES_3,
+};
+#define ALIEN_ROW_TO_SPECIES(_row) \
+ ((_row) == 0 ? ALIEN_SPECIES_3 : (_row) < 3 ? ALIEN_SPECIES_2 : ALIEN_SPECIES_1)
+
+// Input Device State
+volatile bool fire_button_pressed;
+volatile bool fire_button_pressed2;
+volatile float horizontal_movement;
+volatile float vertical_movement;
+volatile float horizontal_movement2;
+
+Ticker input_ticker;
+
+// Barricade state
+bool barricades[SCREEN_MAX_X][BARRICADE_SIZE];
+
+// Player state
+bool player_alive;
+bool player_alive2;
+int player_x;
+int player_x2;
+bool player_bullet_exists;
+int player_bullet_x;
+int player_bullet_y;
+bool player_bullet_exists2;
+int player_bullet_x2;
+int player_bullet_y2;
+int player_score;
+int player_lives;
+
+// Alien state
+bool aliens[5][5];
+int aliens_x; // left-most
+int aliens_y; // bottom-most
+struct { bool exists; int x; int y; } alien_bullets[3];
+
+// Random seed
+unsigned int random_seed;
+bool random_seeded;
+
+int pvp_state = -1;
+
+RawSerial dev(p13,p14);
+BusOut myled(LED1,LED2,LED3,LED4);
+DigitalOut them_led(p24);
+DigitalOut us_led(p20);
+
+void left_blue() {
+ horizontal_movement2 -= 1;
+}
+
+void right_blue() {
+ horizontal_movement2 += 1;
+}
+
+Ticker flipper;
+
+void dev_recv()
+{
+ char bnum=0;
+ while(dev.readable()) {
+ if (dev.getc()=='!') {
+ if (dev.getc()=='B') { //button data
+ bnum = dev.getc(); //button number
+ if ((bnum>='5')&&(bnum<='8')) { //is a number button 1..4
+ player_alive2 = true;
+ bool on = dev.getc()-'0';
+ myled[bnum-'5']= on; //turn on/off that num LED
+ if (on) {
+ if(bnum=='5')
+ fire_button_pressed2 = 1;
+ else if(bnum=='7')
+ flipper.attach(&left_blue, 0.02);
+ else if(bnum=='8')
+ flipper.attach(&right_blue, 0.02);
+ } else {
+ flipper.detach();
+ }
+ }
+ }
+ }
+ }
+}
+
+// Audio
+#include "SongPlayer.h"
#include "rtos.h"
-
-DigitalOut led1(LED1);
-DigitalOut led2(LED2);
-Thread thread;
-
-void led2_thread() {
+
+float game_over_note[18]= {884.0, 798.45, 722.25, 722.25, 798.45, 884.0, 884.0, 884.0, 798.45, 722.25, 798.45, 884.0, 798.45, 722.25, 687.35, 722.25, 722.25, 100.0 };
+float game_over_duration[18]= {0.6384, 0.3192, 0.9576, 0.6384, 0.3192, 0.6384, 0.3192, 0.3192, 0.3192, 0.3192, 0.3192, 0.3192, 0.3192, 0.6384, 0.3192, 0.6384, 0.6384, 0.0 };
+
+float shootNote[1] = {950.0};
+float durationNote[1] = {0.15};
+
+float hitNote[2] = {1400.0, 1400.0};
+float hit_durationNote[2] = {0.15, 0.15};
+
+SongPlayer mySpeaker(p21);
+
+volatile int isPlaying = -1;
+Mutex isPlaying_mutex;
+
+Thread audio_thread;
+
+void audio_thread_run() {
while (true) {
- led2 = !led2;
- Thread::wait(1000);
+ isPlaying_mutex.lock();
+ if (isPlaying == -1) {
+ isPlaying_mutex.unlock();
+ } else {
+
+ int songID = isPlaying;
+
+ isPlaying = 0;
+ isPlaying_mutex.unlock();
+
+
+ if (songID == 1 ) {
+ mySpeaker.PlaySong(shootNote, durationNote);
+ } else if (songID == 2 ) {
+ us_led = 1;
+ mySpeaker.PlaySong(hitNote, hit_durationNote);
+ Thread::wait(500);
+ us_led = 0;
+
+ } else if (songID == 3 ) {
+ us_led = 1;
+ them_led = 1;
+ mySpeaker.PlaySong(game_over_note, game_over_duration);
+ Thread::wait(500);
+ us_led = 0;
+ them_led = 0;
+
+ }else if (songID == 5 ) {
+ them_led = 1;
+ mySpeaker.PlaySong(hitNote, hit_durationNote);
+ Thread::wait(500);
+ them_led = 0;
+ }
+
+ //mySpeaker.PlaySong(shootNote, durationNote);
+
+ isPlaying_mutex.lock();
+ isPlaying = -1;
+ isPlaying_mutex.unlock();
+ }
+
+ Thread::wait(100);
+ }
+}
+
+
+void add_audio(int song_id) {
+ isPlaying_mutex.lock();
+ int isSongPlaying = isPlaying;
+
+ if (isSongPlaying == 0) {
+ isPlaying_mutex.unlock();
+ return;
+ }
+
+ isPlaying = song_id;
+ isPlaying_mutex.unlock();
+}
+
+
+/***** Rendering Routines ****************************************************/
+
+/* The player is drawn as two rectangles, one for the main body, and one for
+ * the cannon.
+ */
+void draw_player(int center_x, int color) {
+ uLCD.filled_rectangle(
+ center_x - (SHIP_WIDTH / 2),
+ SCREEN_MAX_Y - SHIP_HEIGHT,
+ center_x + (SHIP_WIDTH / 2),
+ SCREEN_MAX_Y - 1,
+ color);
+ uLCD.filled_rectangle(
+ center_x - (SHIP_GUN_WIDTH / 2),
+ SCREEN_MAX_Y - (SHIP_HEIGHT + SHIP_GUN_HEIGHT),
+ center_x + (SHIP_GUN_WIDTH / 2),
+ SCREEN_MAX_Y - SHIP_HEIGHT,
+ color);
+}
+
+/* Aliens are drawn using the sprite data and the uLCD's BLIT mode. */
+void draw_alien(AlienSpecies species, int left_x, int bottom_y) {
+ const int *sprite =
+ species == ALIEN_SPECIES_1 ? alien_sprite_1 :
+ species == ALIEN_SPECIES_2 ? alien_sprite_2 :
+ alien_sprite_3;
+ uLCD.BLIT(left_x, bottom_y, ALIEN_WIDTH, ALIEN_HEIGHT, (int*)sprite);
+}
+
+/* Erase an alien. This is a special routine because aliens have sprites
+ * and so take more time to draw than other objects.
+ */
+void erase_alien(int left_x, int bottom_y) {
+ // We erase an extra 2 around the sides and bottom to account for movement.
+ // This should be safe because the aliens never get closer than 8 pixels to
+ // the edge of the LCD.
+ uLCD.filled_rectangle(left_x - 2, bottom_y, left_x + 11 + 2, bottom_y + 8 + 2, BLACK);
+}
+
+/* Draws all 25 aliens. (5 rows, 5 columns) */
+void draw_aliens() {
+ uLCD.filled_rectangle(aliens_x, aliens_y, aliens_x + 16 * 5, aliens_y + 12 * 5, BLACK);
+ wait(0.05);
+
+ for (int x = 0; x < 5; x++) {
+ for (int y = 0; y < 5; y++) {
+ if (aliens[x][y]) {
+ draw_alien(ALIEN_ROW_TO_SPECIES(y), aliens_x + 16 * x, aliens_y + 12 * y);
+ }
+ }
+ }
+}
+
+/* Draw a bullet. A bullet is drawn as a 1x3 vertical line. */
+void draw_bullet(int center_x, int bottom_y, int color) {
+ uLCD.line(
+ center_x,
+ bottom_y,
+ center_x,
+ bottom_y + BULLET_HEIGHT,
+ color);
+}
+
+/***** Initialization ********************************************************/
+void initialize_screen() {
+ // Attempting to use a higher baud rate causes errors (the LCD freezes).
+ uLCD.baudrate(57600);
+ uLCD.background_color(BLACK);
+ uLCD.cls();
+}
+
+
+volatile float init_movement = 0;
+volatile float init_movement2 = 0;
+bool first_call = true;
+void __input_ticker__() {
+// while(true) {
+ if (first_call) {
+ init_movement = horizontal_in;
+ init_movement2 = vertical_in;
+ first_call = false;
+ }
+ fire_button_pressed |= !fire_button;
+ horizontal_movement += horizontal_in - init_movement;
+ vertical_movement += vertical_in - init_movement2;
+ if (fire_button_pressed)
+ player_alive = true;
+// Thread::wait(100);
+// }
+}
+
+Thread movement;
+
+// We use interrupts for measuring input so that input remains responsive even
+// when the graphics are lagging.
+void initialize_input() {
+ input_ticker.detach();
+ input_ticker.attach(__input_ticker__, 0.005);
+// movement.start(__input_ticker__);
+// START;
+}
+
+void initialize_player() {
+ player_x = 2 * SCREEN_MAX_X / 3;
+ player_bullet_exists = false;
+ draw_player(player_x, SHIP_COLOR);
+ player_score = 0;
+ player_lives = 2;
+
+ uLCD.locate(0, 0);
+ uLCD.printf("Score:%i", player_score);
+
+ uLCD.locate(10, 0);
+ uLCD.printf("Lives:%i", player_lives);
+}
+
+void initialize_player2() {
+ player_x2 = SCREEN_MAX_X / 3;
+ draw_player(player_x2, SHIP_COLOR2);
+}
+
+void initialize_player3() {
+ player_x = SCREEN_MAX_X/2;
+ const int *sprite = alien_sprite_1;
+ uLCD.BLIT(player_x, SCREEN_MAX_Y-ALIEN_HEIGHT, ALIEN_WIDTH, ALIEN_HEIGHT, (int*)sprite);
+}
+
+void initialize_player4() {
+ player_x2 = SCREEN_MAX_X/2;
+ const int *sprite = alien_sprite_flip;
+ uLCD.BLIT(player_x2, ALIEN_HEIGHT, ALIEN_WIDTH, ALIEN_HEIGHT, (int*)sprite);
+}
+
+void initialize_aliens() {
+ // Start the aliens in the upper-left corner of the screen
+ aliens_x = 8;
+ aliens_y = 8;
+
+ for (int x = 0; x < 5; x++) {
+ for (int y = 0; y < 5; y++) {
+ aliens[x][y] = true;
+ }
+ }
+
+ for (int i = 0; i < 3; i++) {
+ alien_bullets[i].exists = false;
+ }
+
+ draw_aliens();
+}
+
+void initialize_barricades() {
+ memset(barricades, 0, sizeof(bool) * SCREEN_MAX_X * BARRICADE_SIZE);
+
+ for (int i = 1; i < 5; i++) {
+ int start_x = (SCREEN_MAX_X * i / 5) - (BARRICADE_SIZE / 2);
+
+ uLCD.BLIT(start_x, BARRICADES_START_Y, BARRICADE_SIZE, BARRICADE_SIZE, (int*)barricade_sprite);
+
+ for (int x = 0; x < BARRICADE_SIZE; x++) {
+ for (int y = 0; y < BARRICADE_SIZE; y++) {
+ barricades[start_x + x][y] = !!barricade_sprite[y * BARRICADE_SIZE + x];
+ }
+ }
+ }
+}
+
+// Devices only need to be initialized once during the program, at start up
+void initialize_devices() {
+ initialize_screen();
+ initialize_input();
+}
+
+// Game state needs to be re-initialized whenever a new game is started after
+// a game over or win.
+void initialize_game_state() {
+ initialize_player();
+ initialize_player2();
+ initialize_barricades();
+ initialize_aliens();
+}
+
+void initialize_game_state2() {
+ initialize_player3();
+ initialize_player4();
+}
+
+
+/***** Logic *****************************************************************/
+
+/* Causes a chunk of the barricade around the given x and y coordinates to
+ * be destroyed.
+ */
+void barricade_impact(int x, int y) {
+ // This is supposed to create a diamond pattern around the point of
+ // impact, but for some reason it creates a square.
+ // Eg, it should be * but instead is *****
+ // *** *****
+ // ***** *****
+ // *** *****
+ // * *****
+
+ int x_dist = 2;
+ for (int delta_x = -x_dist; delta_x <= x_dist; delta_x++) {
+
+ int y_dist = abs(x) == 2 ? 0 : abs(x) == 1 ? 1 : 2;
+ for (int delta_y = -y_dist; delta_y <= y_dist; delta_y++) {
+ int abs_x = x + delta_x;
+ int abs_y = y + delta_y;
+
+ if (abs_x >= 0 && abs_x < SCREEN_MAX_X
+ && abs_y >= BARRICADES_START_Y && abs_y < BARRICADES_END_Y) {
+ barricades[abs_x][abs_y - BARRICADES_START_Y] = false;
+ uLCD.pixel(abs_x, abs_y, BLACK);
+ }
+ }
}
}
-
+
+/* Returns true if a bullet at the given coordinates hits an alien.
+ * As a side effect, that alien is destroyed and the player's score is increased.
+ */
+bool alien_impact(int x, int y) {
+ int horizontal_region = (x - aliens_x) / 16;
+ int vertical_region = (y - aliens_y) / 12;
+
+ int horizontal_offset = (x - aliens_x) % 16;
+ int vertical_offset = (y - aliens_y) % 12;
+
+ if (horizontal_region >= 0 && horizontal_region < 5
+ && vertical_region >= 0 && vertical_region < 5
+ && aliens[horizontal_region][vertical_region]
+ && horizontal_offset <= 11
+ && vertical_offset <= 8) {
+ aliens[horizontal_region][vertical_region] = false;
+ erase_alien(aliens_x + 16 * horizontal_region, aliens_y + 12 * vertical_region);
+ player_score += ALIEN_SCORE_AMOUNT;
+ add_audio(5);
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/* Returns true if a bullet at the given coordinates hits the player.
+ * As a side effect, the player loses a life.
+ */
+bool player_impact(int x, int y) {
+ int player_y = SCREEN_MAX_Y;
+ int player_y2 = pvp_state == -1 ? SCREEN_MAX_Y : 0;
+// if (y < SCREEN_MAX_Y && y >= SCREEN_MAX_Y - (SHIP_HEIGHT + SHIP_GUN_HEIGHT)) {
+ if (x >= player_x - SHIP_WIDTH / 2 && x <= player_x + SHIP_WIDTH / 2 && y >= player_y - SHIP_HEIGHT / 2 && y <= player_y + SHIP_HEIGHT / 2) {
+ if (pvp_state == -1)
+ draw_player(player_x, SHIP_DAMAGED_COLOR);
+ player_lives -= 1;
+ add_audio(2);
+
+ return true;
+ } else if (x >= player_x2 - SHIP_WIDTH / 2 && x <= player_x2 + SHIP_WIDTH / 2 && y >= player_y2 - SHIP_HEIGHT / 2 && y <= player_y2 + SHIP_HEIGHT / 2){
+ if (pvp_state == -1)
+ draw_player(player_x2, SHIP_DAMAGED_COLOR);
+ player_lives -= 1;
+ add_audio(2);
+ return true;
+ }
+// }
+ return false;
+}
+
+/* Checks if a barricade exists at the given x and y coordinates. */
+bool barricade_exist_at(int x, int y) {
+ return y >= BARRICADES_START_Y && y < BARRICADES_END_Y && barricades[x][y - BARRICADES_START_Y];
+}
+
+/***** Updates ***************************************************************/
+
+// Uses C++ functors for the updates. The original idea was that each update
+// would return a continuation to allow more responsive updates to the player
+// object, but I realized it would be easier to just update the state in-place.
+// These should be turned into simple functions, but it works as it is now.
+
+class PlayerUpdate {
+ public:
+ void operator()() {
+ // Sample input devices
+ float delta_x = horizontal_movement;
+ horizontal_movement = 0;
+
+ // Update player
+ int old_player_x = player_x;
+ player_x += int(delta_x * 2);
+
+ if (player_x < SHIP_WIDTH / 2) player_x = SHIP_WIDTH / 2;
+ if (player_x > SCREEN_MAX_X - (SHIP_WIDTH / 2) - 1) player_x = SCREEN_MAX_X - (SHIP_WIDTH / 2) - 1;
+
+ if (old_player_x != player_x) {
+ draw_player(old_player_x, BLACK);
+ draw_player(player_x, SHIP_COLOR);
+ draw_player(player_x2, SHIP_COLOR2);
+ }
+ }
+
+ void alien() {
+ // Sample input devices
+ float delta_x = horizontal_movement;
+ horizontal_movement = 0;
+
+ // Update player
+ int old_player_x = player_x;
+ player_x += int(delta_x * 2);
+
+ if (player_x < SHIP_WIDTH / 2) player_x = SHIP_WIDTH / 2;
+ if (player_x > SCREEN_MAX_X - (SHIP_WIDTH / 2) - 1) player_x = SCREEN_MAX_X - (SHIP_WIDTH / 2) - 1;
+
+ if (old_player_x != player_x) {
+ uLCD.BLIT(old_player_x, SCREEN_MAX_X - ALIEN_HEIGHT, ALIEN_WIDTH, ALIEN_HEIGHT, (int*)blank);
+ uLCD.BLIT(player_x, SCREEN_MAX_X - ALIEN_HEIGHT, ALIEN_WIDTH, ALIEN_HEIGHT, (int*)alien_sprite_1);
+ }
+ }
+};
+
+class PlayerUpdate2 {
+ public:
+ void operator()() {
+ // Sample input devices
+ float delta_x2 = horizontal_movement2;
+ horizontal_movement2 = 0;
+
+ // Update player
+ int old_player_x2 = player_x2;
+ player_x2 += int(delta_x2 * 2);
+
+ if (player_x2 < SHIP_WIDTH / 2) player_x2 = SHIP_WIDTH / 2;
+ if (player_x2 > SCREEN_MAX_X - (SHIP_WIDTH / 2) - 1) player_x2 = SCREEN_MAX_X - (SHIP_WIDTH / 2) - 1;
+
+ if (old_player_x2 != player_x2) {
+ draw_player(old_player_x2, BLACK);
+ draw_player(player_x, SHIP_COLOR);
+ draw_player(player_x2, SHIP_COLOR2);
+ }
+ }
+
+ void alien() {
+ // Sample input devices
+ float delta_x2 = horizontal_movement2;
+ horizontal_movement2 = 0;
+
+ // Update player
+ int old_player_x2 = player_x2;
+ player_x2 += int(delta_x2 * 2);
+
+ if (player_x2 < SHIP_WIDTH / 2) player_x2 = SHIP_WIDTH / 2;
+ if (player_x2 > SCREEN_MAX_X - (SHIP_WIDTH / 2) - 1) player_x2 = SCREEN_MAX_X - (SHIP_WIDTH / 2) - 1;
+
+ if (old_player_x2 != player_x2) {
+ uLCD.BLIT(old_player_x2, ALIEN_HEIGHT, ALIEN_WIDTH, ALIEN_HEIGHT, (int*)blank);
+ uLCD.BLIT(player_x2, ALIEN_HEIGHT, ALIEN_WIDTH, ALIEN_HEIGHT, (int*)alien_sprite_flip);
+ }
+ }
+};
+
+class AlienUpdate {
+ // This one is a bit complicated, essentially it is a hard-coded state
+ // machine that shifts one alien at a time so that the player can be
+ // updated in between rows of aliens shifting.
+
+ enum State {
+ SHIFTING_LEFT,
+ SHIFTING_RIGHT,
+ SHIFTING_DOWN_NEXT_IS_RIGHT,
+ SHIFTING_DOWN_NEXT_IS_LEFT,
+ } state;
+ int row_shifting, column_shifting;
+
+ public:
+ AlienUpdate() : state(SHIFTING_RIGHT), row_shifting(0), column_shifting(0) {}
+
+ void operator()() {
+ switch (this->state) {
+ case SHIFTING_LEFT:
+ if (aliens[column_shifting][row_shifting]) {
+ erase_alien(aliens_x + 16 * column_shifting, aliens_y + 12 * row_shifting);
+ draw_alien(ALIEN_ROW_TO_SPECIES(row_shifting), aliens_x + 16 * column_shifting - 2, aliens_y + 12 * row_shifting);
+ }
+ column_shifting -= 1;
+ if (column_shifting < 0) {
+ if (row_shifting >= 4) {
+ aliens_x -= 2;
+ if (aliens_x < 8) {
+ state = SHIFTING_DOWN_NEXT_IS_RIGHT;
+ row_shifting = 0;
+ column_shifting = 0;
+ } else {
+ row_shifting = 0;
+ column_shifting = 4;
+ }
+ } else {
+ row_shifting += 1;
+ column_shifting = 4;
+ }
+ }
+ break;
+
+ case SHIFTING_RIGHT:
+ if (aliens[column_shifting][row_shifting]) {
+ erase_alien(aliens_x + 16 * column_shifting, aliens_y + 12 * row_shifting);
+ draw_alien(ALIEN_ROW_TO_SPECIES(row_shifting), aliens_x + 16 * column_shifting + 2, aliens_y + 12 * row_shifting);
+ }
+ column_shifting += 1;
+ if (column_shifting > 4) {
+ if (row_shifting >= 4) {
+ aliens_x += 2;
+ if (aliens_x > SCREEN_MAX_X - (16 * 5) - 8) {
+ state = SHIFTING_DOWN_NEXT_IS_LEFT;
+ row_shifting = 0;
+ column_shifting = 0;
+ } else {
+ row_shifting = 0;
+ column_shifting = 0;
+ }
+ } else {
+ row_shifting += 1;
+ column_shifting = 0;
+ }
+ }
+ break;
+
+ case SHIFTING_DOWN_NEXT_IS_LEFT:
+ case SHIFTING_DOWN_NEXT_IS_RIGHT:
+ if (aliens[column_shifting][row_shifting]) {
+ erase_alien(aliens_x + 16 * column_shifting, aliens_y + 12 * row_shifting);
+ draw_alien(ALIEN_ROW_TO_SPECIES(row_shifting), aliens_x + 16 * column_shifting, aliens_y + 12 * row_shifting + 2);
+ }
+ column_shifting += 1;
+ if (column_shifting > 4) {
+ if (row_shifting >= 4) {
+ if (this->state == SHIFTING_DOWN_NEXT_IS_LEFT) {
+ state = SHIFTING_LEFT;
+ column_shifting = 4;
+ } else {
+ state = SHIFTING_RIGHT;
+ column_shifting = 0;
+ }
+ row_shifting = 0;
+ aliens_y += 2;
+ } else {
+ row_shifting += 1;
+ column_shifting = 0;
+ }
+ }
+ break;
+ }
+ }
+};
+
+class BulletUpdate {
+ public:
+ void operator()() {
+ // Sample input devices
+ bool fire_button_pressed_ = fire_button_pressed;
+ fire_button_pressed = false;
+
+ bool fire_button_pressed_2 = fire_button_pressed2;
+ fire_button_pressed2 = false;
+
+ // Update player bullet
+ if (player_bullet_exists) {
+ draw_bullet(player_bullet_x, player_bullet_y, BLACK);
+ player_bullet_y -= 2;
+ if (player_bullet_y < 0) {
+ player_bullet_exists = false;
+ }
+ } else if (fire_button_pressed_) {
+ add_audio(1);
+ player_bullet_exists = true;
+ player_bullet_x = player_x;
+ player_bullet_y = SCREEN_MAX_Y - (SHIP_HEIGHT + SHIP_GUN_HEIGHT) - BULLET_HEIGHT - 1;
+ }
+
+ if (player_bullet_exists) {
+ if (barricade_exist_at(player_bullet_x, player_bullet_y)) {
+ barricade_impact(player_bullet_x, player_bullet_y);
+ player_bullet_exists = false;
+ } else if (alien_impact(player_bullet_x, player_bullet_y)) {
+ player_bullet_exists = false;
+ } else if (pvp_state != -1 && player_impact(player_bullet_x, player_bullet_y + BULLET_HEIGHT)) {
+ pvp_state = 1;
+ }else {
+ draw_bullet(player_bullet_x, player_bullet_y, BULLET_COLOR);
+ }
+ }
+
+ // Update player2 bullet
+ if (player_bullet_exists2) {
+ draw_bullet(player_bullet_x2, player_bullet_y2, BLACK);
+ player_bullet_y2 -= pvp_state == -1 ? 2 : -2;
+ if (player_bullet_y2 < 0 || player_bullet_y2 >= SCREEN_MAX_Y) {
+ player_bullet_exists2 = false;
+ }
+ } else if (fire_button_pressed_2) {
+ add_audio(1);
+ player_bullet_exists2 = true;
+ player_bullet_x2 = player_x2;
+ if (pvp_state != -1)
+ player_bullet_y2 = 0 + (SHIP_HEIGHT + SHIP_GUN_HEIGHT) + BULLET_HEIGHT + 1;
+ else
+ player_bullet_y2 = SCREEN_MAX_Y - (SHIP_HEIGHT + SHIP_GUN_HEIGHT) - BULLET_HEIGHT - 1;
+ }
+
+ if (player_bullet_exists2) {
+ if (barricade_exist_at(player_bullet_x2, player_bullet_y2)) {
+ barricade_impact(player_bullet_x2, player_bullet_y2);
+ player_bullet_exists2 = false;
+ } else if (alien_impact(player_bullet_x2, player_bullet_y2)) {
+ player_bullet_exists2 = false;
+ } else if (pvp_state != -1 && player_impact(player_bullet_x2, player_bullet_y2 + BULLET_HEIGHT)) {
+ pvp_state = 2;
+ }else {
+ draw_bullet(player_bullet_x2, player_bullet_y2, BULLET_COLOR);
+ }
+ }
+
+ if (pvp_state != -1)
+ return;
+
+ // Update alien bullets
+ for (int i = 0; i < 3; i++) {
+ bool alien_fire_button_pressed = rand() % 64 == 0;
+
+ if (alien_bullets[i].exists) {
+ draw_bullet(alien_bullets[i].x, alien_bullets[i].y, BLACK);
+ alien_bullets[i].y += 2;
+ if (alien_bullets[i].y >= SCREEN_MAX_Y) {
+ alien_bullets[i].exists = false;
+ }
+ } else if (alien_fire_button_pressed) {
+ int column_firing = rand() % 5;
+ int row_firing = -1;
+
+ for (int y = 0; y < 5; y++) {
+ if (aliens[column_firing][y]) {
+ row_firing = y;
+ }
+ }
+
+ if (row_firing != -1) {
+ alien_bullets[i].exists = true;
+ alien_bullets[i].x = aliens_x + column_firing * 16 + (ALIEN_WIDTH / 2);
+ alien_bullets[i].y = aliens_y + row_firing * 12 + ALIEN_HEIGHT + 1;
+ }
+ }
+
+ if (alien_bullets[i].exists) {
+ if (barricade_exist_at(alien_bullets[i].x, alien_bullets[i].y + BULLET_HEIGHT)) {
+ barricade_impact(alien_bullets[i].x, alien_bullets[i].y + BULLET_HEIGHT);
+ alien_bullets[i].exists = false;
+ } else if (player_impact(alien_bullets[i].x, alien_bullets[i].y + BULLET_HEIGHT)) {
+ alien_bullets[i].exists = false;
+ } else {
+ draw_bullet(alien_bullets[i].x, alien_bullets[i].y, BULLET_COLOR);
+ }
+ }
+ }
+ }
+};
+
+PlayerUpdate player_updater;
+PlayerUpdate2 player_updater2;
+AlienUpdate alien_updater;
+BulletUpdate bullet_updater;
+void game_loop() {
+ player_updater();
+ player_updater2();
+
+ alien_updater();
+ bullet_updater();
+
+ // Seed random number generator if unseeded
+ // Basically, we count the number of cycles until a fire button is pressed.
+ // This should be sufficiently random, for game purposes at least.
+ if (!random_seeded) {
+ if (fire_button_pressed) {
+ srand(random_seed);
+ random_seeded = true;
+ } else {
+ random_seed += 1;
+ }
+ }
+
+ uLCD.text_height(1);
+ uLCD.text_width(1);
+ uLCD.locate(0, 0);
+ uLCD.printf("Score:%i", player_score);
+
+ uLCD.locate(10, 0);
+ uLCD.printf("Lives:%i", player_lives);
+
+ // Check for lose condition
+ if (player_lives < 0) {
+ uLCD.filled_rectangle(0, 0, SCREEN_MAX_X, BARRICADES_START_Y, BLACK);
+
+ uLCD.locate(0, 5);
+ uLCD.printf(" Game Over\n");
+ uLCD.printf(" Score: %5d\n", player_score);
+ uLCD.printf("\n");
+ uLCD.printf(" Press fire to\n");
+ uLCD.printf(" play again.\n");
+
+ wait(0.6);
+ add_audio(3);
+
+ wait(0.25);
+ while (!(fire_button_pressed || fire_button_pressed2));
+ wait(0.25);
+ uLCD.cls();
+ fire_button_pressed = false;
+ fire_button_pressed2 = false;
+ player_updater = PlayerUpdate();
+ player_updater2 = PlayerUpdate2();
+ alien_updater = AlienUpdate();
+ bullet_updater = BulletUpdate();
+ initialize_game_state();
+ return;
+ }
+
+ // Check for win condition
+ bool all_aliens_destroyed = true;
+ for (int x = 0; x < 5; x++) {
+ for (int y = 0; y < 5; y++) {
+ if (aliens[x][y]) {
+ all_aliens_destroyed = false;
+ break;
+ }
+ }
+ }
+
+ if (all_aliens_destroyed) {
+ uLCD.filled_rectangle(0, 0, SCREEN_MAX_X, BARRICADES_START_Y, BLACK);
+
+ uLCD.locate(0, 5);
+ uLCD.printf(" You Win!\n");
+ uLCD.printf(" Score: %5d\n", player_score);
+ uLCD.printf("\n");
+ uLCD.printf(" Press fire to\n");
+ uLCD.printf(" play again.\n");
+
+
+ wait(0.25);
+ while (!(fire_button_pressed || fire_button_pressed2) );
+ wait(0.25);
+ uLCD.cls();
+ fire_button_pressed = false;
+ fire_button_pressed2 = false;
+ player_updater = PlayerUpdate();
+ player_updater2 = PlayerUpdate2();
+ alien_updater = AlienUpdate();
+ bullet_updater = BulletUpdate();
+ initialize_game_state();
+ return;
+ }
+}
+
+void game_loop2() {
+ player_updater.alien();
+ player_updater2.alien();
+
+ bullet_updater();
+
+ // Check for lose condition
+ if (pvp_state != 0) {
+ uLCD.filled_rectangle(0, 0, SCREEN_MAX_X, BARRICADES_START_Y, BLACK);
+
+ uLCD.locate(0, 5);
+ uLCD.printf(" Game Over\n");
+ uLCD.printf(" Player %i won", pvp_state);
+ uLCD.printf("\n");
+ uLCD.printf(" Press fire to\n");
+ uLCD.printf(" play again.\n");
+ wait(0.6);
+ add_audio(3);
+
+ wait(0.25);
+ while (!(fire_button_pressed || fire_button_pressed2));
+ wait(0.25);
+ uLCD.cls();
+ fire_button_pressed = false;
+ fire_button_pressed2 = false;
+ player_bullet_exists = false;
+ player_bullet_exists2 = false;
+ initialize_game_state2();
+ pvp_state = 0;
+ }
+}
+
+
int main() {
- thread.start(led2_thread);
+ initialize_devices();
+ dev.attach(&dev_recv, Serial::RxIrq);
+
+ uLCD.locate(0, 5);
+ uLCD.printf(" Please Connect\n");
+ uLCD.printf(" Both Devices!\n");
+
+ uLCD.locate(0, 8);
+ uLCD.printf("Player 1:Not Ready");
+
+ uLCD.locate(0, 9);
+ uLCD.printf("Player 2:Not Ready");
+
+ while (!(player_alive && player_alive2)) {
+ if (player_alive) {
+ uLCD.locate(0, 8);
+ uLCD.printf("Player 1:Ready ");
+ }
+
+ if (player_alive2) {
+ uLCD.locate(0, 9);
+ uLCD.printf("Player 2:Ready ");
+ }
+ wait(0.01);
+ }
+ fire_button_pressed = false;
+ fire_button_pressed2 = false;
+
+ uLCD.cls();
+
+ uLCD.locate(0, 5);
+ uLCD.printf(" Select Mode!\n");
+
+ uLCD.locate(0, 8);
+ uLCD.printf("Normal -Seleted");
+
+ uLCD.locate(0, 9);
+ uLCD.printf("PVP ");
+
+ while (!(fire_button_pressed)) {
+ if (vertical_movement > 0.1) {
+ uLCD.locate(0, 8);
+ uLCD.printf("Normal ");
+ uLCD.locate(0, 9);
+ uLCD.printf("PVP -Seleted");
+ pvp_state = 0;
+ }else if (vertical_movement < 0.1) {
+ uLCD.locate(0, 8);
+ uLCD.printf("Normal -Seleted");
+ uLCD.locate(0, 9);
+ uLCD.printf("PVP ");
+ pvp_state = -1;
+ }
+ vertical_movement = 0;
+ wait(0.01);
+ }
+ fire_button_pressed = false;
+ fire_button_pressed2 = false;
+
+ uLCD.cls();
+
+ if (pvp_state != -1)
+ initialize_game_state2();
+ else
+ initialize_game_state();
+
+ audio_thread.start(audio_thread_run);
while (true) {
- led1 = !led1;
- Thread::wait(500);
+ if (pvp_state != -1)
+ game_loop2();
+ else
+ game_loop();
+ wait(0.005);
}
-}
+}
\ No newline at end of file