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

add enemy context using JSON + new parsing

jblanked 1 год назад
Родитель
Сommit
e6f9a9330e
9 измененных файлов с 300 добавлено и 101 удалено
  1. 0 2
      callback/callback.c
  2. 0 4
      flipper_http/flipper_http.c
  3. 1 1
      flipper_http/flipper_http.h
  4. 20 20
      game/enemy.c
  5. 0 27
      game/game.c
  6. 58 28
      game/level.c
  7. 204 0
      game/storage.c
  8. 3 0
      game/storage.h
  9. 14 19
      game/world.c

+ 0 - 2
callback/callback.c

@@ -644,7 +644,6 @@ static bool flip_world_fetch_world_list(DataLoaderModel *model)
         {
             furi_string_free(world_list);
             furi_string_free(first_world);
-            FURI_LOG_I(TAG, "World already exists");
             flipper_http_deinit();
             // free game thread
             if (game_thread_running)
@@ -867,7 +866,6 @@ static bool flip_world_fetch_game(DataLoaderModel *model)
         {
             furi_string_free(world_list);
             furi_string_free(first_world);
-            FURI_LOG_I(TAG, "World already exists");
 
             // view_dispatcher_switch_to_view(app_instance->view_dispatcher, FlipWorldViewSubmenu);
             flipper_http_deinit();

+ 0 - 4
flipper_http/flipper_http.c

@@ -137,10 +137,6 @@ FuriString *flipper_http_load_from_file(char *file_path)
         furi_string_push_back(str_result, buffer[i]);
     }
 
-    // Check if there is more data beyond the maximum size
-    char extra_byte;
-    storage_file_read(file, &extra_byte, 1);
-
     // Clean up
     storage_file_close(file);
     storage_file_free(file);

+ 1 - 1
flipper_http/flipper_http.h

@@ -24,7 +24,7 @@
 #define BAUDRATE (115200)                 // UART baudrate
 #define RX_BUF_SIZE 2048                  // UART RX buffer size
 #define RX_LINE_BUFFER_SIZE 4096          // UART RX line buffer size (increase for large responses)
-#define MAX_FILE_SHOW 3000                // Maximum data from file to show
+#define MAX_FILE_SHOW 4096                // Maximum data from file to show
 #define FILE_BUFFER_SIZE 512              // File buffer size
 
 // Forward declaration for callback

+ 20 - 20
game/enemy.c

@@ -583,8 +583,8 @@ void spawn_enemy_json(Level *level, GameManager *manager, char *json)
     char *_index = get_json_value("index", json);
     //
     char *size = get_json_value("size", json);
-    char *size_x = get_json_value("x", size);
-    char *size_y = get_json_value("y", size);
+    char *size_x = get_json_value("width", size);
+    char *size_y = get_json_value("height", size);
     //
     char *start_position = get_json_value("start_position", json);
     char *start_position_x = get_json_value("x", start_position);
@@ -614,14 +614,14 @@ void spawn_enemy_json(Level *level, GameManager *manager, char *json)
                                                                                        manager,
                                                                                        id,
                                                                                        atoi(_index),
-                                                                                       (Vector){atof(size_x), atof(size_y)},
-                                                                                       (Vector){atof(start_position_x), atof(start_position_y)},
-                                                                                       (Vector){atof(end_position_x), atof(end_position_y)},
-                                                                                       atof(move_timer),
-                                                                                       atof(speed),
-                                                                                       atof(attack_timer),
-                                                                                       atof(strength),
-                                                                                       atof(health)));
+                                                                                       (Vector){strtod(size_x, NULL), strtod(size_y, NULL)},
+                                                                                       (Vector){strtod(start_position_x, NULL), strtod(start_position_y, NULL)},
+                                                                                       (Vector){strtod(end_position_x, NULL), strtod(end_position_y, NULL)},
+                                                                                       strtod(move_timer, NULL),
+                                                                                       strtod(speed, NULL),
+                                                                                       strtod(attack_timer, NULL),
+                                                                                       strtod(strength, NULL),
+                                                                                       strtod(health, NULL)));
         game_context->enemy_count++;
     }
 
