فهرست منبع

fix collision + dynamic speed from icon count

jblanked 11 ماه پیش
والد
کامیت
3f08aaac2f
11فایلهای تغییر یافته به همراه139 افزوده شده و 147 حذف شده
  1. 1 0
      assets/CHANGELOG.md
  2. 6 4
      game/draw.c
  3. 2 2
      game/draw.h
  4. 25 47
      game/enemy.c
  5. 44 41
      game/icon.c
  6. 1 2
      game/icon.h
  7. 16 6
      game/level.c
  8. 6 5
      game/player.c
  9. 3 0
      game/player.h
  10. 33 37
      game/world.c
  11. 2 3
      game/world.h

+ 1 - 0
assets/CHANGELOG.md

@@ -3,6 +3,7 @@
 - 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 transition icon for switching worlds.
+- Doubled the size of each world (from 384x192 to 768x384).
 
 ## 0.3 (2025-01-14)
 - Added new worlds.

+ 6 - 4
game/draw.c

@@ -71,14 +71,16 @@ void draw_icon_line(Canvas *canvas, Vector pos, int amount, bool horizontal, con
 }
 char g_name[32];
 // Draw an icon at a specific position (with collision detection)
-void spawn_icon(Level *level, const char *icon_id, float x, float y)
+void spawn_icon(GameManager *manager, Level *level, const char *icon_id, float x, float y)
 {
     snprintf(g_name, sizeof(g_name), "%s", icon_id);
     Entity *e = level_add_entity(level, &icon_desc);
     entity_pos_set(e, (Vector){x, y});
+    GameContext *game_context = game_manager_game_context_get(manager);
+    game_context->icon_count++;
 }
 // Draw a line of icons at a specific position (with collision detection)
-void spawn_icon_line(Level *level, const char *icon_id, float x, float y, uint8_t amount, bool horizontal)
+void spawn_icon_line(GameManager *manager, Level *level, const char *icon_id, float x, float y, uint8_t amount, bool horizontal)
 {
     for (int i = 0; i < amount; i++)
     {
@@ -90,7 +92,7 @@ void spawn_icon_line(Level *level, const char *icon_id, float x, float y, uint8_
                 break;
             }
 
-            spawn_icon(level, icon_id, x + (i * 17), y);
+            spawn_icon(manager, level, icon_id, x + (i * 17), y);
         }
         else
         {
@@ -100,7 +102,7 @@ void spawn_icon_line(Level *level, const char *icon_id, float x, float y, uint8_
                 break;
             }
 
-            spawn_icon(level, icon_id, x, y + (i * 17));
+            spawn_icon(manager, level, icon_id, x, y + (i * 17));
         }
     }
 }

+ 2 - 2
game/draw.h

@@ -9,7 +9,7 @@ extern int camera_y;
 void draw_user_stats(Canvas *canvas, Vector pos, GameManager *manager);
 void draw_username(Canvas *canvas, Vector pos, char *username);
 void draw_icon_line(Canvas *canvas, Vector pos, int amount, bool horizontal, const Icon *icon);
-void spawn_icon(Level *level, const char *icon_id, float x, float y);
-void spawn_icon_line(Level *level, const char *icon_id, float x, float y, uint8_t amount, bool horizontal);
+void spawn_icon(GameManager *manager, Level *level, const char *icon_id, float x, float y);
+void spawn_icon_line(GameManager *manager, Level *level, const char *icon_id, float x, float y, uint8_t amount, bool horizontal);
 extern char g_name[32];
 void background_render(Canvas *canvas, GameManager *manager);

+ 25 - 47
game/enemy.c

