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

Merge pull request #18 from jblanked/dev_0.6

FlipWorld - v0.6
JBlanked 10 месяцев назад
Родитель
Сommit
4eb3ccf47a
19 измененных файлов с 818 добавлено и 1168 удалено
  1. 1 2
      README.md
  2. 17 3
      alloc/alloc.c
  3. 1 1
      app.c
  4. 1 1
      application.fam
  5. BIN
      assets/01-home.png
  6. 8 2
      assets/CHANGELOG.md
  7. 1 2
      assets/README.md
  8. 56 43
      callback/callback.c
  9. 1 0
      flip_world.c
  10. 8 2
      flip_world.h
  11. 539 787
      flipper_http/flipper_http.c
  12. 108 273
      flipper_http/flipper_http.h
  13. 5 12
      game/draw.c
  14. 11 11
      game/enemy.c
  15. 26 10
      game/game.c
  16. 22 15
      game/player.c
  17. 11 0
      game/player.h
  18. 1 3
      game/storage.c
  19. 1 1
      game/world.c

+ 1 - 2
README.md

@@ -80,10 +80,9 @@ NPCs are friendly characters that players can interact with. Currently, you can
 
 **v0.6**
 - New game features
-- Custom Controller Support
 
 **v0.7**
-- ???
+- New game features
 
 **v0.8**
 - Multiplayer support

+ 17 - 3
alloc/alloc.c

@@ -16,7 +16,8 @@ void *global_app;
 void flip_world_show_submenu()
 {
     FlipWorldApp *app = (FlipWorldApp *)global_app;
-    if (app->submenu) {
+    if (app->submenu)
+    {
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipWorldViewSubmenu);
     }
 }
@@ -51,10 +52,17 @@ FlipWorldApp *flip_world_app_alloc()
     {
         return NULL;
     }
-    submenu_add_item(app->submenu, "Play", FlipWorldSubmenuIndexRun, callback_submenu_choices, app);
+    if (!easy_flipper_set_submenu(&app->submenu_game, FlipWorldViewGameSubmenu, "Play", callback_to_submenu, &app->view_dispatcher))
+    {
+        return NULL;
+    }
+    submenu_add_item(app->submenu, "Play", FlipWorldSubmenuIndexGameSubmenu, callback_submenu_choices, app);
     submenu_add_item(app->submenu, "About", FlipWorldSubmenuIndexMessage, callback_submenu_choices, app);
     submenu_add_item(app->submenu, "Settings", FlipWorldSubmenuIndexSettings, callback_submenu_choices, app);
     //
+    submenu_add_item(app->submenu_game, "Story", FlipWorldSubmenuIndexStory, callback_submenu_choices, app);
+    submenu_add_item(app->submenu_game, "PvP", FlipWorldSubmenuIndexPvP, callback_submenu_choices, app);
+    submenu_add_item(app->submenu_game, "PvE", FlipWorldSubmenuIndexPvE, callback_submenu_choices, app);
 
     // Switch to the main view
     view_dispatcher_switch_to_view(app->view_dispatcher, FlipWorldViewSubmenu);
@@ -77,6 +85,11 @@ void flip_world_app_free(FlipWorldApp *app)
         view_dispatcher_remove_view(app->view_dispatcher, FlipWorldViewSubmenu);
         submenu_free(app->submenu);
     }
+    if (app->submenu_game)
+    {
+        view_dispatcher_remove_view(app->view_dispatcher, FlipWorldViewGameSubmenu);
+        submenu_free(app->submenu_game);
+    }
     // Free Widget(s)
     if (app->widget_result)
     {
@@ -101,5 +114,6 @@ void flip_world_app_free(FlipWorldApp *app)
     furi_record_close(RECORD_GUI);
 
     // free the app
-    if (app) free(app);
+    if (app)
+        free(app);
 }

+ 1 - 1
app.c

@@ -24,7 +24,7 @@ int32_t flip_world_main(void *p)
         return -1;
     }
 
-    if (!flipper_http_ping(fhttp))
+    if (!flipper_http_send_command(fhttp, HTTP_CMD_PING))
     {
         FURI_LOG_E(TAG, "Failed to ping the device");
         flipper_http_free(fhttp);

+ 1 - 1
application.fam

@@ -17,5 +17,5 @@ App(
     ),
     fap_author="JBlanked",
     fap_weburl="https://github.com/jblanked/FlipWorld",
-    fap_version="0.5",
+    fap_version="0.6",
 )

BIN
assets/01-home.png


+ 8 - 2
assets/CHANGELOG.md

@@ -1,3 +1,9 @@
+## 0.6 (2025-03-10)
+- Fixed saving of player attributes so that it works as intended.
+- Updated the player's level and strength as XP increases.
+- Started implementing multiplayer (requires FlipperHTTP v1.7).
+- Fixed the display of user stats when switching worlds.
+
 ## 0.5 (2025-01-31)
 - Fixed saving errors.
 - Improved memory allocation.
@@ -5,8 +11,8 @@
 
 ## 0.4 (2025-01-23)
 - Added an In-Game menu.
-- Added New controls (HOLD OK to access the In-Game menu, PRESS BACK to exit the menu, and HOLD BACK to leave the game).
-- Added option to choose player weapon in the Game Settings.
+- Added new controls (HOLD OK to access the In-Game menu, PRESS BACK to exit the menu, and HOLD BACK to leave the game).
+- Added option to choose player's weapon in the Game Settings.
 - Added transition icon for switching worlds.
 - Doubled the size of each world (from 384x192 to 768x384).
 - Improved memory allocation.

+ 1 - 2
assets/README.md

@@ -79,10 +79,9 @@ NPCs are friendly characters that players can interact with. Currently, you can
 
 **v0.6**
 - New game features
-- Custom Controller Support
 
 **v0.7**
-- ???
+- New game features
 
 **v0.8**
 - Multiplayer support

+ 56 - 43
callback/callback.c

@@ -11,7 +11,7 @@
 // FURI_LOG_DEV will log only during app development. Be sure that Settings/System/Log Device is "LPUART"; so we dont use serial port.
 #ifdef DEVELOPMENT
 #define FURI_LOG_DEV(tag, format, ...) furi_log_print_format(FuriLogLevelInfo, tag, format, ##__VA_ARGS__)
-#define DEV_CRASH()                    furi_crash()
+#define DEV_CRASH() furi_crash()
 #else
 #define FURI_LOG_DEV(tag, format, ...)
 #define DEV_CRASH()