@@ -665,8 +665,8 @@ void spawn_enemy_json_furi(Level *level, GameManager *manager, FuriString *json)
     FuriString *_index = get_json_value_furi("index", json);
     //
     FuriString *size = get_json_value_furi("size", json);
-    FuriString *size_x = get_json_value_furi("x", size);
-    FuriString *size_y = get_json_value_furi("y", size);
+    FuriString *size_x = get_json_value_furi("width", size);
+    FuriString *size_y = get_json_value_furi("height", size);
     //
     FuriString *start_position = get_json_value_furi("start_position", json);
     FuriString *start_position_x = get_json_value_furi("x", start_position);
@@ -696,14 +696,14 @@ void spawn_enemy_json_furi(Level *level, GameManager *manager, FuriString *json)
                                                                                        manager,
                                                                                        furi_string_get_cstr(id),
                                                                                        atoi(furi_string_get_cstr(_index)),
-                                                                                       (Vector){atof(furi_string_get_cstr(size_x)), atof(furi_string_get_cstr(size_y))},
-                                                                                       (Vector){atof(furi_string_get_cstr(start_position_x)), atof(furi_string_get_cstr(start_position_y))},
-                                                                                       (Vector){atof(furi_string_get_cstr(end_position_x)), atof(furi_string_get_cstr(end_position_y))},
-                                                                                       atof(furi_string_get_cstr(move_timer)),
-                                                                                       atof(furi_string_get_cstr(speed)),
-                                                                                       atof(furi_string_get_cstr(attack_timer)),
-                                                                                       atof(furi_string_get_cstr(strength)),
-                                                                                       atof(furi_string_get_cstr(health))));
+                                                                                       (Vector){strtod(furi_string_get_cstr(size_x), NULL), strtod(furi_string_get_cstr(size_y), NULL)},
+                                                                                       (Vector){strtod(furi_string_get_cstr(start_position_x), NULL), strtod(furi_string_get_cstr(start_position_y), NULL)},
+                                                                                       (Vector){strtod(furi_string_get_cstr(end_position_x), NULL), strtod(furi_string_get_cstr(end_position_y), NULL)},
+                                                                                       strtod(furi_string_get_cstr(move_timer), NULL),
+                                                                                       strtod(furi_string_get_cstr(speed), NULL),
+                                                                                       strtod(furi_string_get_cstr(attack_timer), NULL),
+                                                                                       strtod(furi_string_get_cstr(strength), NULL),
+                                                                                       strtod(furi_string_get_cstr(health), NULL)));
         game_context->enemy_count++;
     }
 

+ 0 - 27
game/game.c

@@ -38,33 +38,6 @@ static void game_start(GameManager *game_manager, void *ctx)
     }
     furi_string_free(world_list);
 
-    // add one enemy
-    game_context->enemies[0] = level_add_entity(game_context->levels[0], enemy(game_manager,
-                                                                               "sword", // enemy id
-                                                                               0,
-                                                                               (Vector){15, 11},
-                                                                               (Vector){WORLD_WIDTH / 2 + 11, WORLD_HEIGHT / 2 + 16},
-                                                                               (Vector){WORLD_WIDTH / 2 - 11, WORLD_HEIGHT / 2 + 16},
-                                                                               1,
-                                                                               32,
-                                                                               0.5f,
-                                                                               10,
-                                                                               100));
-
-    // add another enemy
-    game_context->enemies[1] = level_add_entity(game_context->levels[0], enemy(game_manager,
-                                                                               "axe", // enemy id
-                                                                               1,
-                                                                               (Vector){15, 11},
-                                                                               (Vector){WORLD_WIDTH / 2 + 11, WORLD_HEIGHT / 2 + 32},
-                                                                               (Vector){WORLD_WIDTH / 2 - 11, WORLD_HEIGHT / 2 + 32},
-                                                                               1,
-                                                                               32,
-                                                                               0.5f,
-                                                                               10,
-                                                                               100));
-
-    game_context->enemy_count = 2;
     game_context->current_level = 0;
 }
 

