Просмотр исходного кода

debug mode for tables, idle timeout

rdefeo 1 год назад
Родитель
Сommit
66e5ccaae5

+ 9 - 3
README.md

@@ -22,6 +22,7 @@ This is a BETA release!! Still a work in progress...
 * Rollover items
 * Rollover items
 * Sounds! Blinky lights! Annoying vibrations!
 * Sounds! Blinky lights! Annoying vibrations!
 * Customizable notification settings: sound, LED, vibration
 * Customizable notification settings: sound, LED, vibration
+* Idle timeout to save battery - will exit after ~120 seconds of no key-presses
 
 
 ## Controls
 ## Controls
 * **Ok** to release the ball
 * **Ok** to release the ball
@@ -34,17 +35,22 @@ I find it easiest to hold the flipper with both hands so I can hit left/right wi
 ## Settings
 ## Settings
 The **SETTINGS** menu will be the "last" table listed. You can Enable / Disable the following: Sound, LED light, Vibration, and Manual mode. Move Up/Down to select your setting and press **OK** to toggle. Settings are saved in `/data/.pinball0.conf` as a native Flipper Format file. **Back** will return you to the main menu.
 The **SETTINGS** menu will be the "last" table listed. You can Enable / Disable the following: Sound, LED light, Vibration, and Manual mode. Move Up/Down to select your setting and press **OK** to toggle. Settings are saved in `/data/.pinball0.conf` as a native Flipper Format file. **Back** will return you to the main menu.
 
 
-**Manual** mode allows you to move the ball using the directional pad _before_ the ball is launched. This is useful for testing and may be removed in the future. May result in unexpected behavior.
+**Debug** mode allows you to move the ball using the directional pad _before_ the ball is launched. This is useful for testing and may be removed in the future. May result in unexpected behavior. It also displays test tables on the main menu. The test tables will only show/hide after you exit and restart the app. This feature is mainly for me - lol.
 
 
 ## Tables
 ## Tables
-Pinball0 ships with several default tables. These tables are automatically deployed into the assets folder (`/apps_data/pinball0`). Tables are simple JSON which means you can define your own! Your tables should be stored in the data folder (`/apps_data/pinball0`). **The default tables may change over time.**
+Pinball0 ships with several default tables. These tables are automatically deployed into the assets folder (`/apps_data/pinball0`) on your SD card. Tables are simple JSON which means you can define your own! Your tables should be stored in the data folder (`/apps_data/pinball0`). On the main menu, tables are sorted alphabetically. In order to "force" a sorting order, you can prepend any filename with `NN_` where `NN` is between `00` and `99`. When the files are displayed on the menu, if they start with `NN_`, that will be stripped - but their sorted order will be preserved.
+
+> The default tables may change over time.
+
+In **Debug** mode, test tables will be shown. A test table is one that begins with the text `dbg`. Given that you can prefix table names for sorting purposes, here are two valid table filenames for a test table called `my FLIPS`: `dbg my FLIPS.json` and `04_dbg my FLIPS.json`. In both cases it will be displayed as `dbg my FLIPS` on the menu. I doubt that you will use this feature, but I'm documenting it anyway.
+
 
 
 ### File Format
 ### File Format
 Table units are specified at a 10x scale. This means our table is **630 x 1270** in size (as the F0 display is 64 pixels x 128 pixels). Our origin is in the top-left at 0, 0. Check out the default tables in the `assets/tables` folder for example usage.
 Table units are specified at a 10x scale. This means our table is **630 x 1270** in size (as the F0 display is 64 pixels x 128 pixels). Our origin is in the top-left at 0, 0. Check out the default tables in the `assets/tables` folder for example usage.
 
 
 The JSON can include comments - because why not!
 The JSON can include comments - because why not!
 
 
-> **DISCLAIMER:** The file format may change from release to release. Sorry. There is some basic error checking when reading / parsing the table files. If the error is serious enough, you will see an error message in the app. Otherwise, check the console logs. For those familiar with `ufbt`, simply run `ufbt cli` and issue the `log` command. Then launch Pinball0. All informational and higher logs will be displayed.
+> **DISCLAIMER:** The file format may change from release to release. Sorry. There is some basic error checking when reading / parsing the table files. If the error is serious enough, you will see an error message in the app. Otherwise, check the console logs. For those familiar with `ufbt`, simply run `ufbt cli` and issue the `log` command. Then launch Pinball0. All informational and higher logs will be displayed. These logs are useful when reporting bugs/issues!
 
 
 #### lives : object (optional)
 #### lives : object (optional)
 Defines how many lives/balls you start with, and display information
 Defines how many lives/balls you start with, and display information