@@ -430,7 +430,7 @@ static bool alloc_variable_item_list(void *context, uint32_t view_id)
             char _game_fps[8];
             if (load_char("Game-FPS", _game_fps, sizeof(_game_fps)))
             {
-                                int index = is_str(_game_fps, "30") ? 0 : is_str(_game_fps, "60") ? 1
+                int index = is_str(_game_fps, "30") ? 0 : is_str(_game_fps, "60") ? 1
                                                       : is_str(_game_fps, "120")  ? 2
                                                       : is_str(_game_fps, "240")  ? 3
                                                                                   : 0;
@@ -441,20 +441,19 @@ static bool alloc_variable_item_list(void *context, uint32_t view_id)
             if (load_char("Game-VGM-X", _game_vgm_x, sizeof(_game_vgm_x)))
             {
                 int vgm_x = atoi(_game_vgm_x);
-                int index = vgm_x == -2 ? 0 :
-                            vgm_x == -1 ? 1 :
-                            vgm_x == 0  ? 2 :
-                            vgm_x == 1  ? 3 :
-                            vgm_x == 2  ? 4 :
-                            vgm_x == 3  ? 5 :
-                            vgm_x == 4  ? 6 :
-                            vgm_x == 5  ? 7 :
-                            vgm_x == 6  ? 8 :
-                            vgm_x == 7  ? 9 :
-                            vgm_x == 8  ? 10 :
-                            vgm_x == 9  ? 11 :
-                            vgm_x == 10 ? 12 :
-                                          2;
+                int index = vgm_x == -2 ? 0 : vgm_x == -1 ? 1
+                                          : vgm_x == 0    ? 2
+                                          : vgm_x == 1    ? 3
+                                          : vgm_x == 2    ? 4
+                                          : vgm_x == 3    ? 5
+                                          : vgm_x == 4    ? 6
+                                          : vgm_x == 5    ? 7
+                                          : vgm_x == 6    ? 8
+                                          : vgm_x == 7    ? 9
+                                          : vgm_x == 8    ? 10
+                                          : vgm_x == 9    ? 11
+                                          : vgm_x == 10   ? 12
+                                                          : 2;
                 variable_item_set_current_value_index(app->variable_item_game_vgm_x, index);
                 variable_item_set_current_value_text(app->variable_item_game_vgm_x, vgm_levels[index]);
             }
@@ -462,41 +461,43 @@ static bool alloc_variable_item_list(void *context, uint32_t view_id)
             if (load_char("Game-VGM-Y", _game_vgm_y, sizeof(_game_vgm_y)))
             {
                 int vgm_y = atoi(_game_vgm_y);
-                int index = vgm_y == -2 ? 0 :
-                            vgm_y == -1 ? 1 :
-                            vgm_y == 0  ? 2 :
-                            vgm_y == 1  ? 3 :
-                            vgm_y == 2  ? 4 :
-                            vgm_y == 3  ? 5 :
-                            vgm_y == 4  ? 6 :
-                            vgm_y == 5  ? 7 :
-                            vgm_y == 6  ? 8 :
-                            vgm_y == 7  ? 9 :
-                            vgm_y == 8  ? 10 :
-                            vgm_y == 9  ? 11 :
-                            vgm_y == 10 ? 12 :
-                                          2;
+                int index = vgm_y == -2 ? 0 : vgm_y == -1 ? 1
+                                          : vgm_y == 0    ? 2
+                                          : vgm_y == 1    ? 3
+                                          : vgm_y == 2    ? 4
+                                          : vgm_y == 3    ? 5
+                                          : vgm_y == 4    ? 6
+                                          : vgm_y == 5    ? 7
+                                          : vgm_y == 6    ? 8
+                                          : vgm_y == 7    ? 9
+                                          : vgm_y == 8    ? 10
+                                          : vgm_y == 9    ? 11
+                                          : vgm_y == 10   ? 12
+                                                          : 2;
                 variable_item_set_current_value_index(app->variable_item_game_vgm_y, index);
                 variable_item_set_current_value_text(app->variable_item_game_vgm_y, vgm_levels[index]);
             }
             char _game_screen_always_on[8];
             if (load_char("Game-Screen-Always-On", _game_screen_always_on, sizeof(_game_screen_always_on)))
             {
-                int index = is_str(_game_screen_always_on, "No") ? 0 : is_str(_game_screen_always_on, "Yes") ? 1 : 0;
+                int index = is_str(_game_screen_always_on, "No") ? 0 : is_str(_game_screen_always_on, "Yes") ? 1
+                                                                                                             : 0;
                 variable_item_set_current_value_text(app->variable_item_game_screen_always_on, yes_or_no_choices[index]);
                 variable_item_set_current_value_index(app->variable_item_game_screen_always_on, index);
             }
             char _game_sound_on[8];
             if (load_char("Game-Sound-On", _game_sound_on, sizeof(_game_sound_on)))
             {
-                int index = is_str(_game_sound_on, "No") ? 0 : is_str(_game_sound_on, "Yes") ? 1 : 0;
+                int index = is_str(_game_sound_on, "No") ? 0 : is_str(_game_sound_on, "Yes") ? 1
+                                                                                             : 0;
                 variable_item_set_current_value_text(app->variable_item_game_sound_on, yes_or_no_choices[index]);
                 variable_item_set_current_value_index(app->variable_item_game_sound_on, index);
             }
             char _game_vibration_on[8];
             if (load_char("Game-Vibration-On", _game_vibration_on, sizeof(_game_vibration_on)))
             {
-                int index = is_str(_game_vibration_on, "No") ? 0 : is_str(_game_vibration_on, "Yes") ? 1 : 0;
+                int index = is_str(_game_vibration_on, "No") ? 0 : is_str(_game_vibration_on, "Yes") ? 1
+                                                                                                     : 0;
                 variable_item_set_current_value_text(app->variable_item_game_vibration_on, yes_or_no_choices[index]);
                 variable_item_set_current_value_index(app->variable_item_game_vibration_on, index);
             }
@@ -737,7 +738,8 @@ void free_all_views(void *context, bool should_free_variable_item_list, bool sho
         }
     }
 