+ 58 - 28
game/level.c

@@ -1,11 +1,63 @@
 #include <game/level.h>
 #include <flip_storage/storage.h>
+#include <game/storage.h>
+static void set_world(Level *level, GameManager *manager, char *id)
+{
+    char file_path[256];
+    snprintf(file_path, sizeof(file_path),
+             STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/%s/%s_json_data.json",
+             id, id);
+
+    FuriString *json_data_str = flipper_http_load_from_file(file_path);
+    if (!json_data_str || furi_string_empty(json_data_str))
+    {
+        FURI_LOG_E("Game", "Failed to load json data from file");
+        draw_tree_world(level);
+        return;
+    }
+
+    if (!draw_json_world_furi(level, json_data_str))
+    {
+        FURI_LOG_E("Game", "Failed to draw world");
+        draw_tree_world(level);
+        furi_string_free(json_data_str);
+    }
+    else
+    {
+        furi_string_free(json_data_str);
+        snprintf(file_path, sizeof(file_path),
+                 STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/%s/%s_enemy_data.json",
+                 id, id);
+
+        FuriString *enemy_data_str = flipper_http_load_from_file(file_path);
+        if (!enemy_data_str || furi_string_empty(enemy_data_str))
+        {
+            FURI_LOG_E("Game", "Failed to get enemy data");
+            draw_tree_world(level);
+            return;
+        }
+        // Loop through the array
+        for (int i = 0; i < MAX_ENEMIES; i++)
+        {
+            FuriString *single_enemy_data = get_json_array_value_furi("enemy_data", i, enemy_data_str);
+            if (!single_enemy_data || furi_string_empty(single_enemy_data))
+            {
+                // No more enemy elements found
+                break;
+            }
+
+            spawn_enemy_json_furi(level, manager, single_enemy_data);
+            furi_string_free(single_enemy_data);
+        }
+        furi_string_free(enemy_data_str);
+    }
+}
+
 static void level_start(Level *level, GameManager *manager, void *context)
 {
-    UNUSED(manager);
-    if (!level || !context)
+    if (!level || !context || !manager)
     {
-        FURI_LOG_E("Game", "Level or context is NULL");
+        FURI_LOG_E("Game", "Level, context, or manager is NULL");
         return;
     }
 
@@ -24,36 +76,14 @@ static void level_start(Level *level, GameManager *manager, void *context)
             draw_tree_world(level);
             return;
         }
-
-        if (!draw_json_world(level, furi_string_get_cstr(world_data)))
-        {
-            FURI_LOG_E("Game", "Failed to draw world");
-            draw_tree_world(level);
-        }
-
-        // world_data is guaranteed non-NULL here
         furi_string_free(world_data);
-        return;
-    }
 
-    // get the world data
-    FuriString *world_data = load_furi_world(level_context->id);
-    if (!world_data)
-    {
-        FURI_LOG_E("Game", "Failed to load world data");
-        draw_tree_world(level);
-        return;
+        set_world(level, manager, level_context->id);
     }
-
-    // draw the world
-    if (!draw_json_world(level, furi_string_get_cstr(world_data)))
+    else
     {
-        FURI_LOG_E("Game", "World exists but failed to draw.");
-        draw_tree_world(level);
+        set_world(level, manager, level_context->id);
     }
-
-    // world_data is guaranteed non-NULL here
-    furi_string_free(world_data);
 }
 
 static LevelContext *level_context_generic;

+ 204 - 0
game/storage.c

@@ -73,3 +73,207 @@ PlayerContext *load_player_context()
 
     return player_context;
 }
