Update apps pt2

This commit is contained in:
Willy-JL
2023-03-09 01:51:24 +00:00
parent e4998bf330
commit a5ee8673d6
89 changed files with 825 additions and 283 deletions
+24
View File
@@ -0,0 +1,24 @@
Copyright (c) 2022-2023 Salvatore Sanfilippo <antirez at gmail dot com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+537 -29
View File
@@ -13,23 +13,51 @@
#include <math.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <asteroids_icons.h>
#define TAG "Asteroids" // Used for logging
#define DEBUG_MSG 1
#define DEBUG_MSG 0
#define SCREEN_XRES 128
#define SCREEN_YRES 64
#define GAME_START_LIVES 3
#define MAXLIVES 5 /* Max bonus lives allowed. */
#define TTLBUL 30 /* Bullet time to live, in ticks. */
#define MAXBUL 5 /* Max bullets on the screen. */
#define MAXBUL 50 /* Max bullets on the screen. */
//@todo MAX Asteroids
#define MAXAST 32 /* Max asteroids on the screen. */
#define MAXPOWERUPS 3 /* Max powerups allowed on screen */
#define POWERUPSTTL 400 /* Max powerup time to live, in ticks. */
#define SHIP_HIT_ANIMATION_LEN 15
#define SAVING_DIRECTORY "/ext/apps_data/asteroids"
#define SAVING_DIRECTORY "/ext/apps/Games"
#define SAVING_FILENAME SAVING_DIRECTORY "/game_asteroids.save"
#ifndef PI
#define PI 3.14159265358979f
#endif
/* ============================ Data structures ============================= */
typedef enum PowerUpType {
PowerUpTypeShield, // Shield
PowerUpTypeLife, // Extra life
PowerUpTypeFirePower, // Burst Fire power
// PowerUpTypeRadialFire, // Radial Fire power
PowerUpTypeNuke, // Nuke power
// PowerUpTypeClone, // Clone ship
// PowerUpTypeAssist, // Secondary ship
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). */
float 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 {
float x, /* Ship x position. */
@@ -51,6 +79,7 @@ typedef struct Asteroid {
uint8_t shape_seed; /* Seed to give random shape. */
} Asteroid;
// @todo AsteroidsApp
typedef struct AsteroidsApp {
/* GUI */
Gui* gui;
@@ -61,6 +90,7 @@ typedef struct AsteroidsApp {
/* Game state. */
int running; /* Once false exists the app. */
bool gameover; /* Gameover status. */
bool paused; /* Game paused status. */
uint32_t ticks; /* Game ticks. Increments at each refresh. */
uint32_t score; /* Game score. */
uint32_t highscore; /* Highscore. Shown on Game Over Screen */
@@ -73,11 +103,14 @@ typedef struct AsteroidsApp {
/* Ship state. */
struct Ship ship;
struct PowerUp powerUps[MAXPOWERUPS]; /* Each powerup state. */
int powerUps_num; /* Active powerups. */
/* Bullets state. */
struct Bullet bullets[MAXBUL]; /* Each bullet state. */
int bullets_num; /* Active bullets. */
uint32_t last_bullet_tick; /* Tick the last bullet was fired. */
uint32_t bullet_min_period; /* Minimum time between bullets in ms. */
/* Asteroids state. */
Asteroid asteroids[MAXAST]; /* Each asteroid state. */
@@ -133,9 +166,63 @@ const NotificationSequence sequence_bullet_fired = {
NULL,
};
const NotificationSequence sequence_powerup_collected = {
&message_vibro_on,
&message_delay_1,
&message_delay_1,
&message_delay_1,
&message_delay_1,
&message_delay_1,
&message_vibro_off,
NULL,
};
const NotificationSequence sequence_nuke = {
&message_blink_set_color_red,
&message_blink_start_100,
&message_vibro_on,
&message_delay_10,
&message_vibro_off,
&message_vibro_on,
&message_delay_10,
&message_vibro_off,
&message_vibro_on,
&message_delay_10,
&message_vibro_off,
&message_red_0,
&message_vibro_on,
&message_delay_10,
&message_delay_1,
&message_delay_1,
&message_vibro_off,
&message_vibro_on,
&message_delay_10,
&message_delay_1,
&message_delay_1,
&message_vibro_off,
&message_vibro_on,
&message_delay_10,
&message_delay_1,
&message_delay_1,
&message_vibro_off,
&message_blink_stop,
&message_vibro_off,
&message_sound_off,
NULL,
};
/* ============================== 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);
@@ -183,6 +270,8 @@ void lfsr_next(unsigned char* prev) {
*prev ^= *prev << 7; /* Mix things a bit more. */
}
/* ================================ Render ================================ */
/* Render the polygon 'poly' at x,y, rotated by the specified angle. */
void draw_poly(Canvas* const canvas, Poly* poly, uint8_t x, uint8_t y, float a) {
Poly rot;
@@ -249,20 +338,127 @@ void draw_left_lives(Canvas* const canvas, AsteroidsApp* app) {
}
}
/* Given the current position, update it according to the velocity and
* wrap it back to the other side if the object went over the screen. */
void update_pos_by_velocity(float* x, float* y, float vx, float vy) {
/* Return back from one side to the other of the screen. */
*x += vx;
*y += vy;
if(*x >= SCREEN_XRES)
*x = 0;
else if(*x < 0)
*x = SCREEN_XRES - 1;
if(*y >= SCREEN_YRES)
*y = 0;
else if(*y < 0)
*y = SCREEN_YRES - 1;
bool should_draw_powerUp(PowerUp* const p) {
// Just return if power up has already been picked up
if(p->display_ttl == 0) return false;
// Begin flashing power up when it is about to expire
if(p->display_ttl < 100) {
return p->display_ttl % 8 > 0;
}
return true;
}
void draw_powerUp_RemainingLife(Canvas* canvas, PowerUp* const p, int y_offset) {
if(!p->isPowerUpActive) return;
/*
Here we generate a reverse progress bar. The bar is 24 pixels wide and 1 pixel tall.
Calculate the percentage of hitpoints left: hitpoints / total hitpoints
Multiply the percentage by the width of the bar (in pixels): percentage * bar width
Subtract the result from the width of the bar to get the filled portion of the bar: bar width - (percentage * bar width)
Round the result to the nearest integer to get the final result.
400 / 400 = 1.0
1.0 * 24 = 24
24 - 24 = 0
Round(0) = 0
*/
int progress_bar_width = SCREEN_XRES / 4;
if(p->ttl > 0) {
canvas_set_color(canvas, ColorBlack);
int remaining = ceil(((float)p->ttl / (float)POWERUPSTTL) * (float)progress_bar_width);
if(remaining > 0) {
canvas_draw_line(
canvas,
(SCREEN_XRES / 2) - remaining, // x1
3 + y_offset, //y1
(SCREEN_XRES / 2) + remaining, //x2
3 + y_offset); // y2
}
}
}
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;
if(!should_draw_powerUp(p)) return;
canvas_set_color(canvas, ColorXOR);
// Display power up to be picked up
switch(p->powerUpType) {
case PowerUpTypeFirePower:
canvas_draw_icon(canvas, p->x, p->y, &I_firepower_shifted_9x10);
break;
case PowerUpTypeShield:
canvas_draw_icon(canvas, p->x, p->y, &I_shield_frame);
break;
case PowerUpTypeLife:
// Draw a heart
canvas_draw_icon(canvas, p->x, p->y, &I_heart_10x10);
break;
case PowerUpTypeNuke:
// canvas_draw_disc(canvas, p->x, p->y, p->size);
// canvas_draw_str(canvas, p->x, p->y, "N");
canvas_draw_icon(canvas, p->x, p->y, &I_nuke_10x10);
break;
// case PowerUpTypeRadialFire:
// // Draw box with letter R inside
// canvas_draw_str(canvas, p->x, p->y, "R");
// break;
// case PowerUpTypeAssist:
// // Draw box with letter A inside
// canvas_draw_str(canvas, p->x, p->y, "A");
// break;
// case PowerUpTypeClone:
// // Draw box with letter C inside
// canvas_draw_str(canvas, p->x, p->y, "C");
// break;
default:
//@todo Uknown Power Up Type Detected
// Draw box with letter U inside
canvas_draw_str(canvas, p->x, p->y, "?");
FURI_LOG_E(TAG, "Unexpected Power Up Type Detected: %i", p->powerUpType);
break;
}
}
void draw_shield(Canvas* const canvas, AsteroidsApp* app) {
if(isPowerUpActive(app, PowerUpTypeShield) == false) return;
canvas_set_color(canvas, ColorXOR);
// canvas_draw_disc(canvas, app->ship.x, app->ship.y, 4);
canvas_draw_circle(canvas, app->ship.x, app->ship.y, 8);
}
/* Render the current game screen. */
@@ -286,6 +482,9 @@ void render_callback(Canvas* const canvas, void* ctx) {
/* Draw ship, asteroids, bullets. */
draw_poly(canvas, &ShipPoly, app->ship.x, app->ship.y, app->ship.rot);
/* Draw shield if active. */
draw_shield(canvas, app);
if(key_pressed_time(app, InputKeyUp) > 0) {
notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_thrusters);
draw_poly(canvas, &ShipFirePoly, app->ship.x, app->ship.y, app->ship.rot);
@@ -295,6 +494,20 @@ void render_callback(Canvas* const canvas, void* ctx) {
for(int j = 0; j < app->asteroids_num; j++) draw_asteroid(canvas, &app->asteroids[j]);
for(int j = 0; j < app->powerUps_num; j++) {
draw_powerUps(canvas, &app->powerUps[j]);
draw_powerUp_RemainingLife(canvas, &app->powerUps[j], j);
}
if(app->paused) {
canvas_set_color(canvas, ColorXOR);
canvas_set_font(canvas, FontPrimary);
canvas_draw_rbox(canvas, 0, 0, SCREEN_XRES, SCREEN_YRES, 4);
canvas_draw_str_aligned(
canvas, SCREEN_XRES / 2, SCREEN_YRES / 2, AlignCenter, AlignCenter, "Paused");
return;
}
/* Game over text. */
if(app->gameover) {
canvas_set_color(canvas, ColorBlack);
@@ -331,6 +544,22 @@ void render_callback(Canvas* const canvas, void* ctx) {
/* ============================ Game logic ================================== */
/* Given the current position, update it according to the velocity and
* wrap it back to the other side if the object went over the screen. */
void update_pos_by_velocity(float* x, float* y, float vx, float vy) {
/* Return back from one side to the other of the screen. */
*x += vx;
*y += vy;
if(*x >= SCREEN_XRES)
*x = 0;
else if(*x < 0)
*x = SCREEN_XRES - 1;
if(*y >= SCREEN_YRES)
*y = 0;
else if(*y < 0)
*y = SCREEN_YRES - 1;
}
float distance(float x1, float y1, float x2, float y2) {
float dx = x1 - x2;
float dy = y1 - y2;
@@ -363,9 +592,16 @@ bool objects_are_colliding(float x1, float y1, float r1, float x2, float y2, flo
return dx * dx + dy * dy < rsum * rsum;
}
/* ================================ Bullets ================================ */
//@todo ship_fire_bullet
/* Create a new bullet headed in the same direction of the ship. */
void ship_fire_bullet(AsteroidsApp* app) {
if(app->bullets_num == MAXBUL) return;
// No power ups, only 5 bullets allowed
if(isPowerUpActive(app, PowerUpTypeFirePower) == false && app->bullets_num >= 5) return;
// Double the Fire Power
if(isPowerUpActive(app, PowerUpTypeFirePower) && (app->bullets_num >= (MAXBUL))) return;
notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_bullet_fired);
Bullet* b = &app->bullets[app->bullets_num];
b->x = app->ship.x;
@@ -401,6 +637,7 @@ void remove_bullet(AsteroidsApp* app, int bid) {
if(n && bid != n) app->bullets[bid] = app->bullets[n];
}
/* ================================ Asteroids ================================ */
/* Create a new asteroid, away from the ship. Return the
* pointer to the asteroid object, so that the caller can change
* certain things of the asteroid if needed. */
@@ -466,6 +703,159 @@ void asteroid_was_hit(AsteroidsApp* app, int id) {
}
}
/* ================================ Power Up ================================ */
bool isPowerUpCollidingWithEachOther(AsteroidsApp* app, float x, float y, float size) {
for(int i = 0; i < app->powerUps_num; i++) {
PowerUp* p2 = &app->powerUps[i];
if(objects_are_colliding(x, y, size, p2->x, p2->y, p2->size, 1)) return true;
}
return false;
}
bool should_trigger_rare_powerUp(PowerUpType selected_powerUpType) {
switch(selected_powerUpType) {
case PowerUpTypeLife: // Make extra life power up more rare
return rand() % 10 != 0;
case PowerUpTypeShield: // Make shield power up more rare
return rand() % 4 != 0;
default:
return true;
}
}
//@todo Add PowerUp
PowerUp* add_powerUp(AsteroidsApp* app) {
FURI_LOG_I(TAG, "add_powerUp: %i", app->powerUps_num);
if(app->powerUps_num == MAXPOWERUPS) return NULL; // Max Power Ups reached
if(app->lives == MAXLIVES) return NULL; // Max Lives reached
// Randomly select power up for display
PowerUpType selected_powerUpType = rand() % Number_of_PowerUps;
FURI_LOG_I(TAG, "[add_powerUp] Power Up Selected: %i", selected_powerUpType);
// Don't add already existing power ups
if(isPowerUpAlreadyExists(app, selected_powerUpType)) {
FURI_LOG_D(TAG, "[add_powerUp] Power Up %i already active", selected_powerUpType);
return NULL;
}
// Make some power ups more rare
if(!should_trigger_rare_powerUp(selected_powerUpType)) {
FURI_LOG_D(TAG, "[add_powerUp] Power Up %i not triggered", selected_powerUpType);
return NULL;
}
float size = 10;
float min_distance = 20;
float x, y;
do {
//Make sure power up is not spawned on the edge of the screen
x = rand() % (SCREEN_XRES - (int)size);
y = rand() % (SCREEN_YRES - (int)size);
//Also keep it away from the lives and score at the top of screen
if(y < size) y = size;
} while(
((distance(app->ship.x, app->ship.y, x, y) < min_distance + size) ||
isPowerUpCollidingWithEachOther(app, x, y, size)));
PowerUp* p = &app->powerUps[app->powerUps_num++];
p->x = x;
p->y = y;
//@todo Disable Velocity
p->vx = 0; //2 * (-.5 + ((float)rand() / RAND_MAX));
p->vy = 0; //2 * (-.5 + ((float)rand() / RAND_MAX));
p->display_ttl = 200;
p->ttl = POWERUPSTTL;
p->size = 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);
//@todo add powerup type, for now hardcoding to firepower
p->powerUpType = selected_powerUpType;
p->isPowerUpActive = false;
FURI_LOG_I(TAG, "[add_powerUp] Power Up Added: %i", p->powerUpType);
return p;
}
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;
}
//@todo remove_powerUp
void remove_powerUp(AsteroidsApp* app, int id) {
FURI_LOG_I(TAG, "remove_powerUp: %i", id);
// Invalid ID, ignore
if(id < 0) {
FURI_LOG_E(TAG, "remove_powerUp: Invalid ID: %i", id);
return;
}
// TODO: Break this out into object types that set the game state
// Return the bullet period to normal
if(app->powerUps[id].powerUpType == PowerUpTypeFirePower) {
app->bullet_min_period = 200;
}
/* 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];
}
void remove_all_astroids_and_bullets(AsteroidsApp* app) {
app->score += app->asteroids_num;
app->asteroids_num = 0;
app->bullets_num = 0;
}
//@todo powerUp_was_hit
void powerUp_was_hit(AsteroidsApp* app, int id) {
PowerUp* p = &app->powerUps[id];
if(p->display_ttl == 0) return; // Don't collect if already collected or expired
// Vibrate to indicate power up was collected
notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_powerup_collected);
switch(p->powerUpType) {
case PowerUpTypeLife:
if(app->lives < MAXLIVES) app->lives++;
remove_powerUp(app, id);
break;
case PowerUpTypeFirePower:
p->ttl = POWERUPSTTL / 2;
app->bullet_min_period = 100;
break;
case PowerUpTypeNuke:
//TODO: Animate explosion
notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_nuke);
// Simulate nuke explosion
remove_all_astroids_and_bullets(app);
break;
default:
break;
}
p->display_ttl = 0;
p->isPowerUpActive = true;
}
/* ================================ Game States ================================ */
/* Set gameover state. When in game-over mode, the game displays a gameover
* text with a background of many asteroids floating around. */
void game_over(AsteroidsApp* app) {
@@ -494,7 +884,9 @@ void restart_game(AsteroidsApp* app) {
app->ship.vx = 0;
app->ship.vy = 0;
app->bullets_num = 0;
app->powerUps_num = 0;
app->last_bullet_tick = 0;
app->bullet_min_period = 200;
app->asteroids_num = 0;
app->ship_hit = 0;
}
@@ -510,6 +902,7 @@ void restart_game_after_gameover(AsteroidsApp* app) {
restart_game(app);
}
/* ================================ Position & Status ================================ */
/* Move bullets. */
void update_bullets_position(AsteroidsApp* app) {
for(int j = 0; j < app->bullets_num; j++) {
@@ -536,6 +929,68 @@ void update_asteroids_position(AsteroidsApp* app) {
}
}
bool should_add_powerUp(AsteroidsApp* app) {
srand(furi_get_tick());
int random_number = rand() % 100 + 1;
// The chance of spawning a power-up decreases as the game goes on
int threshold = 100 - (app->score * 5);
// Make sure the threshold doesn't go below 10
threshold = (threshold < 10) ? 10 : threshold;
// FURI_LOG_I(
// TAG,
// "Random number: %d, threshold: %d Bool: %d",
// random_number,
// threshold,
// random_number <= threshold);
return random_number <= threshold;
}
void update_powerUps_position(AsteroidsApp* app) {
for(int j = 0; j < app->powerUps_num; j++) {
// @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 = 0; j < app->powerUps_num; 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].display_ttl > 0) {
app->powerUps[j].display_ttl--;
} else if(app->powerUps[j].ttl == 0 || app->powerUps[j].display_ttl == 0) {
FURI_LOG_I(
TAG,
"[update_powerUp_status] Power up expired!, ttl: %lu, display_ttl: %lu id: %d",
app->powerUps[j].ttl,
app->powerUps[j].display_ttl,
j);
// we've reached the end of life of the power up
// Time to remove it
app->powerUps[j].isPowerUpActive = false;
remove_powerUp(app, j);
j--; /* Process this power up index again: the removal will
fill it with the top power up to take the array dense. */
} else {
FURI_LOG_E(
TAG,
"[update_powerUp_status] Power up error! Invalid Index: %d ttl: %lu display_ttl: %lu PowerUp_Num: %d",
j,
app->powerUps[j].ttl,
app->powerUps[j].display_ttl,
app->powerUps_num);
}
}
}
/* Collision detection and game state update based on collisions. */
void detect_collisions(AsteroidsApp* app) {
/* Detect collision between bullet and asteroid. */
@@ -560,8 +1015,26 @@ void detect_collisions(AsteroidsApp* app) {
for(int j = 0; j < app->asteroids_num; j++) {
Asteroid* a = &app->asteroids[j];
if(objects_are_colliding(a->x, a->y, a->size, app->ship.x, app->ship.y, 4, 1)) {
ship_was_hit(app);
break;
if(isPowerUpActive(app, PowerUpTypeShield)) {
// Asteroid was hit with shield
notification_message(
furi_record_open(RECORD_NOTIFICATION), &sequence_bullet_fired);
asteroid_was_hit(app, j);
j--; /* Scan this j value again. */
} else {
// No sheild active, take damage
ship_was_hit(app);
break;
}
}
}
/* Detect collision between ship and powerUp. */
for(int j = 0; j < app->powerUps_num; j++) {
PowerUp* p = &app->powerUps[j];
if(objects_are_colliding(p->x, p->y, p->size, app->ship.x, app->ship.y, 4, 1)) {
powerUp_was_hit(app, j);
// break;
}
}
}
@@ -599,6 +1072,12 @@ void game_tick(void* ctx) {
update_asteroids_position(app);
view_port_update(app->view_port);
return;
} else if(app->paused) {
if(key_pressed_time(app, InputKeyBack) > 100 || key_pressed_time(app, InputKeyOk) > 100) {
app->paused = false;
}
view_port_update(app->view_port);
return;
}
/* Handle keypresses. */
@@ -617,19 +1096,32 @@ void game_tick(void* ctx) {
* asteroids_update_keypress_state() since depends on exact
* pressure timing. */
if(app->fire) {
uint32_t bullet_min_period = 200; // In milliseconds
uint32_t now = furi_get_tick();
if(now - app->last_bullet_tick >= bullet_min_period) {
if(now - app->last_bullet_tick >= app->bullet_min_period) {
ship_fire_bullet(app);
app->last_bullet_tick = now;
}
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);
/* From time to time, create a new asteroid. The more asteroids
@@ -639,6 +1131,13 @@ void game_tick(void* ctx) {
add_asteroid(app);
}
/* From time to time add a random power up */
//@todo game tick
// if(app->powerUps_num == 0 || random() % (500 + (100 * (int)app->score)) <= app->powerUps_num) {
if(should_add_powerUp(app)) {
add_powerUp(app);
}
app->ticks++;
view_port_update(app->view_port);
}
@@ -760,8 +1259,17 @@ 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 Pause */
if(input.type == InputTypeShort && input.key == InputKeyBack) {
app->paused = !app->paused;
if(app->paused) {
furi_timer_stop(timer);
} else {
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10);
}
}
/* Handle navigation here. Then handle view-specific inputs
* in the view specific handling function. */
@@ -775,11 +1283,11 @@ int32_t asteroids_app_entry(void* p) {
} else {
/* Useful to understand if the app is still alive when it
* does not respond because of bugs. */
if(DEBUG_MSG) {
static int c = 0;
c++;
if(!(c % 20)) FURI_LOG_E(TAG, "Loop timeout");
}
// if(DEBUG_MSG) {
// static int c = 0;
// c++;
// if(!(c % 20)) FURI_LOG_E(TAG, "Loop timeout");
// }
}
}
@@ -3,10 +3,14 @@ App(
name="Asteroids",
apptype=FlipperAppType.EXTERNAL,
entry_point="asteroids_app_entry",
cdefines=["APP_PROTOVIEW"],
cdefines=["APP_ASTEROIDS"],
requires=["gui"],
stack_size=8 * 1024,
order=50,
fap_icon="appicon.png",
fap_category="Games",
fap_icon_assets="assets", # Image assets to compile for this application
fap_description="An implementation of the classic arcade game Asteroids",
fap_author="antirez, SimplyMinimal",
fap_weburl="https://github.com/SimplyMinimal/FlipperZero-Asteroids",
)
Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B