-    if (should_free_submenu_settings) free_submenu_settings(app);
+    if (should_free_submenu_settings)
+        free_submenu_settings(app);
 }
 static bool fetch_world_list(FlipperHTTP *fhttp)
 {
@@ -760,7 +762,7 @@ static bool fetch_world_list(FlipperHTTP *fhttp)
     snprintf(fhttp->file_path, sizeof(fhttp->file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/world_list.json");
 
     fhttp->save_received_data = true;
-    return flipper_http_get_request_with_headers(fhttp, "https://www.flipsocial.net/api/world/v5/list/10/", "{\"Content-Type\":\"application/json\"}");
+    return flipper_http_request(fhttp, GET, "https://www.flipsocial.net/api/world/v5/list/10/", "{\"Content-Type\":\"application/json\"}", NULL);
 }
 // we will load the palyer stats from the API and save them
 // in player_spawn game method, it will load the player stats that we saved
@@ -795,7 +797,7 @@ static bool fetch_player_stats(FlipperHTTP *fhttp)
 
     snprintf(fhttp->file_path, sizeof(fhttp->file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/data/player/player_stats.json");
     fhttp->save_received_data = true;
-    return flipper_http_get_request_with_headers(fhttp, url, "{\"Content-Type\":\"application/json\"}");
+    return flipper_http_request(fhttp, GET, url, "{\"Content-Type\":\"application/json\"}", NULL);
 }
 
 // static bool fetch_app_update(FlipperHTTP *fhttp)
@@ -919,7 +921,7 @@ static bool _fetch_game(DataLoaderModel *model)
         }
         char payload[256];
         snprintf(payload, sizeof(payload), "{\"username\":\"%s\",\"password\":\"%s\"}", username, password);
-        return flipper_http_post_request_with_headers(model->fhttp, "https://www.flipsocial.net/api/user/login/", "{\"Content-Type\":\"application/json\"}", payload);
+        return flipper_http_request(model->fhttp, POST, "https://www.flipsocial.net/api/user/login/", "{\"Content-Type\":\"application/json\"}", payload);
     }
     else if (model->request_index == 1)
     {
@@ -957,7 +959,7 @@ static bool _fetch_game(DataLoaderModel *model)
             char payload[172];
             snprintf(payload, sizeof(payload), "{\"username\":\"%s\",\"password\":\"%s\"}", username, password);
             model->title = "Registering...";
-            return flipper_http_post_request_with_headers(model->fhttp, "https://www.flipsocial.net/api/user/register/", "{\"Content-Type\":\"application/json\"}", payload);
+            return flipper_http_request(model->fhttp, POST, "https://www.flipsocial.net/api/user/register/", "{\"Content-Type\":\"application/json\"}", payload);
         }
         else
         {
@@ -1015,7 +1017,7 @@ static bool _fetch_game(DataLoaderModel *model)
         snprintf(url, sizeof(url), "https://www.flipsocial.net/api/world/v5/get/world/%s/", furi_string_get_cstr(first_world));
         furi_string_free(world_list);
         furi_string_free(first_world);
-        return flipper_http_get_request_with_headers(model->fhttp, url, "{\"Content-Type\":\"application/json\"}");
+        return flipper_http_request(model->fhttp, GET, url, "{\"Content-Type\":\"application/json\"}", NULL);
     }
     FURI_LOG_E(TAG, "Unknown request index");
     return false;
@@ -1187,7 +1189,19 @@ void callback_submenu_choices(void *context, uint32_t index)
     }
     switch (index)
     {
-    case FlipWorldSubmenuIndexRun:
+    case FlipWorldSubmenuIndexGameSubmenu:
+        view_dispatcher_switch_to_view(app->view_dispatcher, FlipWorldViewGameSubmenu);
+        break;
+    case FlipWorldSubmenuIndexStory:
+        game_mode_index = 2; // GAME_MODE_STORY
+        easy_flipper_dialog("Unavailable", "\nStory mode is not ready yet.\nPress BACK to return.");
+        break;
+    case FlipWorldSubmenuIndexPvP:
+        game_mode_index = 1; // GAME_MODE_PVP
+        easy_flipper_dialog("Unavailable", "\nPvP mode is not ready yet.\nPress BACK to return.");
+        break;
+    case FlipWorldSubmenuIndexPvE:
+        game_mode_index = 0; // GAME_MODE_PVE
         free_all_views(app, true, true);
         if (!is_enough_heap(60000))
         {
@@ -1248,7 +1262,6 @@ void callback_submenu_choices(void *context, uint32_t index)
                 easy_flipper_dialog("Error", "Failed to start game thread. Press BACK to return.");
                 return;
             }
-            
         }
         else
         {
@@ -1615,7 +1628,7 @@ static bool _fetch_worlds(DataLoaderModel *model)
     furi_record_close(RECORD_STORAGE);
     snprintf(model->fhttp->file_path, sizeof(model->fhttp->file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/world_list_full.json");
     model->fhttp->save_received_data = true;
-    return flipper_http_get_request_with_headers(model->fhttp, "https://www.flipsocial.net/api/world/v5/get/10/", "{\"Content-Type\":\"application/json\"}");
+    return flipper_http_request(model->fhttp, GET, "https://www.flipsocial.net/api/world/v5/get/10/", "{\"Content-Type\":\"application/json\"}", NULL);
 }
 static char *_parse_worlds(DataLoaderModel *model)
 {
@@ -1797,7 +1810,7 @@ void loader_draw_callback(Canvas *canvas, void *model)
     }
 
     DataLoaderModel *data_loader_model = (DataLoaderModel *)model;
-    SerialState http_state = data_loader_model->fhttp->state;
+    HTTPState http_state = data_loader_model->fhttp->state;
     DataState data_state = data_loader_model->data_state;
     char *title = data_loader_model->title;
 

+ 1 - 0
flip_world.c

@@ -10,6 +10,7 @@ int player_sprite_index = 1;
 char *vgm_levels[] = {"-2", "-1", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"};
 int vgm_x_index = 2;
 int vgm_y_index = 2;
+int game_mode_index = 0;
 float atof_(const char *nptr) { return (float)strtod(nptr, NULL); }
 float atof_furi(const FuriString *nptr) { return atof_(furi_string_get_cstr(nptr)); }
 bool is_str(const char *src, const char *dst) { return strcmp(src, dst) == 0; }

+ 8 - 2
flip_world.h

@@ -15,13 +15,16 @@
 //
 
 #define TAG "FlipWorld"
-#define VERSION 0.5
+#define VERSION 0.6
 #define VERSION_TAG TAG " " FAP_VERSION
 
 // Define the submenu items for our FlipWorld application
 typedef enum
 {
-    FlipWorldSubmenuIndexRun, // Click to run the FlipWorld application
+    FlipWorldSubmenuIndexPvE,
+    FlipWorldSubmenuIndexStory,
+    FlipWorldSubmenuIndexPvP,
+    FlipWorldSubmenuIndexGameSubmenu,
     FlipWorldSubmenuIndexMessage,
     FlipWorldSubmenuIndexSettings,
     FlipWorldSubmenuIndexWiFiSettings,
@@ -33,6 +36,7 @@ typedef enum
 typedef enum
 {
     FlipWorldViewSubmenu,          // The submenu
+    FlipWorldViewGameSubmenu,      // The game submenu
     FlipWorldViewMessage,          // The about, loading screen
     FlipWorldViewSettings,         // The settings screen
     FlipWorldViewVariableItemList, // The variable item list screen
@@ -58,6 +62,7 @@ typedef struct
     ViewDispatcher *view_dispatcher;       // Switches between our views
     View *view_message;                    // The about, loading screen
     Submenu *submenu;                      // The submenu
+    Submenu *submenu_game;                 // The game submenu
     Submenu *submenu_settings;             // The settings submenu
     VariableItemList *variable_item_list;  // The variable item list (settngs)
     VariableItem *variable_item_wifi_ssid; // The variable item for WiFi SSID
@@ -92,6 +97,7 @@ extern int player_sprite_index;
 extern char *vgm_levels[];
 extern int vgm_x_index;
 extern int vgm_y_index;
+extern int game_mode_index;
 float atof_(const char *nptr);
 float atof_furi(const FuriString *nptr);
 bool is_str(const char *src, const char *dst);

Разница между файлами не показана из-за своего большого размера
+ 539 - 787
flipper_http/flipper_http.c


+ 108 - 273
flipper_http/flipper_http.h

@@ -22,8 +22,6 @@
 #define FURI_LOG_I(tag, msg, ...)
 //
 
-// STORAGE_EXT_PATH_PREFIX is defined in the Furi SDK as /ext
-
 #define HTTP_TAG "FlipWorld"              // change this to your app name
 #define http_tag "flip_world"             // change this to your app id
 #define UART_CH (FuriHalSerialIdUsart)    // UART channel
@@ -45,7 +43,7 @@ typedef enum
     RECEIVING, // Receiving data
     SENDING,   // Sending data
     ISSUE,     // Issue with connection
-} SerialState;
+} HTTPState;
 
 // Event Flags for UART Worker Thread
 typedef enum
@@ -54,97 +52,58 @@ typedef enum
     WorkerEvtRxDone = (1 << 1),
 } WorkerEvtFlags;
 
-// FlipperHTTP Structure
-typedef struct
+typedef enum
 {
-    FuriStreamBuffer *flipper_http_stream;  // Stream buffer for UART communication
-    FuriHalSerialHandle *serial_handle;     // Serial handle for UART communication
-    FuriThread *rx_thread;                  // Worker thread for UART
-    FuriThreadId rx_thread_id;              // Worker thread ID
-    FlipperHTTP_Callback handle_rx_line_cb; // Callback for received lines
-    void *callback_context;                 // Context for the callback
-    SerialState state;                      // State of the UART
-
-    // variable to store the last received data from the UART
-    char *last_response;
-    char file_path[256]; // Path to save the received data
-
-    // Timer-related members
-    FuriTimer *get_timeout_timer; // Timer for HTTP request timeout
-
-    bool started_receiving_get; // Indicates if a GET request has started
-    bool just_started_get;      // Indicates if GET data reception has just started
-
-    bool started_receiving_post; // Indicates if a POST request has started
-    bool just_started_post;      // Indicates if POST data reception has just started
-
-    bool started_receiving_put; // Indicates if a PUT request has started
-    bool just_started_put;      // Indicates if PUT data reception has just started
-
-    bool started_receiving_delete; // Indicates if a DELETE request has started
-    bool just_started_delete;      // Indicates if DELETE data reception has just started
+    GET,    // GET request
+    POST,   // POST request
+    PUT,    // PUT request
+    DELETE, // DELETE request
+    //
+    BYTES,      // Stream bytes to file
+    BYTES_POST, // Stream bytes to file after a POST request
+} HTTPMethod;
 
-    // Buffer to hold the raw bytes received from the UART
-    uint8_t *received_bytes;
-    size_t received_bytes_len; // Length of the received bytes
-    bool is_bytes_request;     // Flag to indicate if the request is for bytes
-    bool save_bytes;           // Flag to save the received data to a file
-    bool save_received_data;   // Flag to save the received data to a file
-
-    bool just_started_bytes; // Indicates if bytes data reception has just started
+typedef enum
+{
+    HTTP_CMD_WIFI_CONNECT,
+    HTTP_CMD_WIFI_DISCONNECT,
+    HTTP_CMD_IP_ADDRESS,
+    HTTP_CMD_IP_WIFI,
+    HTTP_CMD_SCAN,
+    HTTP_CMD_LIST_COMMANDS,
+    HTTP_CMD_LED_ON,
+    HTTP_CMD_LED_OFF,
+    HTTP_CMD_PING
+} HTTPCommand; // list of non-input commands
 
-    char rx_line_buffer[RX_LINE_BUFFER_SIZE];
-    uint8_t file_buffer[FILE_BUFFER_SIZE];
-    size_t file_buffer_len;
+// FlipperHTTP Structure
+typedef struct
+{
+    FuriStreamBuffer *flipper_http_stream;    // Stream buffer for UART communication
+    FuriHalSerialHandle *serial_handle;       // Serial handle for UART communication
+    FuriThread *rx_thread;                    // Worker thread for UART
+    FuriThreadId rx_thread_id;                // Worker thread ID
+    FlipperHTTP_Callback handle_rx_line_cb;   // Callback for received lines
+    void *callback_context;                   // Context for the callback
+    HTTPState state;                          // State of the UART
+    HTTPMethod method;                        // HTTP method
+    char *last_response;                      // variable to store the last received data from the UART
+    char file_path[256];                      // Path to save the received data
+    FuriTimer *get_timeout_timer;             // Timer for HTTP request timeout
+    bool started_receiving;                   // Indicates if a request has started
+    bool just_started;                        // Indicates if data reception has just started
+    bool is_bytes_request;                    // Flag to indicate if the request is for bytes
+    bool save_bytes;                          // Flag to save the received data to a file
+    bool save_received_data;                  // Flag to save the received data to a file
+    bool just_started_bytes;                  // Indicates if bytes data reception has just started
+    size_t bytes_received;                    // Number of bytes received
+    char rx_line_buffer[RX_LINE_BUFFER_SIZE]; // Buffer for received lines
+    uint8_t file_buffer[FILE_BUFFER_SIZE];    // Buffer for file data
+    size_t file_buffer_len;                   // Length of the file buffer
+    size_t content_length;                    // Length of the content received
+    int status_code;                          // HTTP status code
 } FlipperHTTP;
 
-// fhttp.last_response holds the last received data from the UART
-
-// Function to append received data to file
-// make sure to initialize the file path before calling this function
-bool flipper_http_append_to_file(
-    const void *data,
-    size_t data_size,
-    bool start_new_file,
-    char *file_path);
-
-FuriString *flipper_http_load_from_file(char *file_path);
-FuriString *flipper_http_load_from_file_with_limit(char *file_path, size_t limit);
-
-// UART worker thread
-/**
- * @brief      Worker thread to handle UART data asynchronously.
- * @return     0
- * @param      context   The context to pass to the callback.
- * @note       This function will handle received data asynchronously via the callback.
- */
-// UART worker thread
-int32_t flipper_http_worker(void *context);
-
-// Timer callback function
-/**
- * @brief      Callback function for the GET timeout timer.
- * @return     0
- * @param      context   The context to pass to the callback.
- * @note       This function will be called when the GET request times out.
- */
-void get_timeout_timer_callback(void *context);
-
-// UART RX Handler Callback (Interrupt Context)
-/**
- * @brief      A private callback function to handle received data asynchronously.
- * @return     void
- * @param      handle    The UART handle.
- * @param      event     The event type.
- * @param      context   The context to pass to the callback.
- * @note       This function will handle received data asynchronously via the callback.
- */
-void _flipper_http_rx_callback(
-    FuriHalSerialHandle *handle,
-    FuriHalSerialRxEvent event,
-    void *context);
-
-// UART initialization function
 /**
  * @brief      Initialize UART.
  * @return     FlipperHTTP context if the UART was initialized successfully, NULL otherwise.
@@ -152,7 +111,6 @@ void _flipper_http_rx_callback(
  */
 FlipperHTTP *flipper_http_alloc();
 
-// Deinitialize UART
 /**
  * @brief      Deinitialize UART.
  * @return     void
@@ -161,55 +119,49 @@ FlipperHTTP *flipper_http_alloc();
  */
 void flipper_http_free(FlipperHTTP *fhttp);
 
-// Function to send data over UART with newline termination
-/**
- * @brief      Send data over UART with newline termination.
- * @return     true if the data was sent successfully, false otherwise.
- * @param fhttp The FlipperHTTP context
- * @param      data  The data to send over UART.
- * @note       The data will be sent over UART with a newline character appended.
- */
-bool flipper_http_send_data(FlipperHTTP *fhttp, const char *data);
-
-// Function to send a PING request
 /**
- * @brief      Send a PING request to check if the Wifi Dev Board is connected.
- * @return     true if the request was successful, false otherwise.
- * @param fhttp The FlipperHTTP context
- * @note       The received data will be handled asynchronously via the callback.
- * @note       This is best used to check if the Wifi Dev Board is connected.
- * @note       The state will remain INACTIVE until a PONG is received.
+ * @brief      Append received data to a file.
+ * @return     true if the data was appended successfully, false otherwise.
+ * @param      data        The data to append to the file.
+ * @param      data_size   The size of the data to append.
+ * @param      start_new_file  Flag to indicate if a new file should be created.
+ * @param      file_path   The path to the file.
+ * @note       Make sure to initialize the file path before calling this function.
  */
-bool flipper_http_ping(FlipperHTTP *fhttp);
+bool flipper_http_append_to_file(const void *data, size_t data_size, bool start_new_file, char *file_path);
 
-// Function to list available commands
 /**
- * @brief      Send a command to list available commands.
- * @return     true if the request was successful, false otherwise.
- * @param fhttp The FlipperHTTP context
- * @note       The received data will be handled asynchronously via the callback.
+ * @brief      Load data from a file.
+ * @return     The loaded data as a FuriString.
+ * @param      file_path The path to the file to load.
  */
-bool flipper_http_list_commands(FlipperHTTP *fhttp);
+FuriString *flipper_http_load_from_file(char *file_path);
 
-// Function to turn on the LED
 /**
- * @brief      Allow the LED to display while processing.
- * @return     true if the request was successful, false otherwise.
- * @param fhttp The FlipperHTTP context
- * @note       The received data will be handled asynchronously via the callback.
+ * @brief      Load data from a file with a size limit.
+ * @return     The loaded data as a FuriString.
+ * @param      file_path The path to the file to load.
+ * @param      limit     The size limit for loading data.
  */
-bool flipper_http_led_on(FlipperHTTP *fhttp);
+FuriString *flipper_http_load_from_file_with_limit(char *file_path, size_t limit);
 
-// Function to turn off the LED
 /**
- * @brief      Disable the LED from displaying while processing.
- * @return     true if the request was successful, false otherwise.
+ * @brief Perform a task while displaying a loading screen
  * @param fhttp The FlipperHTTP context
- * @note       The received data will be handled asynchronously via the callback.
+ * @param http_request The function to send the request
+ * @param parse_response The function to parse the response
+ * @param success_view_id The view ID to switch to on success
+ * @param failure_view_id The view ID to switch to on failure
+ * @param view_dispatcher The view dispatcher to use
+ * @return
  */
-bool flipper_http_led_off(FlipperHTTP *fhttp);
+void flipper_http_loading_task(FlipperHTTP *fhttp,
+                               bool (*http_request)(void),
+                               bool (*parse_response)(void),
+                               uint32_t success_view_id,
+                               uint32_t failure_view_id,
+                               ViewDispatcher **view_dispatcher);
 
-// Function to parse JSON data
 /**
  * @brief      Parse JSON data.
  * @return     true if the JSON data was parsed successfully, false otherwise.
@@ -220,7 +172,6 @@ bool flipper_http_led_off(FlipperHTTP *fhttp);
  */
 bool flipper_http_parse_json(FlipperHTTP *fhttp, const char *key, const char *json_data);
 
-// Function to parse JSON array data
 /**
  * @brief      Parse JSON array data.
  * @return     true if the JSON array data was parsed successfully, false otherwise.
@@ -232,184 +183,68 @@ bool flipper_http_parse_json(FlipperHTTP *fhttp, const char *key, const char *js
  */
 bool flipper_http_parse_json_array(FlipperHTTP *fhttp, const char *key, int index, const char *json_data);
 
-// Function to scan for WiFi networks
-/**
- * @brief      Send a command to scan for WiFi networks.
- * @return     true if the request was successful, false otherwise.
- * @param fhttp The FlipperHTTP context
- * @note       The received data will be handled asynchronously via the callback.
- */
-bool flipper_http_scan_wifi(FlipperHTTP *fhttp);
-
-// Function to save WiFi settings (returns true if successful)
-/**
- * @brief      Send a command to save WiFi settings.
- * @return     true if the request was successful, false otherwise.
- * @param fhttp The FlipperHTTP context
- * @note       The received data will be handled asynchronously via the callback.
- */
-bool flipper_http_save_wifi(FlipperHTTP *fhttp, const char *ssid, const char *password);
-
-// Function to get IP address of WiFi Devboard
-/**
- * @brief      Send a command to get the IP address of the WiFi Devboard
- * @return     true if the request was successful, false otherwise.
- * @param fhttp The FlipperHTTP context
- * @note       The received data will be handled asynchronously via the callback.
- */
-bool flipper_http_ip_address(FlipperHTTP *fhttp);
-
-// Function to get IP address of the connected WiFi network
-/**
- * @brief      Send a command to get the IP address of the connected WiFi network.
- * @return     true if the request was successful, false otherwise.
- * @param fhttp The FlipperHTTP context
- * @note       The received data will be handled asynchronously via the callback.
- */
-bool flipper_http_ip_wifi(FlipperHTTP *fhttp);
-
-// Function to disconnect from WiFi (returns true if successful)
-/**
- * @brief      Send a command to disconnect from WiFi.
- * @return     true if the request was successful, false otherwise.
- * @param fhttp The FlipperHTTP context
- * @note       The received data will be handled asynchronously via the callback.
- */
-bool flipper_http_disconnect_wifi(FlipperHTTP *fhttp);
-
-// Function to connect to WiFi (returns true if successful)
-/**
- * @brief      Send a command to connect to WiFi.
- * @return     true if the request was successful, false otherwise.
- * @param fhttp The FlipperHTTP context
- * @note       The received data will be handled asynchronously via the callback.
- */
-bool flipper_http_connect_wifi(FlipperHTTP *fhttp);
-
-// Function to send a GET request
 /**
- * @brief      Send a GET request to the specified URL.
- * @return     true if the request was successful, false otherwise.
+ * @brief Process requests and parse JSON data asynchronously
  * @param fhttp The FlipperHTTP context
- * @param      url  The URL to send the GET request to.
- * @note       The received data will be handled asynchronously via the callback.
+ * @param http_request The function to send the request
+ * @param parse_json The function to parse the JSON
+ * @return true if successful, false otherwise
  */
-bool flipper_http_get_request(FlipperHTTP *fhttp, const char *url);
+bool flipper_http_process_response_async(FlipperHTTP *fhttp, bool (*http_request)(void), bool (*parse_json)(void));
 
-// Function to send a GET request with headers
 /**
- * @brief      Send a GET request to the specified URL.
+ * @brief      Send a request to the specified URL.
  * @return     true if the request was successful, false otherwise.
- * @param fhttp The FlipperHTTP context
- * @param      url  The URL to send the GET request to.
- * @param      headers  The headers to send with the GET request.
+ * @param      fhttp The FlipperHTTP context
+ * @param      method The HTTP method to use.
+ * @param      url  The URL to send the request to.
+ * @param      headers  The headers to send with the request.
+ * @param      payload  The data to send with the request.
  * @note       The received data will be handled asynchronously via the callback.
  */
-bool flipper_http_get_request_with_headers(FlipperHTTP *fhttp, const char *url, const char *headers);
+bool flipper_http_request(FlipperHTTP *fhttp, HTTPMethod method, const char *url, const char *headers, const char *payload);
 
-// Function to send a GET request with headers and return bytes
 /**
- * @brief      Send a GET request to the specified URL.
+ * @brief      Send a command to save WiFi settings.
  * @return     true if the request was successful, false otherwise.
  * @param fhttp The FlipperHTTP context
- * @param      url  The URL to send the GET request to.
- * @param      headers  The headers to send with the GET request.
  * @note       The received data will be handled asynchronously via the callback.
  */
-bool flipper_http_get_request_bytes(FlipperHTTP *fhttp, const char *url, const char *headers);
+bool flipper_http_save_wifi(FlipperHTTP *fhttp, const char *ssid, const char *password);
 
-// Function to send a POST request with headers
 /**
- * @brief      Send a POST request to the specified URL.
+ * @brief      Send a command.
  * @return     true if the request was successful, false otherwise.
- * @param fhttp The FlipperHTTP context
- * @param      url  The URL to send the POST request to.
- * @param      headers  The headers to send with the POST request.
- * @param      data  The data to send with the POST request.
+ * @param      fhttp The FlipperHTTP context
+ * @param      command The command to send.
  * @note       The received data will be handled asynchronously via the callback.
  */
-bool flipper_http_post_request_with_headers(
-    FlipperHTTP *fhttp,
-    const char *url,
-    const char *headers,
-    const char *payload);
+bool flipper_http_send_command(FlipperHTTP *fhttp, HTTPCommand command);
 
-// Function to send a POST request with headers and return bytes
 /**
- * @brief      Send a POST request to the specified URL.
- * @return     true if the request was successful, false otherwise.
+ * @brief      Send data over UART with newline termination.
+ * @return     true if the data was sent successfully, false otherwise.
  * @param fhttp The FlipperHTTP context
- * @param      url  The URL to send the POST request to.
- * @param      headers  The headers to send with the POST request.
- * @param      payload  The data to send with the POST request.
- * @note       The received data will be handled asynchronously via the callback.
+ * @param      data  The data to send over UART.
+ * @note       The data will be sent over UART with a newline character appended.
  */
-bool flipper_http_post_request_bytes(FlipperHTTP *fhttp, const char *url, const char *headers, const char *payload);
+bool flipper_http_send_data(FlipperHTTP *fhttp, const char *data);
 
-// Function to send a PUT request with headers
 /**
- * @brief      Send a PUT request to the specified URL.
+ * @brief      Send a request to the specified URL to start a WebSocket connection.
  * @return     true if the request was successful, false otherwise.
  * @param fhttp The FlipperHTTP context
- * @param      url  The URL to send the PUT request to.
- * @param      headers  The headers to send with the PUT request.
- * @param      data  The data to send with the PUT request.
+ * @param      url  The URL to send the WebSocket request to.
+ * @param port The port to connect to
+ * @param headers The headers to send with the WebSocket request
  * @note       The received data will be handled asynchronously via the callback.
  */
-bool flipper_http_put_request_with_headers(
-    FlipperHTTP *fhttp,
-    const char *url,
-    const char *headers,
-    const char *payload);
+bool flipper_http_websocket_start(FlipperHTTP *fhttp, const char *url, uint16_t port, const char *headers);
 
-// Function to send a DELETE request with headers
 /**
- * @brief      Send a DELETE request to the specified URL.
+ * @brief      Send a request to stop the WebSocket connection.
  * @return     true if the request was successful, false otherwise.
  * @param fhttp The FlipperHTTP context
- * @param      url  The URL to send the DELETE request to.
- * @param      headers  The headers to send with the DELETE request.
- * @param      data  The data to send with the DELETE request.
  * @note       The received data will be handled asynchronously via the callback.
  */
-bool flipper_http_delete_request_with_headers(
-    FlipperHTTP *fhttp,
-    const char *url,
-    const char *headers,
-    const char *payload);
-
-// Function to handle received data asynchronously
-/**
- * @brief      Callback function to handle received data asynchronously.
- * @return     void
- * @param      line     The received line.
- * @param      context  The FlipperHTTP context.
- * @note       The received data will be handled asynchronously via the callback and handles the state of the UART.
- */
-void flipper_http_rx_callback(const char *line, void *context);
-
-/**
- * @brief Process requests and parse JSON data asynchronously
- * @param fhttp The FlipperHTTP context
- * @param http_request The function to send the request
- * @param parse_json The function to parse the JSON
- * @return true if successful, false otherwise
- */
-bool flipper_http_process_response_async(FlipperHTTP *fhttp, bool (*http_request)(void), bool (*parse_json)(void));
-
-/**
- * @brief Perform a task while displaying a loading screen
- * @param fhttp The FlipperHTTP context
- * @param http_request The function to send the request
- * @param parse_response The function to parse the response
- * @param success_view_id The view ID to switch to on success
- * @param failure_view_id The view ID to switch to on failure
- * @param view_dispatcher The view dispatcher to use
- * @return
- */
-void flipper_http_loading_task(FlipperHTTP *fhttp,
-                               bool (*http_request)(void),
-                               bool (*parse_response)(void),
-                               uint32_t success_view_id,
-                               uint32_t failure_view_id,
-                               ViewDispatcher **view_dispatcher);
+bool flipper_http_websocket_stop(FlipperHTTP *fhttp);

+ 5 - 12
game/draw.c

@@ -169,25 +169,18 @@ void background_render(Canvas *canvas, GameManager *manager)
     GameContext *game_context = game_manager_game_context_get(manager);
     if (!game_context->is_menu_open)
     {
-
         // get player position
         Vector posi = entity_pos_get(game_context->player);
 
         // draw username over player's head
         draw_username(canvas, posi, game_context->player_context->username);
 
-        // draw switch world icon
         if (game_context->is_switching_level)
-        {
-            canvas_draw_icon(
-                canvas,
-                0,
-                0,
-                &I_icon_world_change_128x64px);
-        }
-
-        // Draw user stats
-        draw_user_stats(canvas, (Vector){0, 50}, manager);
+            // draw switch world icon
+            canvas_draw_icon(canvas, 0, 0, &I_icon_world_change_128x64px);
+        else
+            // Draw user stats
+            draw_user_stats(canvas, (Vector){0, 50}, manager);
     }
     else
     {

+ 11 - 11
game/enemy.c

@@ -263,7 +263,6 @@ static void enemy_collision(Entity *self, Entity *other, GameManager *manager, v
 
                 if (enemy_context->health <= 0)
                 {
-                    FURI_LOG_I("Game", "Enemy '%s' is dead.. resetting enemy position and health", enemy_context->id);
                     enemy_context->state = ENTITY_DEAD;
 
                     // Reset enemy position and health
@@ -278,12 +277,11 @@ static void enemy_collision(Entity *self, Entity *other, GameManager *manager, v
                 }
                 else
                 {
-                    FURI_LOG_I("Game", "Enemy '%s' took %f damage from player", enemy_context->id, (double)game_context->player_context->strength);
                     enemy_context->state = ENTITY_ATTACKED;
                     // Vector old_pos = entity_pos_get(self);
                     //  Bounce the enemy back by X units opposite their last movement direction
                     enemy_pos.x -= game_context->player_context->dx * enemy_context->radius + game_context->icon_offset;
-                    enemy_pos.y -= game_context->player_context->dy * enemy_context->radius + game_context->icon_offset;
+                    // enemy_pos.y -= game_context->player_context->dy * enemy_context->radius + game_context->icon_offset;
                     entity_pos_set(self, enemy_pos);
 
                     // Reset enemy's movement direction to prevent immediate re-collision
@@ -332,7 +330,7 @@ static void enemy_collision(Entity *self, Entity *other, GameManager *manager, v
 
                     // Bounce the player back by X units opposite their last movement direction
                     player_pos.x -= game_context->player_context->dx * enemy_context->radius + game_context->icon_offset;
-                    player_pos.y -= game_context->player_context->dy * enemy_context->radius + game_context->icon_offset;
+                    // player_pos.y -= game_context->player_context->dy * enemy_context->radius + game_context->icon_offset;
                     entity_pos_set(other, player_pos);
 
                     // Reset player's movement direction to prevent immediate re-collision
@@ -350,10 +348,6 @@ static void enemy_collision(Entity *self, Entity *other, GameManager *manager, v
             game_context->player_context->dy = 0;
         }
 
-        // Reset enemy's state
-        enemy_context->state = ENTITY_IDLE;
-        enemy_context->elapsed_move_timer = 0.0f;
-
         if (game_context->player_context->state == ENTITY_DEAD)
         {
             // Reset player's position and health
@@ -437,12 +431,18 @@ static void enemy_update(Entity *self, GameManager *manager, void *context)
 
     case ENTITY_MOVING_TO_END:
     case ENTITY_MOVING_TO_START:
+    case ENTITY_ATTACKED:
     {
-        // Determine the target position based on the current state
-        Vector target_position = (enemy_context->state == ENTITY_MOVING_TO_END) ? enemy_context->end_position : enemy_context->start_position;
-
         // Get current position
         Vector current_pos = entity_pos_get(self);
+        if (enemy_context->state == ENTITY_ATTACKED)
+        {
+            // set direction again
+            enemy_context->state = enemy_context->direction == ENTITY_LEFT ? ENTITY_MOVING_TO_START : ENTITY_MOVING_TO_END;
+        }
+
+        // Determine the target position based on the current state
+        Vector target_position = (enemy_context->state == ENTITY_MOVING_TO_END) ? enemy_context->end_position : enemy_context->start_position;
         Vector direction_vector = {0, 0};
 
         // Calculate direction towards the target

+ 26 - 10
game/game.c

@@ -21,6 +21,8 @@ static void game_start(GameManager *game_manager, void *ctx)
     game_context->enemy_count = 0;
     game_context->npc_count = 0;
 
+    game_context->game_mode = game_mode_index;
+
     // set all levels to NULL
     for (int i = 0; i < MAX_LEVELS; i++)
         game_context->levels[i] = NULL;
@@ -82,16 +84,23 @@ static void game_stop(void *ctx)
         level_clear(game_context->levels[game_context->current_level]);
     }
 
-    if (game_context->player_context)
+    PlayerContext *player_context = malloc(sizeof(PlayerContext));
+    if (!player_context)
     {
-        if (!game_context->ended_early)
-            easy_flipper_dialog(
-                "Game Over",
-                "Thanks for playing FlipWorld!\nHit BACK then wait for\nthe game to save.");
-        else
-            easy_flipper_dialog(
-                "Game Over", "Ran out of memory so the\ngame ended early.\nHit BACK to exit.");
+        FURI_LOG_E("Game", "Failed to allocate PlayerContext");
+        return;
+    }
+
+    if (!game_context->ended_early)
+        easy_flipper_dialog(
+            "Game Over",
+            "Thanks for playing FlipWorld!\nHit BACK then wait for\nthe game to save.");
+    else
+        easy_flipper_dialog(
+            "Game Over", "Ran out of memory so the\ngame ended early.\nHit BACK to exit.");
 
+    if (load_player_context(player_context))
+    {
         ViewPort *view_port = view_port_alloc();
         view_port_draw_callback_set(view_port, thanks, NULL);
         Gui *gui = furi_record_open(RECORD_GUI);
@@ -99,9 +108,9 @@ static void game_stop(void *ctx)
         uint32_t tick_count = furi_get_tick();
         furi_delay_ms(800);
 
-        save_player_context_api(game_context->player_context);
+        save_player_context_api(player_context);
 
-        const uint32_t delay = 2500;
+        const uint32_t delay = 3500;
         tick_count = (tick_count + delay) - furi_get_tick();
         if (tick_count <= delay)
         {
@@ -115,6 +124,13 @@ static void game_stop(void *ctx)
         gui_remove_view_port(gui, view_port);
         furi_record_close(RECORD_GUI);
     }
+
+    // free the player context
+    if (player_context)
+    {
+        free(player_context);
+        player_context = NULL;
+    }
 }
 
 /*

+ 22 - 15
game/player.c

@@ -43,6 +43,21 @@ static Level *next_level(GameManager *manager)
     return NULL;
 }
 
+// Update player stats based on XP using iterative method
+static int get_player_level_iterative(uint32_t xp)
+{
+    int level = 1;
+    uint32_t xp_required = 100; // Base XP for level 2
+
+    while (level < 100 && xp >= xp_required) // Maximum level supported
+    {
+        level++;
+        xp_required = (uint32_t)(xp_required * 1.5); // 1.5 growth factor per level
+    }
+
+    return level;
+}
+
 void player_spawn(Level *level, GameManager *manager)
 {
     if (!level || !manager)
@@ -135,21 +150,6 @@ void player_spawn(Level *level, GameManager *manager)
 
     pctx->start_position = entity_pos_get(game_context->player);
 
-    // Update player stats based on XP using iterative method
-    int get_player_level_iterative(uint32_t xp)
-    {
-        int level = 1;
-        uint32_t xp_required = 100; // Base XP for level 2
-
-        while (level < 100 && xp >= xp_required) // Maximum level supported
-        {
-            level++;
-            xp_required = (uint32_t)(xp_required * 1.5); // 1.5 growth factor per level
-        }
-
-        return level;
-    }
-
     // Determine the player's level based on XP
     pctx->level = get_player_level_iterative(pctx->xp);
 
@@ -214,6 +214,11 @@ static void player_update(Entity *self, GameManager *manager, void *context)
     player->old_position = pos;
     GameContext *game_context = game_manager_game_context_get(manager);
 
+    // Determine the player's level based on XP
+    player->level = get_player_level_iterative(player->xp);
+    player->strength = 10 + (player->level * 1);           // 1 strength per level
+    player->max_health = 100 + ((player->level - 1) * 10); // 10 health per level
+
     // Store previous direction
     int prev_dx = player->dx;
     int prev_dy = player->dy;
@@ -377,6 +382,8 @@ static void player_update(Entity *self, GameManager *manager, void *context)
         {
             if (!game_context->is_menu_open)
             {
+                save_player_context(player);
+                furi_delay_ms(100);
                 game_manager_game_stop(manager);
                 return;
             }

+ 11 - 0
game/player.h

@@ -62,6 +62,14 @@ typedef enum
     GAME_MENU_NPC,  // NPC dialog
 } GameMenuScreen;
 
+// game modes
+typedef enum
+{
+    GAME_MODE_PVE = 0,   // player(s) vs everyone
+    GAME_MODE_PVP = 1,   // player vs player
+    GAME_MODE_STORY = 2, // story mode
+} GameMode;
+
 typedef struct
 {
     PlayerContext *player_context;
@@ -69,6 +77,7 @@ typedef struct
     Entity *enemies[MAX_ENEMIES];
     Entity *npcs[MAX_NPCS];
     Entity *player;
+    //
     float fps;
     int level_count;
     int enemy_count;
@@ -87,6 +96,8 @@ typedef struct
     GameMenuScreen menu_screen;
     uint8_t menu_selection;
     //
+    GameMode game_mode;
+    //
     int icon_count;
     int icon_offset;
     //

+ 1 - 3
game/storage.c

@@ -358,8 +358,6 @@ bool save_player_context_api(PlayerContext *player_context)
     // closing brace
     furi_string_cat_str(json, "}");
 
-    // save the json to API
-
     // create new JSON with username key (of just username), and game_stats key (of the all of the data)
     FuriString *json_data = furi_string_alloc();
     if (!json_data)
@@ -378,7 +376,7 @@ bool save_player_context_api(PlayerContext *player_context)
     furi_string_free(json);
 
     // save the json_data to the API
-    if (!flipper_http_post_request_with_headers(fhttp, "https://www.flipsocial.net/api/user/update-game-stats/", "{\"Content-Type\":\"application/json\"}", furi_string_get_cstr(json_data)))
+    if (!flipper_http_request(fhttp, POST, "https://www.flipsocial.net/api/user/update-game-stats/", "{\"Content-Type\": \"application/json\"}", furi_string_get_cstr(json_data)))
     {
         FURI_LOG_E(TAG, "Failed to save player context to API");
         furi_string_free(json_data);

+ 1 - 1
game/world.c

@@ -149,7 +149,7 @@ FuriString *fetch_world(const char *name)
     snprintf(url, sizeof(url), "https://www.flipsocial.net/api/world/v5/get/world/%s/", name);
     snprintf(fhttp->file_path, sizeof(fhttp->file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/%s.json", name);
     fhttp->save_received_data = true;
-    if (!flipper_http_get_request_with_headers(fhttp, url, "{\"Content-Type\": \"application/json\"}"))
+    if (!flipper_http_request(fhttp, GET, url, "{\"Content-Type\": \"application/json\"}", NULL))
     {
         FURI_LOG_E("Game", "Failed to send HTTP request");
         flipper_http_free(fhttp);

Некоторые файлы не были показаны из-за большого количества измененных файлов