@@ -50,13 +50,11 @@ static EnemyContext *enemy_generic_alloc(
 // Free function
 static void enemy_generic_free(void *context)
 {
-    if (!context)
+    if (context)
     {
-        FURI_LOG_E("Game", "Enemy generic free: Invalid context");
-        return;
+        free(context);
+        context = NULL;
     }
-    free(context);
-    context = NULL;
     if (enemy_context_generic)
     {
         free(enemy_context_generic);
@@ -305,8 +303,8 @@ static void enemy_collision(Entity *self, Entity *other, GameManager *manager, v
                     enemy_context->state = ENEMY_ATTACKED;
 
                     // Bounce the enemy back by X units opposite their last movement direction
-                    enemy_pos.x -= game_context->player_context->dx * enemy_context->radius;
-                    enemy_pos.y -= game_context->player_context->dy * enemy_context->radius;
+                    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;
                     entity_pos_set(self, enemy_pos);
 
                     // Reset enemy's movement direction to prevent immediate re-collision
@@ -354,8 +352,8 @@ static void enemy_collision(Entity *self, Entity *other, GameManager *manager, v
                     game_context->player_context->state = PLAYER_ATTACKED;
 
                     // Bounce the player back by X units opposite their last movement direction
-                    player_pos.x -= game_context->player_context->dx * enemy_context->radius;
-                    player_pos.y -= game_context->player_context->dy * enemy_context->radius;
+                    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;
                     entity_pos_set(other, player_pos);
 
                     // Reset player's movement direction to prevent immediate re-collision
@@ -363,37 +361,27 @@ static void enemy_collision(Entity *self, Entity *other, GameManager *manager, v
                     game_context->player_context->dy = 0;
                 }
             }
-            else
-            {
-                FURI_LOG_I("Game", "Enemy '%s' attack on player is on cooldown: %f seconds remaining", enemy_context->id, (double)(enemy_context->attack_timer - enemy_context->elapsed_attack_timer));
-            }
         }
         else // handle other collisions
         {
-            // bounce player and enemy away from each other
+            // Bounce player in the direction they came
             Vector player_pos = entity_pos_get(other);
-            Vector enemy_pos = entity_pos_get(self);
-
-            // Calculate the direction vector from player to enemy
-            Vector direction_vector = {
-                enemy_pos.x - player_pos.x,
-                enemy_pos.y - player_pos.y};
-
-            // Normalize the direction vector
-            float length = sqrt(direction_vector.x * direction_vector.x + direction_vector.y * direction_vector.y);
-            if (length != 0)
+            switch (game_context->player_context->direction)
             {
-                direction_vector.x /= length;
-                direction_vector.y /= length;
-            }
-
-            // Move the player and enemy away from each other
-            player_pos.y -= direction_vector.y * 3;
+            case PLAYER_UP:
+                player_pos.y += enemy_context->size.y;
+                break;
+            case PLAYER_DOWN:
+                player_pos.y -= enemy_context->size.y;
+                break;
+            case PLAYER_LEFT:
+                player_pos.x += enemy_context->size.x;
+                break;
+            case PLAYER_RIGHT:
+                player_pos.x -= enemy_context->size.x;
+                break;
+            };
             entity_pos_set(other, player_pos);
-
-            enemy_pos.x += direction_vector.x * 3;
-            entity_pos_set(self, enemy_pos);
-
             // Reset player's movement direction to prevent immediate re-collision
             game_context->player_context->dx = 0;
             game_context->player_context->dy = 0;
@@ -625,22 +613,12 @@ const EntityDescription *enemy(
 
 void spawn_enemy_json_furi(Level *level, GameManager *manager, FuriString *json)
 {
-    if (!level)
-    {
-        FURI_LOG_E("Game", "Level is NULL");
-        return;
-    }
-    if (!json)
+    if (!level || !manager || !json)
     {
-        FURI_LOG_E("Game", "JSON is NULL");
+        FURI_LOG_E("Game", "Level, GameManager, or JSON is NULL");
         return;
     }
-    if (!manager)
-    {
-        FURI_LOG_E("Game", "GameManager is NULL");
-        return;
-    }
-    // parameters: id, index, size.x, size.y, start_position.x, start_position.y, end_position.x, end_position.y, move_timer, speed, attack_timer, strength, health
+
     FuriString *id = get_json_value_furi("id", json);
     FuriString *_index = get_json_value_furi("index", json);
     //

+ 44 - 41
game/icon.c

@@ -5,22 +5,31 @@ static void icon_collision(Entity *self, Entity *other, GameManager *manager, vo
     UNUSED(manager);
     UNUSED(self);
     IconContext *ictx = (IconContext *)context;
-    if (!ictx)
-    {
-        FURI_LOG_E("Game", "Icon context is NULL");
-        return;
-    }
-
-    if (entity_description_get(other) == &player_desc)
+    if (ictx && entity_description_get(other) == &player_desc)
     {
         PlayerContext *player = (PlayerContext *)entity_context_get(other);
         if (player)
         {
-            Vector pos = entity_pos_get(other);
-            // Bounce back by 2
-            pos.x -= player->dx * 2;
-            pos.y -= player->dy * 2;
-            entity_pos_set(other, pos);
+            Vector player_pos = entity_pos_get(other);
+
+            // Bounce player in the direction they came
+            switch (player->direction)
+            {
+            case PLAYER_UP:
+                player_pos.y += ictx->size.y;
+                break;
+            case PLAYER_DOWN:
+                player_pos.y -= ictx->size.y;
+                break;
+            case PLAYER_LEFT:
+                player_pos.x += ictx->size.x;
+                break;
+            case PLAYER_RIGHT:
+                player_pos.x -= ictx->size.x;
+                break;
+            };
+
+            entity_pos_set(other, player_pos);
 
             // Reset movement to prevent re-collision
             player->dx = 0;
@@ -33,19 +42,17 @@ static void icon_render(Entity *self, GameManager *manager, Canvas *canvas, void
 {
     UNUSED(manager);
     IconContext *ictx = (IconContext *)context;
-    if (!ictx)
+    if (ictx)
     {
-        FURI_LOG_E("Game", "Icon context is NULL");
-        return;
-    }
-    Vector pos = entity_pos_get(self);
+        Vector pos = entity_pos_get(self);
 
-    // Draw the icon, centered
-    canvas_draw_icon(
-        canvas,
-        pos.x - camera_x - ictx->width / 2,
-        pos.y - camera_y - ictx->height / 2,
-        ictx->icon);
+        // Draw the icon, centered
+        canvas_draw_icon(
+            canvas,
+            pos.x - camera_x - ictx->size.x / 2,
+            pos.y - camera_y - ictx->size.y / 2,
+            ictx->icon);
+    }
 }
 
 static void icon_start(Entity *self, GameManager *manager, void *context)
@@ -73,20 +80,18 @@ static void icon_start(Entity *self, GameManager *manager, void *context)
     }
 
     ictx_self->icon = loaded_data->icon;
-    ictx_self->width = loaded_data->width;
-    ictx_self->height = loaded_data->height;
+    ictx_self->size = (Vector){loaded_data->size.x, loaded_data->size.y};
     ictx->icon = loaded_data->icon;
-    ictx->width = loaded_data->width;
-    ictx->height = loaded_data->height;
+    ictx->size = (Vector){loaded_data->size.x, loaded_data->size.y};
 
     Vector pos = entity_pos_get(self);
-    pos.x += ictx_self->width / 2;
-    pos.y += ictx_self->height / 2;
+    pos.x += ictx_self->size.x / 2;
+    pos.y += ictx_self->size.y / 2;
     entity_pos_set(self, pos);
 
     entity_collider_add_circle(
         self,
-        (ictx_self->width + ictx_self->height) / 4);
+        (ictx_self->size.x + ictx_self->size.y) / 4);
 
     free(loaded_data);
 }
@@ -96,7 +101,6 @@ static void icon_free(Entity *self, GameManager *manager, void *context)
 {
     UNUSED(self);
     UNUSED(manager);
-    UNUSED(context);
     if (context)
     {
         free(context);
@@ -124,8 +128,7 @@ static IconContext *icon_generic_alloc(const char *id, const Icon *icon, uint8_t
     }
     snprintf(ctx->id, sizeof(ctx->id), "%s", id);
     ctx->icon = icon;
-    ctx->width = width;
-    ctx->height = height;
+    ctx->size = (Vector){width, height};
     return ctx;
 }
 
@@ -183,10 +186,10 @@ IconContext *get_icon_context(const char *name)
     {
         return icon_generic_alloc("woman", &I_icon_woman_9x16, 9, 16);
     }
-    else if (is_str(name, "chest_closed"))
-    {
-        return icon_generic_alloc("chest_closed", &I_icon_chest_closed_16x13px, 16, 13);
-    }
+    // else if (is_str(name, "chest_closed"))
+    // {
+    //     return icon_generic_alloc("chest_closed", &I_icon_chest_closed_16x13px, 16, 13);
+    // }
     // else if (is_str(name, "chest_open") )
     // {
     //     return icon_generic_alloc("chest_open", &I_icon_chest_open_16x16px, 16, 16);
@@ -315,10 +318,10 @@ const char *icon_get_id(const Icon *icon)
     {
         return "woman";
     }
-    else if (icon == &I_icon_chest_closed_16x13px)
-    {
-        return "chest_closed";
-    }
+    // else if (icon == &I_icon_chest_closed_16x13px)
+    // {
+    //     return "chest_closed";
+    // }
     // else if (icon == &I_icon_chest_open_16x16px)
     // {
     //     return "chest_open";

+ 1 - 2
game/icon.h

@@ -6,8 +6,7 @@ typedef struct
 {
     char id[32];
     const Icon *icon;
-    uint8_t width;
-    uint8_t height;
+    Vector size;
 } IconContext;
 
 extern const EntityDescription icon_desc;

+ 16 - 6
game/level.c

@@ -40,7 +40,7 @@ static void set_world(Level *level, GameManager *manager, char *id)
     if (!json_data_str || furi_string_empty(json_data_str))
     {
         FURI_LOG_E("Game", "Failed to load json data from file");
-        draw_town_world(level);
+        draw_town_world(manager, level);
         return;
     }
 
@@ -55,10 +55,10 @@ static void set_world(Level *level, GameManager *manager, char *id)
     }
 
     FURI_LOG_I("Game", "Drawing world");
-    if (!draw_json_world_furi(level, json_data_str))
+    if (!draw_json_world_furi(manager, level, json_data_str))
     {
         FURI_LOG_E("Game", "Failed to draw world");
-        draw_town_world(level);
+        draw_town_world(manager, level);
         furi_string_free(json_data_str);
     }
     else
@@ -73,7 +73,7 @@ static void set_world(Level *level, GameManager *manager, char *id)
         if (!enemy_data_str || furi_string_empty(enemy_data_str))
         {
             FURI_LOG_E("Game", "Failed to get enemy data");
-            draw_town_world(level);
+            draw_town_world(manager, level);
             return;
         }
 
@@ -129,7 +129,7 @@ static void level_start(Level *level, GameManager *manager, void *context)
         if (!world_data)
         {
             FURI_LOG_E("Game", "Failed to fetch world data");
-            draw_town_world(level);
+            draw_town_world(manager, level);
             game_context->is_switching_level = false;
             // furi_delay_ms(1000);
             player_spawn(level, manager);
@@ -150,7 +150,17 @@ static void level_start(Level *level, GameManager *manager, void *context)
         // furi_delay_ms(1000);
         game_context->is_switching_level = false;
     }
-
+    /*
+       adjust the player's position n such based on icon count
+       the more icons to draw, the slower the player moves
+       so we'll increase the player's speed as the icon count increases
+       by 0.1 for every 10 icons
+   */
+    game_context->icon_offset = 0;
+    if (!game_context->imu_present)
+    {
+        game_context->icon_offset += ((game_context->icon_count / 10) / 10);
+    }
     player_spawn(level, manager);
 }
 

+ 6 - 5
game/player.c

@@ -211,6 +211,7 @@ static void player_update(Entity *self, GameManager *manager, void *context)
 
     if (game_context->imu_present)
     {
+        // update position using the IMU
         vgm_direction(game_context->imu, player, &pos);
     }
 
@@ -237,7 +238,7 @@ static void player_update(Entity *self, GameManager *manager, void *context)
 
         if (!game_context->is_menu_open)
         {
-            pos.y -= 2;
+            pos.y -= (2 + game_context->icon_offset);
             player->dy = -1;
             player->direction = PLAYER_UP;
         }
@@ -258,7 +259,7 @@ static void player_update(Entity *self, GameManager *manager, void *context)
 
         if (!game_context->is_menu_open)
         {
-            pos.y += 2;
+            pos.y += (2 + game_context->icon_offset);
             player->dy = 1;
             player->direction = PLAYER_DOWN;
         }
@@ -279,7 +280,7 @@ static void player_update(Entity *self, GameManager *manager, void *context)
 
         if (!game_context->is_menu_open)
         {
-            pos.x -= 2;
+            pos.x -= (2 + game_context->icon_offset);
             player->dx = -1;
             player->direction = PLAYER_LEFT;
         }
@@ -302,7 +303,7 @@ static void player_update(Entity *self, GameManager *manager, void *context)
 
         if (!game_context->is_menu_open)
         {
-            pos.x += 2;
+            pos.x += (2 + game_context->icon_offset);
             player->dx = 1;
             player->direction = PLAYER_RIGHT;
         }
@@ -415,7 +416,7 @@ static void player_render(Entity *self, GameManager *manager, Canvas *canvas, vo
     );
 
     // Draw the outer bounds adjusted by camera offset
-    draw_bounds(canvas);
+    canvas_draw_frame(canvas, -camera_x, -camera_y, WORLD_WIDTH, WORLD_HEIGHT);
 
     // Draw the user stats (health, xp, and level)
     background_render(canvas, manager);

+ 3 - 0
game/player.h

@@ -75,6 +75,9 @@ typedef struct
     //
     GameMenuScreen menu_screen;
     uint8_t menu_selection;
+    //
+    int icon_count;
+    int icon_offset;
 } GameContext;
 
 typedef struct

+ 33 - 37
game/world.c

@@ -1,14 +1,8 @@
 #include <game/world.h>
 #include <game/storage.h>
 #include <flip_storage/storage.h>
-void draw_bounds(Canvas *canvas)
-{
-    // Draw the outer bounds adjusted by camera offset
-    // we draw this last to ensure users can see the bounds
-    canvas_draw_frame(canvas, -camera_x, -camera_y, WORLD_WIDTH, WORLD_HEIGHT);
-}
 
-bool draw_json_world_furi(Level *level, const FuriString *json_data)
+bool draw_json_world_furi(GameManager *manager, Level *level, const FuriString *json_data)
 {
     if (!json_data)
     {
@@ -58,6 +52,7 @@ bool draw_json_world_furi(Level *level, const FuriString *json_data)
         {
             // Just one icon
             spawn_icon(
+                manager,
                 level,
                 furi_string_get_cstr(icon),
                 atoi(furi_string_get_cstr(x)),
@@ -67,6 +62,7 @@ bool draw_json_world_furi(Level *level, const FuriString *json_data)
         {
             bool is_horizontal = (furi_string_cmp(horizontal, "true") == 0);
             spawn_icon_line(
+                manager,
                 level,
                 furi_string_get_cstr(icon),
                 atoi(furi_string_get_cstr(x)),
@@ -87,74 +83,74 @@ bool draw_json_world_furi(Level *level, const FuriString *json_data)
     return levels_added > 0;
 }
 
-void draw_town_world(Level *level)
+void draw_town_world(GameManager *manager, Level *level)
 {
 
     // house-fence group 1
-    spawn_icon(level, "house", 164, 40);
-    spawn_icon(level, "fence", 148, 64);
-    spawn_icon(level, "fence", 164, 64);
-    spawn_icon(level, "fence_end", 180, 64);
+    spawn_icon(manager, level, "house", 164, 40);
+    spawn_icon(manager, level, "fence", 148, 64);
+    spawn_icon(manager, level, "fence", 164, 64);
+    spawn_icon(manager, level, "fence_end", 180, 64);
 
     // house-fence group 4 (the left of group 1)
-    spawn_icon(level, "house", 110, 40);
-    spawn_icon(level, "fence", 96, 64);
-    spawn_icon(level, "fence", 110, 64);
-    spawn_icon(level, "fence_end", 126, 64);
+    spawn_icon(manager, level, "house", 110, 40);
+    spawn_icon(manager, level, "fence", 96, 64);
+    spawn_icon(manager, level, "fence", 110, 64);
+    spawn_icon(manager, level, "fence_end", 126, 64);
 
     // house-fence group 5 (the left of group 4)
-    spawn_icon(level, "house", 56, 40);
-    spawn_icon(level, "fence", 40, 64);
-    spawn_icon(level, "fence", 56, 64);
-    spawn_icon(level, "fence_end", 72, 64);
+    spawn_icon(manager, level, "house", 56, 40);
+    spawn_icon(manager, level, "fence", 40, 64);
+    spawn_icon(manager, level, "fence", 56, 64);
+    spawn_icon(manager, level, "fence_end", 72, 64);
 
     // line of fences on the 8th row (using spawn_icon_line)
-    spawn_icon_line(level, "fence", 8, 96, 10, true);
+    spawn_icon_line(manager, level, "fence", 8, 96, 10, true);
 
     // plants spaced out underneath the fences
-    spawn_icon_line(level, "plant", 40, 110, 6, true);
-    spawn_icon_line(level, "flower", 40, 140, 6, true);
+    spawn_icon_line(manager, level, "plant", 40, 110, 6, true);
+    spawn_icon_line(manager, level, "flower", 40, 140, 6, true);
 
     // man and woman
-    spawn_icon(level, "man", 156, 110);
-    spawn_icon(level, "woman", 164, 110);
+    spawn_icon(manager, level, "man", 156, 110);
+    spawn_icon(manager, level, "woman", 164, 110);
 
     // lake
     // Top row
-    spawn_icon(level, "lake_top_left", 240, 62);
-    spawn_icon(level, "lake_top", 264, 57);
-    spawn_icon(level, "lake_top_right", 295, 62);
+    spawn_icon(manager, level, "lake_top_left", 240, 62);
+    spawn_icon(manager, level, "lake_top", 264, 57);
+    spawn_icon(manager, level, "lake_top_right", 295, 62);
 
     // Middle row
-    spawn_icon(level, "lake_left", 231, 84);
-    spawn_icon(level, "lake_right", 304, 84);
+    spawn_icon(manager, level, "lake_left", 231, 84);
+    spawn_icon(manager, level, "lake_right", 304, 84);
 
     // Bottom row
-    spawn_icon(level, "lake_bottom_left", 240, 115);
-    spawn_icon(level, "lake_bottom", 264, 120);
-    spawn_icon(level, "lake_bottom_right", 295, 115);
+    spawn_icon(manager, level, "lake_bottom_left", 240, 115);
+    spawn_icon(manager, level, "lake_bottom", 264, 120);
+    spawn_icon(manager, level, "lake_bottom_right", 295, 115);
 
     // Spawn two full left/up tree lines
     for (int i = 0; i < 2; i++)
     {
         // Horizontal line of 22 icons
-        spawn_icon_line(level, "tree", 5, 2 + i * 17, 22, true);
+        spawn_icon_line(manager, level, "tree", 5, 2 + i * 17, 22, true);
         // Vertical line of 11 icons
-        spawn_icon_line(level, "tree", 5 + i * 17, 2, 11, false);
+        spawn_icon_line(manager, level, "tree", 5 + i * 17, 2, 11, false);
     }
 
     // Spawn two full down tree lines
     for (int i = 9; i < 11; i++)
     {
         // Horizontal line of 22 icons
-        spawn_icon_line(level, "tree", 5, 2 + i * 17, 22, true);
+        spawn_icon_line(manager, level, "tree", 5, 2 + i * 17, 22, true);
     }
 
     // Spawn two full right tree lines
     for (int i = 20; i < 22; i++)
     {
         // Vertical line of 8 icons starting further down (y=50)
-        spawn_icon_line(level, "tree", 5 + i * 17, 50, 8, false);
+        spawn_icon_line(manager, level, "tree", 5 + i * 17, 50, 8, false);
     }
 }
 

+ 2 - 3
game/world.h

@@ -11,7 +11,6 @@
 // Maximum number of world objects
 #define MAX_WORLD_OBJECTS 25 // any more than that and we may run out of heap when switching worlds
 
-void draw_bounds(Canvas *canvas);
-void draw_town_world(Level *level);
-bool draw_json_world_furi(Level *level, const FuriString *json_data);
+void draw_town_world(GameManager *manager, Level *level);
+bool draw_json_world_furi(GameManager *manager, Level *level, const FuriString *json_data);
 FuriString *fetch_world(const char *name);