+
+#include <stdio.h>
+#include <furi.h>
+#include <furi_hal.h>
+// etc.  Make sure you include the relevant headers for your project
+
+// 1) Helper: remove first occurrence of "needle" from "string"
+static inline void furi_string_remove_str(FuriString *string, const char *needle)
+{
+    // Remove the FIRST occurrence
+    furi_string_replace_str(string, needle, "", 0);
+}
+
+// 2) Adjusted function: returns exactly "json_data":[ ... ]
+static FuriString *enemy_data(const FuriString *world_data)
+{
+    size_t enemy_data_pos = furi_string_search_str(world_data, "enemy_data", 0);
+    if (enemy_data_pos == FURI_STRING_FAILURE)
+    {
+        FURI_LOG_E("Game", "Failed to find enemy_data in world data");
+
+        return NULL;
+    }
+
+    size_t bracket_start = furi_string_search_char(world_data, '[', enemy_data_pos);
+    if (bracket_start == FURI_STRING_FAILURE)
+    {
+        FURI_LOG_E("Game", "Failed to find start of enemy_data array");
+
+        return NULL;
+    }
+
+    size_t bracket_end = furi_string_search_char(world_data, ']', bracket_start);
+    if (bracket_end == FURI_STRING_FAILURE)
+    {
+        FURI_LOG_E("Game", "Failed to find end of enemy_data array");
+
+        return NULL;
+    }
+
+    FuriString *enemy_data_str = furi_string_alloc();
+    if (!enemy_data_str)
+    {
+        FURI_LOG_E("Game", "Failed to allocate enemy_data string");
+
+        return NULL;
+    }
+
+    furi_string_cat_str(enemy_data_str, "{\"enemy_data\":");
+
+    {
+        FuriString *temp_sub = furi_string_alloc();
+
+        furi_string_set_strn(
+            temp_sub,
+            furi_string_get_cstr(world_data) + bracket_start,
+            (bracket_end + 1) - bracket_start);
+
+        furi_string_cat(enemy_data_str, temp_sub);
+        furi_string_free(temp_sub);
+    }
+
+    furi_string_cat_str(enemy_data_str, "}");
+
+    return enemy_data_str;
+}
+
+static FuriString *json_data(const FuriString *world_data)
+{
+    size_t json_data_pos = furi_string_search_str(world_data, "json_data", 0);
+    if (json_data_pos == FURI_STRING_FAILURE)
+    {
+        FURI_LOG_E("Game", "Failed to find json_data in world data");
+
+        return NULL;
+    }
+
+    size_t bracket_start = furi_string_search_char(world_data, '[', json_data_pos);
+    if (bracket_start == FURI_STRING_FAILURE)
+    {
+        FURI_LOG_E("Game", "Failed to find start of json_data array");
+
+        return NULL;
+    }
+
+    size_t bracket_end = furi_string_search_char(world_data, ']', bracket_start);
+    if (bracket_end == FURI_STRING_FAILURE)
+    {
+        FURI_LOG_E("Game", "Failed to find end of json_data array");
+
+        return NULL;
+    }
+
+    FuriString *json_data_str = furi_string_alloc();
+    if (!json_data_str)
+    {
+        FURI_LOG_E("Game", "Failed to allocate json_data string");
+
+        return NULL;
+    }
+
+    furi_string_cat_str(json_data_str, "{\"json_data\":");
+
+    {
+        FuriString *temp_sub = furi_string_alloc();
+
+        furi_string_set_strn(
+            temp_sub,
+            furi_string_get_cstr(world_data) + bracket_start,
+            (bracket_end + 1) - bracket_start);
+
+        furi_string_cat(json_data_str, temp_sub);
+        furi_string_free(temp_sub);
+    }
+
+    furi_string_cat_str(json_data_str, "}");
+
+    return json_data_str;
+}
+
+bool separate_world_data(char *id, FuriString *world_data)
+{
+    if (!id || !world_data)
+    {
+        FURI_LOG_E("Game", "Invalid parameters");
+        return false;
+    }
+    FuriString *file_json_data = json_data(world_data);
+    if (!file_json_data)
+    {
+        FURI_LOG_E("Game", "Failed to get json data");
+        return false;
+    }
+
+    // Save file_json_data to disk
+    char directory_path[256];
+    snprintf(directory_path, sizeof(directory_path),
+             STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/%s", id);
+
+    Storage *storage = furi_record_open(RECORD_STORAGE);
+    storage_common_mkdir(storage, directory_path);
+
+    File *file = storage_file_alloc(storage);
+    char file_path[256];
+    snprintf(file_path, sizeof(file_path),
+             STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/%s/%s_json_data.json",
+             id, id);
+
+    if (!storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS))
+    {
+        FURI_LOG_E("Game", "Failed to open file for writing: %s", file_path);
+        storage_file_free(file);
+        furi_record_close(RECORD_STORAGE);
+        furi_string_free(file_json_data);
+        return false;
+    }
+
+    size_t data_size = furi_string_size(file_json_data);
+    if (storage_file_write(file, furi_string_get_cstr(file_json_data), data_size) != data_size)
+    {
+        FURI_LOG_E("Game", "Failed to write json_data");
+    }
+
+    furi_string_replace_at(file_json_data, 0, 1, "");
+    furi_string_replace_at(file_json_data, furi_string_size(file_json_data) - 1, 1, "");
+    // include the comma at the end of the json_data array
+    furi_string_cat_str(file_json_data, ",");
+    furi_string_remove_str(world_data, furi_string_get_cstr(file_json_data));
+    furi_string_free(file_json_data);
+
+    FuriString *file_enemy_data = enemy_data(world_data);
+    if (!file_enemy_data)
+    {
+        FURI_LOG_E("Game", "Failed to get enemy data");
+        return false;
+    }
+
+    snprintf(file_path, sizeof(file_path),
+             STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/worlds/%s/%s_enemy_data.json",
+             id, id);
+
+    if (!storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS))
+    {
+        FURI_LOG_E("Game", "Failed to open file for writing: %s", file_path);
+        storage_file_free(file);
+        furi_record_close(RECORD_STORAGE);
+        furi_string_free(file_enemy_data);
+        return false;
+    }
+
+    data_size = furi_string_size(file_enemy_data);
+    if (storage_file_write(file, furi_string_get_cstr(file_enemy_data), data_size) != data_size)
+    {
+        FURI_LOG_E("Game", "Failed to write enemy_data");
+    }
+
+    // Clean up
+    furi_string_free(file_enemy_data);
+    storage_file_close(file);
+    storage_file_free(file);
+    furi_record_close(RECORD_STORAGE);
+
+    return true;
+}