+ 0 - 0
assets/tables/40_ex Arc Test.json → assets/tables/40_dbg Arc Test.json


+ 0 - 0
assets/tables/50_ex Bumpers.json → assets/tables/50_dbg Bumpers.json


+ 0 - 0
assets/tables/70_ex Platforms.json → assets/tables/70_dbg Platforms.json


+ 0 - 0
assets/tables/95_ex Error.json → assets/tables/95_dbg Error.json


+ 31 - 18
pinball0.cxx

@@ -14,15 +14,17 @@
 #define GRAVITY           3.0f // 9.8f
 #define GRAVITY           3.0f // 9.8f
 #define PHYSICS_SUB_STEPS 5
 #define PHYSICS_SUB_STEPS 5
 #define GAME_FPS          30
 #define GAME_FPS          30
-#define TABLE_BUMP_AMOUNT 0.3l
-
 #define MANUAL_ADJUSTMENT 20
 #define MANUAL_ADJUSTMENT 20
+#define IDLE_TIMEOUT      120 * 1000 // 120 seconds * 1000 ticks/sec
 
 
 #define PINBALL_SETTINGS_FILENAME     ".pinball0.conf"
 #define PINBALL_SETTINGS_FILENAME     ".pinball0.conf"
 #define PINBALL_SETTINGS_PATH         APP_DATA_PATH(PINBALL_SETTINGS_FILENAME)
 #define PINBALL_SETTINGS_PATH         APP_DATA_PATH(PINBALL_SETTINGS_FILENAME)
 #define PINBALL_SETTINGS_FILE_TYPE    "Pinball0 Settings File"
 #define PINBALL_SETTINGS_FILE_TYPE    "Pinball0 Settings File"
 #define PINBALL_SETTINGS_FILE_VERSION 1
 #define PINBALL_SETTINGS_FILE_VERSION 1
 
 
+namespace {
+uint32_t idle_start;
+
 void pinball_load_settings(PinballApp* pb) {
 void pinball_load_settings(PinballApp* pb) {
     FlipperFormat* fff_settings = flipper_format_file_alloc(pb->storage);
     FlipperFormat* fff_settings = flipper_format_file_alloc(pb->storage);
     FuriString* tmp_str = furi_string_alloc();
     FuriString* tmp_str = furi_string_alloc();
@@ -33,7 +35,7 @@ void pinball_load_settings(PinballApp* pb) {
     pb->settings.sound_enabled = true;
     pb->settings.sound_enabled = true;
     pb->settings.led_enabled = true;
     pb->settings.led_enabled = true;
     pb->settings.vibrate_enabled = true;
     pb->settings.vibrate_enabled = true;
-    pb->settings.manual_mode = false;
+    pb->settings.debug_mode = false;
     pb->selected_setting = 0;
     pb->selected_setting = 0;
     pb->max_settings = 4;
     pb->max_settings = 4;
 
 
@@ -61,8 +63,8 @@ void pinball_load_settings(PinballApp* pb) {
         if(flipper_format_read_uint32(fff_settings, "Vibrate", &tmp_data32, 1)) {
         if(flipper_format_read_uint32(fff_settings, "Vibrate", &tmp_data32, 1)) {
             pb->settings.vibrate_enabled = (tmp_data32 == 0) ? false : true;
             pb->settings.vibrate_enabled = (tmp_data32 == 0) ? false : true;
         }
         }
-        if(flipper_format_read_uint32(fff_settings, "Manual", &tmp_data32, 1)) {
-            pb->settings.manual_mode = (tmp_data32 == 0) ? false : true;
+        if(flipper_format_read_uint32(fff_settings, "Debug", &tmp_data32, 1)) {
+            pb->settings.debug_mode = (tmp_data32 == 0) ? false : true;
         }
         }
 
 
     } while(false);
     } while(false);
@@ -101,9 +103,9 @@ void pinball_save_settings(PinballApp* pb) {
             FURI_LOG_E(TAG, "SETTINGS: Failed to write 'Vibrate'");
             FURI_LOG_E(TAG, "SETTINGS: Failed to write 'Vibrate'");
             break;
             break;
         }
         }
-        tmp_data32 = pb->settings.manual_mode ? 1 : 0;
-        if(!flipper_format_write_uint32(fff_settings, "Manual", &tmp_data32, 1)) {
-            FURI_LOG_E(TAG, "SETTINGS: Failed to write 'Manual'");
+        tmp_data32 = pb->settings.debug_mode ? 1 : 0;
+        if(!flipper_format_write_uint32(fff_settings, "Debug", &tmp_data32, 1)) {
+            FURI_LOG_E(TAG, "SETTINGS: Failed to write 'Debug'");
             break;
             break;
         }
         }
     } while(false);
     } while(false);
