SimplyMinimal 3 лет назад
Родитель
Сommit
8d5e78af5f
1 измененных файлов с 167 добавлено и 48 удалено
  1. 167 48
      app.c

+ 167 - 48
app.c

@@ -21,9 +21,10 @@
 #define GAME_START_LIVES 3
 #define TTLBUL 30 /* Bullet time to live, in ticks. */
 #define MAXBUL 5 /* Max bullets on the screen. */
-#define MAXAST 32 /* Max asteroids on the screen. */
-#define MAXPOWERUPS 3 /* Max powerups allowed active */
-#define POWERUPSTTL 60 * 1000 /* Max powerup time to live, in ticks. */
+//@todo MAX Asteroids
+#define MAXAST 0 //32 /* Max asteroids on the screen. */
+#define MAXPOWERUPS 3 /* Max powerups allowed on screen */
+#define POWERUPSTTL 10 * 1000 /* Max powerup time to live, in ticks. */
 #define SHIP_HIT_ANIMATION_LEN 15
 #define SAVING_DIRECTORY "/ext/apps/Games"
 #define SAVING_FILENAME SAVING_DIRECTORY "/game_asteroids.save"
@@ -33,7 +34,7 @@
 
 /* ============================ Data structures ============================= */
 typedef enum PowerUpType {
-    PowerUpTypeNone, // No powerup
+    // PowerUpTypeNone, // No powerup
     PowerUpTypeShield, // Shield
     PowerUpTypeLife, // Extra life
     PowerUpTypeFirePower, // Burst Fire power
@@ -44,14 +45,17 @@ typedef enum PowerUpType {
     Number_of_PowerUps // Used to count the number of powerups
 } PowerUpType;
 
+//    struct PowerUp
 typedef struct PowerUp {
-    float x, y, vx, vy, /* Fields like in ship. */
-        // rot, /* Fields like ship. */
-        // rot_speed, /* Angular velocity (rot speed and sense). */
-        size; /* Power Up size. */
-    uint32_t ttl; /* Time to live, in ticks. */
-    enum PowerUpType powerUpType; /* Powerup type */
+    float x, y, vx, vy; /* Fields like in ship. */
+    // rot, /* Fields like ship. */
+    // rot_speed, /* Angular velocity (rot speed and sense). */
+    int size; /* Power Up size */
 
+    uint32_t ttl; /* Time to live, in ticks. */
+    uint32_t display_ttl; /* How long to display the powerup before it disappears */
+    enum PowerUpType powerUpType; /* PowerUp type */
+    bool isPowerUpActive; /* Is the powerup active? */
 } PowerUp;
 
 typedef struct Ship {
@@ -60,8 +64,6 @@ typedef struct Ship {
         vx, /* x velocity. */
         vy, /* y velocity. */
         rot; /* Current rotation. 2*PI full ortation. */
-    enum PowerUpType powerUp; /* Current powerup */
-    bool isPowerUpActive; /* Is the powerup active? */
 } Ship;
 
 typedef struct Bullet {
@@ -164,6 +166,8 @@ const NotificationSequence sequence_bullet_fired = {
 /* ============================== Prototyeps ================================ */
 
 // Only functions called before their definition are here.
+bool isPowerUpActive(AsteroidsApp* app, enum PowerUpType powerUpType);
+bool isPowerUpAlreadyExists(AsteroidsApp* app, enum PowerUpType powerUpType);
 bool load_game(AsteroidsApp* app);
 void save_game(AsteroidsApp* app);
 void restart_game_after_gameover(AsteroidsApp* app);
@@ -277,47 +281,87 @@ void draw_left_lives(Canvas* const canvas, AsteroidsApp* app) {
     }
 }
 
-void draw_powerUps(Canvas* const canvas, PowerUp* p) {
-    //@todo draw_powerUp
-    int BOX_SIZE = p->size;
+void draw_powerUps(Canvas* const canvas, PowerUp* const p) {
+    /*
+    
+    * * * * * * * * * *
+    *                 *
+    *	              *
+    *                 *
+    *       F         *
+    *                 *
+    *                 *
+    *                 *
+    *                 *
+    * * * * * * * * * *
+
+    BOX_SIZE = 10
+    Box_Width = BOX_SIZE
+    BOX_HEIGHT = BOX_SIZE
+    BOX_X_POS = x - BOX_WIDTH/2
+    BOX_Y_POS = y - BOX_HEIGHT/2
+    POS_F_X = WIDTH/2
+    POS_F_Y = HEIGHT/2
+
+    */
+
+    //@todo render_callback
+
+    // Just return if power up has already been picked up
+    FURI_LOG_I(TAG, "[draw_powerUps] Display TTL: %lu", p->display_ttl);
+    if(p->display_ttl == 0) return;
+
+    canvas_set_color(canvas, ColorXOR);
+
+    int BOX_SIZE = p->size * 2;
     switch(p->powerUpType) {
-    case PowerUpTypeNone:
-        break;
     case PowerUpTypeFirePower:
         // Draw box with letter F inside
-        canvas_draw_frame(canvas, p->x, p->y, BOX_SIZE, BOX_SIZE);
-        canvas_draw_str(canvas, p->x + 8, p->y + 6, "F");
+        FURI_LOG_D(
+            TAG,
+            "[draw_powerUps] p->size: %i x:%i y:%i BOX_SIZE: %i",
+            p->size,
+            abs((int)p->x - (int)(p->size / 2)),
+            abs((int)p->y - (int)(p->size / 2)),
+            BOX_SIZE);
+        canvas_draw_frame(
+            canvas,
+            abs((int)p->x - (int)(p->size / 2)),
+            abs((int)p->y - (int)(p->size / 2)),
+            BOX_SIZE,
+            BOX_SIZE);
+        canvas_draw_str(canvas, p->x, p->y, "F");
         break;
     case PowerUpTypeShield:
         // Draw box with letter S inside
-        canvas_draw_frame(canvas, p->x, p->y, BOX_SIZE, BOX_SIZE);
-        canvas_draw_str(canvas, p->x + 8, p->y + 6, "S");
+        canvas_draw_frame(canvas, p->x - p->size / 2, p->y - p->size / 2, BOX_SIZE, BOX_SIZE);
+        canvas_draw_str(canvas, p->x, p->y - BOX_SIZE / 2, "S");
         break;
     case PowerUpTypeLife:
         // Draw box with letter L inside
-        canvas_draw_frame(canvas, p->x, p->y, BOX_SIZE, BOX_SIZE);
-        canvas_draw_str(canvas, p->x + 8, p->y + 6, "L");
+        canvas_draw_frame(canvas, p->x - p->size / 2, p->y - p->size / 2, BOX_SIZE, BOX_SIZE);
+        canvas_draw_str(canvas, p->x, p->y - BOX_SIZE / 2, "L");
         break;
     case PowerUpTypeNuke:
         // Draw box with letter N inside
-        canvas_draw_frame(canvas, p->x, p->y, BOX_SIZE, BOX_SIZE);
-        canvas_draw_str(canvas, p->x + 8, p->y + 6, "N");
+        canvas_draw_frame(canvas, p->x - p->size / 2, p->y - p->size / 2, BOX_SIZE, BOX_SIZE);
+        canvas_draw_str(canvas, p->x, p->y - BOX_SIZE / 2, "N");
         break;
     case PowerUpTypeRadialFire:
         // Draw box with letter R inside
-        canvas_draw_frame(canvas, p->x, p->y, BOX_SIZE, BOX_SIZE);
-        canvas_draw_str(canvas, p->x + 8, p->y + 6, "R");
+        canvas_draw_frame(canvas, p->x - p->size / 2, p->y - p->size / 2, BOX_SIZE, BOX_SIZE);
+        canvas_draw_str(canvas, p->x, p->y - BOX_SIZE / 2, "R");
         break;
     case PowerUpTypeAssist:
         // Draw box with letter A inside
-        canvas_draw_frame(canvas, p->x, p->y, BOX_SIZE, BOX_SIZE);
-        canvas_draw_str(canvas, p->x + 8, p->y + 6, "A");
+        canvas_draw_frame(canvas, p->x - p->size / 2, p->y - p->size / 2, BOX_SIZE, BOX_SIZE);
+        canvas_draw_str(canvas, p->x, p->y - BOX_SIZE / 2, "A");
         break;
     default:
         //@todo Uknown Power Up Type Detected
         // Draw box with letter U inside
         canvas_draw_frame(canvas, p->x, p->y, BOX_SIZE, BOX_SIZE);
-        canvas_draw_str(canvas, p->x + 3, p->y + 3, "U");
+        canvas_draw_str(canvas, p->x, p->y, "U");
         FURI_LOG_E(TAG, "Unexpected Power Up Type Detected: %i", p->powerUpType);
         break;
     }
@@ -438,14 +482,14 @@ bool objects_are_colliding(float x1, float y1, float r1, float x2, float y2, flo
     float rsum = r1 + r2;
     return dx * dx + dy * dy < rsum * rsum;
 }
-
+//@todo ship_fire_bullet
 /* Create a new bullet headed in the same direction of the ship. */
 void ship_fire_bullet(AsteroidsApp* app) {
     // No power ups, only MAXBUL allowed
-    if(app->ship.isPowerUpActive == false && app->bullets_num == MAXBUL) return;
+    if(isPowerUpActive(app, PowerUpTypeFirePower) == false && app->bullets_num == MAXBUL) return;
 
     // Double the Fire Power
-    if((app->ship.powerUp == PowerUpTypeFirePower) && (app->bullets_num == MAXBUL * 2)) return;
+    if(isPowerUpActive(app, PowerUpTypeFirePower) && (app->bullets_num == (MAXBUL * 2))) return;
 
     notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_bullet_fired);
     Bullet* b = &app->bullets[app->bullets_num];
@@ -547,52 +591,95 @@ void asteroid_was_hit(AsteroidsApp* app, int id) {
     }
 }
 
+//@todo Add PowerUp
 PowerUp* add_powerUp(AsteroidsApp* app) {
     if(app->powerUps_num == MAXPOWERUPS) return NULL;
-    float size = 4;
+
+    // Randomly select power up for display
+    //@todo Random Power Up Select
+    // PowerUpType powerUpType = rand() % Number_of_PowerUps;
+    PowerUpType selected_powerUpType = PowerUpTypeFirePower;
+
+    FURI_LOG_I(TAG, "[add_powerUp] Power Ups Active: %i", app->powerUps_num);
+    // Don't add already existing power ups
+    if(isPowerUpAlreadyExists(app, selected_powerUpType)) {
+        FURI_LOG_I(TAG, "[add_powerUp] Power Up %i already active", selected_powerUpType);
+        return NULL;
+    }
+
+    float size = 10;
     float min_distance = 20;
     float x, y;
     do {
         x = rand() % SCREEN_XRES;
         y = rand() % SCREEN_YRES;
     } while(distance(app->ship.x, app->ship.y, x, y) < min_distance + size);
+
     PowerUp* p = &app->powerUps[app->powerUps_num++];
     p->x = x;
     p->y = y;
-    p->vx = 2 * (-.5 + ((float)rand() / RAND_MAX));
-    p->vy = 2 * (-.5 + ((float)rand() / RAND_MAX));
+    //@todo Disable Velocity
+    p->vx = 0; //2 * (-.5 + ((float)rand() / RAND_MAX));
+    p->vy = 0; //2 * (-.5 + ((float)rand() / RAND_MAX));
+    p->display_ttl = 500;
+    p->ttl = POWERUPSTTL;
+    p->size = (int)size;
     // p->size = size;
     // p->rot = 0;
     // p->rot_speed = ((float)rand() / RAND_MAX) / 10;
     // if(app->ticks & 1) p->rot_speed = -(p->rot_speed);
-    // p->shape_seed = rand() & 255;
 
     //@todo add powerup type, for now hardcoding to firepower
-    p->powerUpType = 3; //rand() % Number_of_PowerUps;
+    p->powerUpType = selected_powerUpType;
+    p->isPowerUpActive = false;
     return p;
 }
 
+//@todo remove_powerUp
 void remove_powerUp(AsteroidsApp* app, int id) {
     /* Replace the top powerUp with the empty space left
      * by the removal of this one. This way we always take the
      * array dense, which is an advantage when looping. */
+
     int n = --app->powerUps_num;
     if(n && id != n) app->powerUps[id] = app->powerUps[n];
 }
 
+//@todo powerUp_was_hit
 void powerUp_was_hit(AsteroidsApp* app, int id) {
     PowerUp* p = &app->powerUps[id];
-    remove_powerUp(app, id);
+    if(p->display_ttl == 0) return; // Don't collect if already collected
+
     switch(p->powerUpType) {
     case PowerUpTypeLife:
         if(app->lives < GAME_START_LIVES) app->lives++;
         break;
-    case PowerUpTypeFirePower:
-        app->powerUps[id].powerUpType = PowerUpTypeFirePower;
-        break;
+    // case PowerUpTypeFirePower:
+    //     app->powerUps[id].powerUpType = PowerUpTypeFirePower;
+    //     break;
     default:
         break;
     }
+    p->display_ttl = 0;
+    p->isPowerUpActive = true;
+}
+
+bool isPowerUpActive(AsteroidsApp* const app, PowerUpType const powerUpType) {
+    for(int i = 0; i < app->powerUps_num; i++) {
+        // PowerUp* p = &app->powerUps[i];
+        // if(p->powerUpType == powerUpType && p->ttl > 0 && p->display_ttl == 0) return true;
+        if(app->powerUps[i].isPowerUpActive && app->powerUps[i].powerUpType == powerUpType) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool isPowerUpAlreadyExists(AsteroidsApp* const app, PowerUpType const powerUpType) {
+    for(int i = 0; i < app->powerUps_num; i++) {
+        if(app->powerUps[i].powerUpType == powerUpType) return true;
+    }
+    return false;
 }
 
 /* Set gameover state. When in game-over mode, the game displays a gameover
@@ -668,8 +755,27 @@ void update_asteroids_position(AsteroidsApp* app) {
 
 void update_powerUps_position(AsteroidsApp* app) {
     for(int j = 0; j < app->powerUps_num; j++) {
-        update_pos_by_velocity(
-            &app->powerUps[j].x, &app->powerUps[j].y, app->powerUps[j].vx, app->powerUps[j].vy);
+        // @todo update_powerUps_position
+        if(app->powerUps[j].display_ttl > 0) {
+            update_pos_by_velocity(
+                &app->powerUps[j].x, &app->powerUps[j].y, app->powerUps[j].vx, app->powerUps[j].vy);
+        }
+    }
+}
+
+// @todo update_powerUp_status
+/* This updates the state of each power up collected and removes them if they have expired. */
+void update_powerUp_status(AsteroidsApp* app) {
+    for(int j = app->powerUps_num; j > 0; j--) {
+        if(app->powerUps[j].ttl > 0 && app->powerUps[j].isPowerUpActive) {
+            // Only decrement ttl if we actually picked up power up
+            app->powerUps[j].ttl--;
+        } else if(app->powerUps[j].ttl == 0 && app->powerUps[j].display_ttl == 0) {
+            // we've reached the end of life of the power up
+            // Time to remove it
+            app->powerUps[j].isPowerUpActive = false;
+            remove_powerUp(app, j);
+        }
     }
 }
 
@@ -743,7 +849,6 @@ void game_tick(void* ctx) {
             restart_game_after_gameover(app);
         }
         update_asteroids_position(app);
-        update_powerUps_position(app);
         view_port_update(app->view_port);
         return;
     }
@@ -773,10 +878,23 @@ void game_tick(void* ctx) {
         app->fire = false;
     }
 
+    // DEBUG: Show Power Up Status
+    for(int j = 0; j < app->powerUps_num; j++) {
+        PowerUp* p = &app->powerUps[j];
+        FURI_LOG_I(
+            TAG,
+            "Power Up Type: %d TTL: %lu Display_TTL: %lu PowerUpNum: %i",
+            p->powerUpType,
+            p->ttl,
+            p->display_ttl,
+            app->powerUps_num);
+    }
+
     /* Update positions and detect collisions. */
     update_pos_by_velocity(&app->ship.x, &app->ship.y, app->ship.vx, app->ship.vy);
     update_bullets_position(app);
     update_asteroids_position(app);
+    update_powerUp_status(app); //@todo update_powerUp_status
     update_powerUps_position(app);
     detect_collisions(app);
 
@@ -788,7 +906,8 @@ void game_tick(void* ctx) {
     }
 
     /* From time to time add a random power up */
-    if(app->powerUps_num == 0 || (random() % 5000) < (30 / (1 + app->powerUps_num))) {
+    //@todo game tick
+    if((app->powerUps_num == 0 || random() % 5000) < (30 / (1 + app->powerUps_num))) {
         add_powerUp(app);
     }
 
@@ -913,8 +1032,8 @@ int32_t asteroids_app_entry(void* p) {
     while(app->running) {
         FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100);
         if(qstat == FuriStatusOk) {
-            if(DEBUG_MSG)
-                FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", input.type, input.key);
+            // if(DEBUG_MSG)
+            // FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", input.type, input.key);
 
             /* Handle navigation here. Then handle view-specific inputs
              * in the view specific handling function. */