+ 3 - 0
game/storage.h

@@ -6,3 +6,6 @@
 
 bool save_player_context(PlayerContext *player_context);
 PlayerContext *load_player_context();
+
+// save the json_data and enemy_data to separate files
+bool separate_world_data(char *id, FuriString *world_data);

+ 14 - 19
game/world.c

@@ -1,4 +1,5 @@
 #include <game/world.h>
+#include <game/storage.h>
 #include <flip_storage/storage.h>
 void draw_bounds(Canvas *canvas)
 {
@@ -9,24 +10,21 @@ void draw_bounds(Canvas *canvas)
 
 bool draw_json_world(Level *level, const char *json_data)
 {
+    int levels_added = 0;
     for (int i = 0; i < MAX_WORLD_OBJECTS; i++)
     {
-        // 1) Get data array item
         char *data = get_json_array_value("json_data", i, json_data);
         if (!data)
         {
-            // Means we've reached the end of the array
             break;
         }
 
-        // 2) Extract all required fields
         char *icon = get_json_value("icon", data);
         char *x = get_json_value("x", data);
         char *y = get_json_value("y", data);
         char *amount = get_json_value("amount", data);
         char *horizontal = get_json_value("horizontal", data);
 
-        // 3) Check for any NULL pointers
         if (!icon || !x || !y || !amount || !horizontal)
         {
             FURI_LOG_E("Game", "Failed Data: %s", data);
@@ -49,7 +47,6 @@ bool draw_json_world(Level *level, const char *json_data)
             return false;
         }
 
-        // 4) Get the IconContext
         IconContext *icon_context = get_icon_context(icon);
         if (!icon_context)
         {
@@ -66,7 +63,6 @@ bool draw_json_world(Level *level, const char *json_data)
             return false;
         }
 
-        // 5) Decide how many icons to spawn
         int count = atoi(amount);
         if (count < 2)
         {
@@ -81,7 +77,6 @@ bool draw_json_world(Level *level, const char *json_data)
         }
         else
         {
-            // Spawn multiple in a line
             bool is_horizontal = (strcmp(horizontal, "true") == 0);
             spawn_icon_line(
                 level,
@@ -93,8 +88,6 @@ bool draw_json_world(Level *level, const char *json_data)
                 count,
                 is_horizontal);
         }
-
-        // 6) Cleanup
         free(data);
         free(icon);
         free(x);
@@ -102,30 +95,29 @@ bool draw_json_world(Level *level, const char *json_data)
         free(amount);
         free(horizontal);
         free(icon_context);
+        levels_added++;
     }
-    return true;
+    return levels_added > 0;
 }
 
 bool draw_json_world_furi(Level *level, FuriString *json_data)
 {
+    int levels_added = 0;
+
     for (int i = 0; i < MAX_WORLD_OBJECTS; i++)
     {
-        // 1) Get data array item as FuriString
         FuriString *data = get_json_array_value_furi("json_data", i, json_data);
         if (!data)
         {
-            // Means we've reached the end of the array
             break;
         }
 
-        // 2) Extract all required fields
         FuriString *icon = get_json_value_furi("icon", data);
         FuriString *x = get_json_value_furi("x", data);
         FuriString *y = get_json_value_furi("y", data);
         FuriString *amount = get_json_value_furi("amount", data);
         FuriString *horizontal = get_json_value_furi("horizontal", data);
 
-        // 3) Check for any NULL pointers
         if (!icon || !x || !y || !amount || !horizontal)
         {
             FURI_LOG_E("Game", "Failed Data: %s", furi_string_get_cstr(data));
@@ -147,7 +139,6 @@ bool draw_json_world_furi(Level *level, FuriString *json_data)
             return false;
         }
 
-        // 4) Get the IconContext from a FuriString
         IconContext *icon_context = get_icon_context_furi(icon);
         if (!icon_context)
         {
@@ -164,7 +155,6 @@ bool draw_json_world_furi(Level *level, FuriString *json_data)
             return false;
         }
 
-        // 5) Decide how many icons to spawn
         int count = atoi(furi_string_get_cstr(amount));
         if (count < 2)
         {
@@ -179,7 +169,6 @@ bool draw_json_world_furi(Level *level, FuriString *json_data)
         }
         else
         {
-            // Spawn multiple in a line
             bool is_horizontal = (furi_string_cmp(horizontal, "true") == 0);
             spawn_icon_line(
                 level,
@@ -192,7 +181,6 @@ bool draw_json_world_furi(Level *level, FuriString *json_data)
                 is_horizontal);
         }
 
-        // 6) Clean up after every iteration
         furi_string_free(data);
         furi_string_free(icon);
         furi_string_free(x);
@@ -200,8 +188,9 @@ bool draw_json_world_furi(Level *level, FuriString *json_data)
         furi_string_free(amount);
         furi_string_free(horizontal);
         free(icon_context);
+        levels_added++;
     }
-    return true;
+    return levels_added > 0;
 }
 
 void draw_tree_world(Level *level)
@@ -434,5 +423,11 @@ FuriString *fetch_world(const char *name)
         FURI_LOG_E("Game", "Failed to load world data from file");
         return NULL;
     }
+    if (!separate_world_data((char *)name, returned_data))
+    {
+        FURI_LOG_E("Game", "Failed to separate world data");
+        furi_string_free(returned_data);
+        return NULL;
+    }
     return returned_data;
 }