@@ -111,6 +113,7 @@ void pinball_save_settings(PinballApp* pb) {
     flipper_format_file_close(fff_settings);
     flipper_format_file_close(fff_settings);
     flipper_format_free(fff_settings);
     flipper_format_free(fff_settings);
 }
 }
+};
 
 
 void solve(PinballApp* pb, float dt) {
 void solve(PinballApp* pb, float dt) {
     Table* table = pb->table;
     Table* table = pb->table;
@@ -126,7 +129,6 @@ void solve(PinballApp* pb, float dt) {
             }
             }
             for(auto& b : table->balls) {
             for(auto& b : table->balls) {
                 // We multiply GRAVITY by dt since gravity is based on seconds
                 // We multiply GRAVITY by dt since gravity is based on seconds
-                FURI_LOG_I(TAG, "GRAVI-TAYYY");
                 b.accelerate(Vec2(0, GRAVITY * bump_amt * sub_dt));
                 b.accelerate(Vec2(0, GRAVITY * bump_amt * sub_dt));
             }
             }
         }
         }
@@ -384,9 +386,9 @@ static void pinball_draw_callback(Canvas* const canvas, void* ctx) {
         }
         }
         y += 12;
         y += 12;
 
 
-        canvas_draw_str_aligned(canvas, 10, y, AlignLeft, AlignTop, "Manual");
+        canvas_draw_str_aligned(canvas, 10, y, AlignLeft, AlignTop, "Debug");
         canvas_draw_circle(canvas, x, y + 3, 4);
         canvas_draw_circle(canvas, x, y + 3, 4);
-        if(pb->settings.manual_mode) {
+        if(pb->settings.debug_mode) {
             canvas_draw_disc(canvas, x, y + 3, 2);
             canvas_draw_disc(canvas, x, y + 3, 2);
         }
         }
         if(pb->selected_setting == 3) {
         if(pb->selected_setting == 3) {
@@ -461,6 +463,7 @@ extern "C" int32_t pinball0_app(void* p) {
 
 
     float dt = 0.0f;
     float dt = 0.0f;
     uint32_t last_frame_time = furi_get_tick();
     uint32_t last_frame_time = furi_get_tick();
+    idle_start = last_frame_time;
 
 
     FURI_LOG_I(TAG, "Starting event loop");
     FURI_LOG_I(TAG, "Starting event loop");
     PinballEvent event;
     PinballEvent event;
@@ -489,7 +492,7 @@ extern "C" int32_t pinball0_app(void* p) {
                     case InputKeyRight: {
                     case InputKeyRight: {
                         app->keys[InputKeyRight] = true;
                         app->keys[InputKeyRight] = true;
 
 
-                        if(app->settings.manual_mode && app->table->balls_released == false) {
+                        if(app->settings.debug_mode && app->table->balls_released == false) {
                             app->table->balls[0].p.x += MANUAL_ADJUSTMENT;
                             app->table->balls[0].p.x += MANUAL_ADJUSTMENT;
                             app->table->balls[0].prev_p.x += MANUAL_ADJUSTMENT;
                             app->table->balls[0].prev_p.x += MANUAL_ADJUSTMENT;
                         }
                         }
@@ -507,7 +510,7 @@ extern "C" int32_t pinball0_app(void* p) {
                     case InputKeyLeft: {
                     case InputKeyLeft: {
                         app->keys[InputKeyLeft] = true;
                         app->keys[InputKeyLeft] = true;
 
 
-                        if(app->settings.manual_mode && app->table->balls_released == false) {
+                        if(app->settings.debug_mode && app->table->balls_released == false) {
                             app->table->balls[0].p.x -= MANUAL_ADJUSTMENT;
                             app->table->balls[0].p.x -= MANUAL_ADJUSTMENT;
                             app->table->balls[0].prev_p.x -= MANUAL_ADJUSTMENT;
                             app->table->balls[0].prev_p.x -= MANUAL_ADJUSTMENT;
                         }
                         }
@@ -531,9 +534,10 @@ extern "C" int32_t pinball0_app(void* p) {
                                 // we only set the key if it's a 'press' to ensure
                                 // we only set the key if it's a 'press' to ensure
                                 // a single table "bump"
                                 // a single table "bump"
                                 app->keys[InputKeyUp] = true;
                                 app->keys[InputKeyUp] = true;
+
                                 notify_table_bump(app);
                                 notify_table_bump(app);
                             }
                             }
-                            if(app->settings.manual_mode && app->table->balls_released == false) {
+                            if(app->settings.debug_mode && app->table->balls_released == false) {
                                 app->table->balls[0].p.y -= MANUAL_ADJUSTMENT;
                                 app->table->balls[0].p.y -= MANUAL_ADJUSTMENT;
                                 app->table->balls[0].prev_p.y -= MANUAL_ADJUSTMENT;
                                 app->table->balls[0].prev_p.y -= MANUAL_ADJUSTMENT;
                             }
                             }
@@ -556,7 +560,7 @@ extern "C" int32_t pinball0_app(void* p) {
                         switch(app->game_mode) {
                         switch(app->game_mode) {
                         case GM_Playing:
                         case GM_Playing:
                             app->keys[InputKeyDown] = true;
                             app->keys[InputKeyDown] = true;
-                            if(app->settings.manual_mode && app->table->balls_released == false) {
+                            if(app->settings.debug_mode && app->table->balls_released == false) {
                                 app->table->balls[0].p.y += MANUAL_ADJUSTMENT;
                                 app->table->balls[0].p.y += MANUAL_ADJUSTMENT;
                                 app->table->balls[0].prev_p.y += MANUAL_ADJUSTMENT;
                                 app->table->balls[0].prev_p.y += MANUAL_ADJUSTMENT;
                             }
                             }
@@ -610,7 +614,7 @@ extern "C" int32_t pinball0_app(void* p) {
                                 app->settings.vibrate_enabled = !app->settings.vibrate_enabled;
                                 app->settings.vibrate_enabled = !app->settings.vibrate_enabled;
                                 break;
                                 break;
                             case 3:
                             case 3:
-                                app->settings.manual_mode = !app->settings.manual_mode;
+                                app->settings.debug_mode = !app->settings.debug_mode;
                                 break;
                                 break;
                             default:
                             default:
                                 break;
                                 break;
@@ -654,6 +658,8 @@ extern "C" int32_t pinball0_app(void* p) {
                         break;
                         break;
                     }
                     }
                 }
                 }
+                // a key was pressed, reset idle counter
+                idle_start = furi_get_tick();
             }
             }
         }
         }
         solve(app, dt);
         solve(app, dt);
@@ -672,8 +678,15 @@ extern "C" int32_t pinball0_app(void* p) {
         view_port_update(view_port);
         view_port_update(view_port);
         furi_mutex_release(app->mutex);
         furi_mutex_release(app->mutex);
 
 
-        // game timing
-        uint32_t time_lapsed = furi_get_tick() - last_frame_time;
+        // game timing + idle check
+        uint32_t current_tick = furi_get_tick();
+        if(current_tick - idle_start >= IDLE_TIMEOUT) {
+            FURI_LOG_W(TAG, "Idle timeout! Exiting Pinball0...");
+            app->processing = false;
+            break;
+        }
+
+        uint32_t time_lapsed = current_tick - last_frame_time;
         dt = time_lapsed / 1000.0f;
         dt = time_lapsed / 1000.0f;
         while(dt < 1.0f / GAME_FPS) {
         while(dt < 1.0f / GAME_FPS) {
             time_lapsed = furi_get_tick() - last_frame_time;
             time_lapsed = furi_get_tick() - last_frame_time;

+ 3 - 1
pinball0.h

@@ -65,11 +65,13 @@ typedef struct PinballApp {
         bool sound_enabled;
         bool sound_enabled;
         bool vibrate_enabled;
         bool vibrate_enabled;
         bool led_enabled;
         bool led_enabled;
-        bool manual_mode;
+        bool debug_mode;
     } settings;
     } settings;
     int selected_setting;
     int selected_setting;
     int max_settings;
     int max_settings;
 
 
+    uint32_t idle_start; // tick count (time) of last key press
+
     // system objects
     // system objects
     Storage* storage;
     Storage* storage;
     NotificationApp* notify; // allows us to blink/buzz during game
     NotificationApp* notify; // allows us to blink/buzz during game

+ 31 - 10
table.cxx

@@ -80,6 +80,8 @@ void table_table_list_init(void* ctx) {
     // sort tables by original filename
     // sort tables by original filename
 
 
     const char* paths[] = {APP_ASSETS_PATH("tables"), APP_DATA_PATH("tables")};
     const char* paths[] = {APP_ASSETS_PATH("tables"), APP_DATA_PATH("tables")};
+    const size_t ext_len_max = 32;
+    char ext[ext_len_max];
 
 
     for(size_t p = 0; p < 2; p++) {
     for(size_t p = 0; p < 2; p++) {
         const char* path = paths[p];
         const char* path = paths[p];
@@ -92,25 +94,44 @@ void table_table_list_init(void* ctx) {
         dir_walk_set_recursive(dir_walk, false);
         dir_walk_set_recursive(dir_walk, false);
         if(dir_walk_open(dir_walk, path)) {
         if(dir_walk_open(dir_walk, path)) {
             while(dir_walk_read(dir_walk, table_path, NULL) == DirWalkOK) {
             while(dir_walk_read(dir_walk, table_path, NULL) == DirWalkOK) {
-                FURI_LOG_I(TAG, furi_string_get_cstr(table_path));
-                // set display 'name' and 'filename'
-                TableMenuItem tmi;
+                path_extract_extension(table_path, ext, ext_len_max);
+                if(strcmp(ext, ".json") != 0) {
+                    FURI_LOG_W(
+                        TAG, "Skipping non-json file: %s", furi_string_get_cstr(table_path));
+                    continue;
+                }
                 const char* cpath = furi_string_get_cstr(table_path);
                 const char* cpath = furi_string_get_cstr(table_path);
-                tmi.filename = furi_string_alloc_set_str(cpath);
 
 
-                tmi.name = furi_string_alloc();
-                path_extract_filename_no_ext(cpath, tmi.name);
+                FuriString* filename_no_ext = furi_string_alloc();
+                path_extract_filename_no_ext(cpath, filename_no_ext);
 
 
                 // If filename starts with XX_ (for custom sorting) strip the prefix
                 // If filename starts with XX_ (for custom sorting) strip the prefix
-                char c = furi_string_get_char(tmi.name, 2);
+                char c = furi_string_get_char(filename_no_ext, 2);
                 if(c == '_') {
                 if(c == '_') {
-                    char a = furi_string_get_char(tmi.name, 0);
-                    char b = furi_string_get_char(tmi.name, 1);
+                    char a = furi_string_get_char(filename_no_ext, 0);
+                    char b = furi_string_get_char(filename_no_ext, 1);
                     if(a >= '0' && a <= '9' && b >= '0' && b <= '9') {
                     if(a >= '0' && a <= '9' && b >= '0' && b <= '9') {
-                        furi_string_right(tmi.name, 3);
+                        furi_string_right(filename_no_ext, 3);
                     }
                     }
                 }
                 }
 
 
+                if(!pb->settings.debug_mode &&
+                   !strncmp("dbg", furi_string_get_cstr(filename_no_ext), 3)) {
+                    furi_string_free(filename_no_ext);
+                    continue;
+                }
+
+                FURI_LOG_I(
+                    TAG,
+                    "Found table: name=%s | path=%s",
+                    furi_string_get_cstr(filename_no_ext),
+                    furi_string_get_cstr(table_path));
+
+                // set display 'name' and 'filename'
+                TableMenuItem tmi;
+                tmi.filename = furi_string_alloc_set_str(cpath);
+                tmi.name = filename_no_ext;
+
                 // Insert in sorted order
                 // Insert in sorted order
                 size_t i = 0;
                 size_t i = 0;
                 auto it = pb->table_list.menu_items.begin();
                 auto it = pb->table_list.menu_items.begin();