|
@@ -2,10 +2,10 @@
|
|
|
#include <game/enemy.h>
|
|
#include <game/enemy.h>
|
|
|
#include <notification/notification_messages.h>
|
|
#include <notification/notification_messages.h>
|
|
|
|
|
|
|
|
-static EnemyContext *enemy_context_generic;
|
|
|
|
|
|
|
+static EntityContext *enemy_context_generic;
|
|
|
|
|
|
|
|
// Allocation function
|
|
// Allocation function
|
|
|
-static EnemyContext *enemy_generic_alloc(
|
|
|
|
|
|
|
+static EntityContext *enemy_generic_alloc(
|
|
|
const char *id,
|
|
const char *id,
|
|
|
int index,
|
|
int index,
|
|
|
Vector size,
|
|
Vector size,
|
|
@@ -19,11 +19,11 @@ static EnemyContext *enemy_generic_alloc(
|
|
|
{
|
|
{
|
|
|
if (!enemy_context_generic)
|
|
if (!enemy_context_generic)
|
|
|
{
|
|
{
|
|
|
- enemy_context_generic = malloc(sizeof(EnemyContext));
|
|
|
|
|
|
|
+ enemy_context_generic = malloc(sizeof(EntityContext));
|
|
|
}
|
|
}
|
|
|
if (!enemy_context_generic)
|
|
if (!enemy_context_generic)
|
|
|
{
|
|
{
|
|
|
- FURI_LOG_E("Game", "Failed to allocate EnemyContext");
|
|
|
|
|
|
|
+ FURI_LOG_E("Game", "Failed to allocate EntityContext");
|
|
|
return NULL;
|
|
return NULL;
|
|
|
}
|
|
}
|
|
|
snprintf(enemy_context_generic->id, sizeof(enemy_context_generic->id), "%s", id);
|
|
snprintf(enemy_context_generic->id, sizeof(enemy_context_generic->id), "%s", id);
|
|
@@ -38,32 +38,15 @@ static EnemyContext *enemy_generic_alloc(
|
|
|
enemy_context_generic->strength = strength;
|
|
enemy_context_generic->strength = strength;
|
|
|
enemy_context_generic->health = health;
|
|
enemy_context_generic->health = health;
|
|
|
// Initialize other fields as needed
|
|
// Initialize other fields as needed
|
|
|
- enemy_context_generic->sprite_right = NULL; // Assign appropriate sprite
|
|
|
|
|
- enemy_context_generic->sprite_left = NULL; // Assign appropriate sprite
|
|
|
|
|
- enemy_context_generic->direction = ENEMY_RIGHT; // Default direction
|
|
|
|
|
- enemy_context_generic->state = ENEMY_MOVING_TO_END; // Start in IDLE state
|
|
|
|
|
|
|
+ enemy_context_generic->sprite_right = NULL; // sprite is assigned later
|
|
|
|
|
+ enemy_context_generic->sprite_left = NULL; // sprite is assigned later
|
|
|
|
|
+ enemy_context_generic->direction = ENTITY_RIGHT; // Default direction
|
|
|
|
|
+ enemy_context_generic->state = ENTITY_MOVING_TO_END; // Start in IDLE state
|
|
|
// Set radius based on size, for example, average of size.x and size.y divided by 2
|
|
// Set radius based on size, for example, average of size.x and size.y divided by 2
|
|
|
enemy_context_generic->radius = (size.x + size.y) / 4.0f;
|
|
enemy_context_generic->radius = (size.x + size.y) / 4.0f;
|
|
|
return enemy_context_generic;
|
|
return enemy_context_generic;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Free function
|
|
|
|
|
-static void enemy_generic_free(void *context)
|
|
|
|
|
-{
|
|
|
|
|
- if (!context)
|
|
|
|
|
- {
|
|
|
|
|
- FURI_LOG_E("Game", "Enemy generic free: Invalid context");
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- free(context);
|
|
|
|
|
- context = NULL;
|
|
|
|
|
- if (enemy_context_generic)
|
|
|
|
|
- {
|
|
|
|
|
- free(enemy_context_generic);
|
|
|
|
|
- enemy_context_generic = NULL;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
// Enemy start function
|
|
// Enemy start function
|
|
|
static void enemy_start(Entity *self, GameManager *manager, void *context)
|
|
static void enemy_start(Entity *self, GameManager *manager, void *context)
|
|
|
{
|
|
{
|
|
@@ -79,7 +62,7 @@ static void enemy_start(Entity *self, GameManager *manager, void *context)
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- EnemyContext *enemy_context = (EnemyContext *)context;
|
|
|
|
|
|
|
+ EntityContext *enemy_context = (EntityContext *)context;
|
|
|
// Copy fields from generic context
|
|
// Copy fields from generic context
|
|
|
snprintf(enemy_context->id, sizeof(enemy_context->id), "%s", enemy_context_generic->id);
|
|
snprintf(enemy_context->id, sizeof(enemy_context->id), "%s", enemy_context_generic->id);
|
|
|
enemy_context->index = enemy_context_generic->index;
|
|
enemy_context->index = enemy_context_generic->index;
|
|
@@ -111,15 +94,22 @@ static void enemy_render(Entity *self, GameManager *manager, Canvas *canvas, voi
|
|
|
if (!self || !context || !canvas || !manager)
|
|
if (!self || !context || !canvas || !manager)
|
|
|
return;
|
|
return;
|
|
|
|
|
|
|
|
- EnemyContext *enemy_context = (EnemyContext *)context;
|
|
|
|
|
- GameContext *game_context = game_manager_game_context_get(manager);
|
|
|
|
|
|
|
+ EntityContext *enemy_context = (EntityContext *)context;
|
|
|
|
|
|
|
|
// Get the position of the enemy
|
|
// Get the position of the enemy
|
|
|
Vector pos = entity_pos_get(self);
|
|
Vector pos = entity_pos_get(self);
|
|
|
|
|
|
|
|
|
|
+ // Get the camera position
|
|
|
|
|
+ int x_pos = pos.x - camera_x - enemy_context->size.x / 2;
|
|
|
|
|
+ int y_pos = pos.y - camera_y - enemy_context->size.y / 2;
|
|
|
|
|
+
|
|
|
|
|
+ // check if position is within the screen
|
|
|
|
|
+ if (x_pos + enemy_context->size.x < 0 || x_pos > SCREEN_WIDTH || y_pos + enemy_context->size.y < 0 || y_pos > SCREEN_HEIGHT)
|
|
|
|
|
+ return;
|
|
|
|
|
+
|
|
|
// Choose sprite based on direction
|
|
// Choose sprite based on direction
|
|
|
Sprite *current_sprite = NULL;
|
|
Sprite *current_sprite = NULL;
|
|
|
- if (enemy_context->direction == ENEMY_LEFT)
|
|
|
|
|
|
|
+ if (enemy_context->direction == ENTITY_LEFT)
|
|
|
{
|
|
{
|
|
|
current_sprite = enemy_context->sprite_left;
|
|
current_sprite = enemy_context->sprite_left;
|
|
|
}
|
|
}
|
|
@@ -135,20 +125,13 @@ static void enemy_render(Entity *self, GameManager *manager, Canvas *canvas, voi
|
|
|
pos.x - camera_x - (enemy_context->size.x / 2),
|
|
pos.x - camera_x - (enemy_context->size.x / 2),
|
|
|
pos.y - camera_y - (enemy_context->size.y / 2));
|
|
pos.y - camera_y - (enemy_context->size.y / 2));
|
|
|
|
|
|
|
|
- // instead of username, draw health
|
|
|
|
|
|
|
+ // draw health of enemy
|
|
|
char health_str[32];
|
|
char health_str[32];
|
|
|
snprintf(health_str, sizeof(health_str), "%.0f", (double)enemy_context->health);
|
|
snprintf(health_str, sizeof(health_str), "%.0f", (double)enemy_context->health);
|
|
|
draw_username(canvas, pos, health_str);
|
|
draw_username(canvas, pos, health_str);
|
|
|
-
|
|
|
|
|
- // Draw user stats (this has to be done for all enemies)
|
|
|
|
|
- draw_user_stats(canvas, (Vector){0, 50}, manager);
|
|
|
|
|
-
|
|
|
|
|
- // draw player username from GameContext
|
|
|
|
|
- Vector posi = entity_pos_get(game_context->player);
|
|
|
|
|
- draw_username(canvas, posi, game_context->player_context->username);
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static void send_attack_notification(GameContext *game_context, EnemyContext *enemy_context, bool player_attacked)
|
|
|
|
|
|
|
+static void atk_notify(GameContext *game_context, EntityContext *enemy_context, bool player_attacked)
|
|
|
{
|
|
{
|
|
|
if (!game_context || !enemy_context)
|
|
if (!game_context || !enemy_context)
|
|
|
{
|
|
{
|
|
@@ -158,8 +141,8 @@ static void send_attack_notification(GameContext *game_context, EnemyContext *en
|
|
|
|
|
|
|
|
NotificationApp *notifications = furi_record_open(RECORD_NOTIFICATION);
|
|
NotificationApp *notifications = furi_record_open(RECORD_NOTIFICATION);
|
|
|
|
|
|
|
|
- const bool vibration_allowed = strstr(yes_or_no_choices[game_vibration_on_index], "Yes") != NULL;
|
|
|
|
|
- const bool sound_allowed = strstr(yes_or_no_choices[game_sound_on_index], "Yes") != NULL;
|
|
|
|
|
|
|
+ const bool vibration_allowed = strstr(yes_or_no_choices[vibration_on_index], "Yes") != NULL;
|
|
|
|
|
+ const bool sound_allowed = strstr(yes_or_no_choices[sound_on_index], "Yes") != NULL;
|
|
|
|
|
|
|
|
if (player_attacked)
|
|
if (player_attacked)
|
|
|
{
|
|
{
|
|
@@ -217,23 +200,13 @@ static void enemy_collision(Entity *self, Entity *other, GameManager *manager, v
|
|
|
FURI_LOG_E("Game", "Enemy collision: Invalid parameters");
|
|
FURI_LOG_E("Game", "Enemy collision: Invalid parameters");
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+ EntityContext *enemy_context = (EntityContext *)context;
|
|
|
|
|
+ furi_check(enemy_context, "Enemy collision: EntityContext is NULL");
|
|
|
|
|
+ GameContext *game_context = game_manager_game_context_get(manager);
|
|
|
|
|
+ furi_check(game_context, "Enemy collision: GameContext is NULL");
|
|
|
// Check if the enemy collided with the player
|
|
// Check if the enemy collided with the player
|
|
|
if (entity_description_get(other) == &player_desc)
|
|
if (entity_description_get(other) == &player_desc)
|
|
|
{
|
|
{
|
|
|
- // Retrieve enemy context
|
|
|
|
|
- EnemyContext *enemy_context = (EnemyContext *)context;
|
|
|
|
|
- GameContext *game_context = game_manager_game_context_get(manager);
|
|
|
|
|
- if (!enemy_context)
|
|
|
|
|
- {
|
|
|
|
|
- FURI_LOG_E("Game", "Enemy collision: EnemyContext is NULL");
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (!game_context)
|
|
|
|
|
- {
|
|
|
|
|
- FURI_LOG_E("Game", "Enemy collision: GameContext is NULL");
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
// Get positions of the enemy and the player
|
|
// Get positions of the enemy and the player
|
|
|
Vector enemy_pos = entity_pos_get(self);
|
|
Vector enemy_pos = entity_pos_get(self);
|
|
@@ -244,29 +217,32 @@ static void enemy_collision(Entity *self, Entity *other, GameManager *manager, v
|
|
|
bool player_is_facing_enemy = false;
|
|
bool player_is_facing_enemy = false;
|
|
|
|
|
|
|
|
// Determine if the enemy is facing the player
|
|
// Determine if the enemy is facing the player
|
|
|
- if ((enemy_context->direction == ENEMY_LEFT && player_pos.x < enemy_pos.x) ||
|
|
|
|
|
- (enemy_context->direction == ENEMY_RIGHT && player_pos.x > enemy_pos.x) ||
|
|
|
|
|
- (enemy_context->direction == ENEMY_UP && player_pos.y < enemy_pos.y) ||
|
|
|
|
|
- (enemy_context->direction == ENEMY_DOWN && player_pos.y > enemy_pos.y))
|
|
|
|
|
|
|
+ if ((enemy_context->direction == ENTITY_LEFT && player_pos.x < enemy_pos.x) ||
|
|
|
|
|
+ (enemy_context->direction == ENTITY_RIGHT && player_pos.x > enemy_pos.x) ||
|
|
|
|
|
+ (enemy_context->direction == ENTITY_UP && player_pos.y < enemy_pos.y) ||
|
|
|
|
|
+ (enemy_context->direction == ENTITY_DOWN && player_pos.y > enemy_pos.y))
|
|
|
{
|
|
{
|
|
|
enemy_is_facing_player = true;
|
|
enemy_is_facing_player = true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Determine if the player is facing the enemy
|
|
// Determine if the player is facing the enemy
|
|
|
- if ((game_context->player_context->direction == PLAYER_LEFT && enemy_pos.x < player_pos.x) ||
|
|
|
|
|
- (game_context->player_context->direction == PLAYER_RIGHT && enemy_pos.x > player_pos.x) ||
|
|
|
|
|
- (game_context->player_context->direction == PLAYER_UP && enemy_pos.y < player_pos.y) ||
|
|
|
|
|
- (game_context->player_context->direction == PLAYER_DOWN && enemy_pos.y > player_pos.y))
|
|
|
|
|
|
|
+ if ((game_context->player_context->direction == ENTITY_LEFT && enemy_pos.x < player_pos.x) ||
|
|
|
|
|
+ (game_context->player_context->direction == ENTITY_RIGHT && enemy_pos.x > player_pos.x) ||
|
|
|
|
|
+ (game_context->player_context->direction == ENTITY_UP && enemy_pos.y < player_pos.y) ||
|
|
|
|
|
+ (game_context->player_context->direction == ENTITY_DOWN && enemy_pos.y > player_pos.y))
|
|
|
{
|
|
{
|
|
|
player_is_facing_enemy = true;
|
|
player_is_facing_enemy = true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Handle Player Attacking Enemy (Press OK, facing enemy, and enemy not facing player)
|
|
// Handle Player Attacking Enemy (Press OK, facing enemy, and enemy not facing player)
|
|
|
- if (player_is_facing_enemy && game_context->user_input == GameKeyOk && !enemy_is_facing_player)
|
|
|
|
|
|
|
+ if (player_is_facing_enemy && game_context->last_button == GameKeyOk && !enemy_is_facing_player)
|
|
|
{
|
|
{
|
|
|
|
|
+ // Reset last button
|
|
|
|
|
+ game_context->last_button = -1;
|
|
|
|
|
+
|
|
|
if (game_context->player_context->elapsed_attack_timer >= game_context->player_context->attack_timer)
|
|
if (game_context->player_context->elapsed_attack_timer >= game_context->player_context->attack_timer)
|
|
|
{
|
|
{
|
|
|
- send_attack_notification(game_context, enemy_context, true);
|
|
|
|
|
|
|
+ atk_notify(game_context, enemy_context, true);
|
|
|
|
|
|
|
|
// Reset player's elapsed attack timer
|
|
// Reset player's elapsed attack timer
|
|
|
game_context->player_context->elapsed_attack_timer = 0.0f;
|
|
game_context->player_context->elapsed_attack_timer = 0.0f;
|
|
@@ -288,7 +264,7 @@ static void enemy_collision(Entity *self, Entity *other, GameManager *manager, v
|
|
|
if (enemy_context->health <= 0)
|
|
if (enemy_context->health <= 0)
|
|
|
{
|
|
{
|
|
|
FURI_LOG_I("Game", "Enemy '%s' is dead.. resetting enemy position and health", enemy_context->id);
|
|
FURI_LOG_I("Game", "Enemy '%s' is dead.. resetting enemy position and health", enemy_context->id);
|
|
|
- enemy_context->state = ENEMY_DEAD;
|
|
|
|
|
|
|
+ enemy_context->state = ENTITY_DEAD;
|
|
|
|
|
|
|
|
// Reset enemy position and health
|
|
// Reset enemy position and health
|
|
|
enemy_context->health = 100; // this needs to be set to the enemy's max health
|
|
enemy_context->health = 100; // this needs to be set to the enemy's max health
|
|
@@ -303,11 +279,11 @@ static void enemy_collision(Entity *self, Entity *other, GameManager *manager, v
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
FURI_LOG_I("Game", "Enemy '%s' took %f damage from player", enemy_context->id, (double)game_context->player_context->strength);
|
|
FURI_LOG_I("Game", "Enemy '%s' took %f damage from player", enemy_context->id, (double)game_context->player_context->strength);
|
|
|
- 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_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;
|
|
|
entity_pos_set(self, enemy_pos);
|
|
entity_pos_set(self, enemy_pos);
|
|
|
|
|
|
|
|
// Reset enemy's movement direction to prevent immediate re-collision
|
|
// Reset enemy's movement direction to prevent immediate re-collision
|
|
@@ -325,7 +301,7 @@ static void enemy_collision(Entity *self, Entity *other, GameManager *manager, v
|
|
|
{
|
|
{
|
|
|
if (enemy_context->elapsed_attack_timer >= enemy_context->attack_timer)
|
|
if (enemy_context->elapsed_attack_timer >= enemy_context->attack_timer)
|
|
|
{
|
|
{
|
|
|
- send_attack_notification(game_context, enemy_context, false);
|
|
|
|
|
|
|
+ atk_notify(game_context, enemy_context, false);
|
|
|
|
|
|
|
|
// Reset enemy's elapsed attack timer
|
|
// Reset enemy's elapsed attack timer
|
|
|
enemy_context->elapsed_attack_timer = 0.0f;
|
|
enemy_context->elapsed_attack_timer = 0.0f;
|
|
@@ -336,7 +312,7 @@ static void enemy_collision(Entity *self, Entity *other, GameManager *manager, v
|
|
|
if (game_context->player_context->health <= 0)
|
|
if (game_context->player_context->health <= 0)
|
|
|
{
|
|
{
|
|
|
FURI_LOG_I("Game", "Player is dead.. resetting player position and health");
|
|
FURI_LOG_I("Game", "Player is dead.. resetting player position and health");
|
|
|
- game_context->player_context->state = PLAYER_DEAD;
|
|
|
|
|
|
|
+ game_context->player_context->state = ENTITY_DEAD;
|
|
|
|
|
|
|
|
// Reset player position and health
|
|
// Reset player position and health
|
|
|
entity_pos_set(other, game_context->player_context->start_position);
|
|
entity_pos_set(other, game_context->player_context->start_position);
|
|
@@ -352,11 +328,11 @@ static void enemy_collision(Entity *self, Entity *other, GameManager *manager, v
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
FURI_LOG_I("Game", "Player took %f damage from enemy '%s'", (double)enemy_context->strength, enemy_context->id);
|
|
FURI_LOG_I("Game", "Player took %f damage from enemy '%s'", (double)enemy_context->strength, enemy_context->id);
|
|
|
- game_context->player_context->state = PLAYER_ATTACKED;
|
|
|
|
|
|
|
+ game_context->player_context->state = ENTITY_ATTACKED;
|
|
|
|
|
|
|
|
// Bounce the player back by X units opposite their last movement direction
|
|
// 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);
|
|
entity_pos_set(other, player_pos);
|
|
|
|
|
|
|
|
// Reset player's movement direction to prevent immediate re-collision
|
|
// Reset player's movement direction to prevent immediate re-collision
|
|
@@ -364,52 +340,50 @@ static void enemy_collision(Entity *self, Entity *other, GameManager *manager, v
|
|
|
game_context->player_context->dy = 0;
|
|
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
|
|
else // handle other collisions
|
|
|
{
|
|
{
|
|
|
- // bounce player and enemy away from each other
|
|
|
|
|
- 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)
|
|
|
|
|
- {
|
|
|
|
|
- direction_vector.x /= length;
|
|
|
|
|
- direction_vector.y /= length;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Move the player and enemy away from each other
|
|
|
|
|
- player_pos.y -= direction_vector.y * 3;
|
|
|
|
|
- entity_pos_set(other, player_pos);
|
|
|
|
|
-
|
|
|
|
|
- enemy_pos.x += direction_vector.x * 3;
|
|
|
|
|
- entity_pos_set(self, enemy_pos);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ // Set the player's old position to prevent collision
|
|
|
|
|
+ entity_pos_set(other, game_context->player_context->old_position);
|
|
|
// Reset player's movement direction to prevent immediate re-collision
|
|
// Reset player's movement direction to prevent immediate re-collision
|
|
|
game_context->player_context->dx = 0;
|
|
game_context->player_context->dx = 0;
|
|
|
game_context->player_context->dy = 0;
|
|
game_context->player_context->dy = 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Reset enemy's state
|
|
// Reset enemy's state
|
|
|
- enemy_context->state = ENEMY_IDLE;
|
|
|
|
|
|
|
+ enemy_context->state = ENTITY_IDLE;
|
|
|
enemy_context->elapsed_move_timer = 0.0f;
|
|
enemy_context->elapsed_move_timer = 0.0f;
|
|
|
|
|
|
|
|
- if (game_context->player_context->state == PLAYER_DEAD)
|
|
|
|
|
|
|
+ if (game_context->player_context->state == ENTITY_DEAD)
|
|
|
{
|
|
{
|
|
|
// Reset player's position and health
|
|
// Reset player's position and health
|
|
|
entity_pos_set(other, game_context->player_context->start_position);
|
|
entity_pos_set(other, game_context->player_context->start_position);
|
|
|
- game_context->player_context->health = 100;
|
|
|
|
|
|
|
+ game_context->player_context->health = game_context->player_context->max_health;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // if not player than must be an icon or npc; so push back
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ // push enemy back
|
|
|
|
|
+ Vector enemy_pos = entity_pos_get(self);
|
|
|
|
|
+ switch (enemy_context->direction)
|
|
|
|
|
+ {
|
|
|
|
|
+ case ENTITY_LEFT:
|
|
|
|
|
+ enemy_pos.x += (enemy_context->size.x + game_context->icon_offset);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case ENTITY_RIGHT:
|
|
|
|
|
+ enemy_pos.x -= (enemy_context->size.x + game_context->icon_offset);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case ENTITY_UP:
|
|
|
|
|
+ enemy_pos.y += (enemy_context->size.y + game_context->icon_offset);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case ENTITY_DOWN:
|
|
|
|
|
+ enemy_pos.y -= (enemy_context->size.y + game_context->icon_offset);
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ break;
|
|
|
}
|
|
}
|
|
|
|
|
+ entity_pos_set(self, enemy_pos);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -419,8 +393,8 @@ static void enemy_update(Entity *self, GameManager *manager, void *context)
|
|
|
if (!self || !context || !manager)
|
|
if (!self || !context || !manager)
|
|
|
return;
|
|
return;
|
|
|
|
|
|
|
|
- EnemyContext *enemy_context = (EnemyContext *)context;
|
|
|
|
|
- if (!enemy_context || enemy_context->state == ENEMY_DEAD)
|
|
|
|
|
|
|
+ EntityContext *enemy_context = (EntityContext *)context;
|
|
|
|
|
+ if (!enemy_context || enemy_context->state == ENTITY_DEAD)
|
|
|
{
|
|
{
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -439,7 +413,7 @@ static void enemy_update(Entity *self, GameManager *manager, void *context)
|
|
|
|
|
|
|
|
switch (enemy_context->state)
|
|
switch (enemy_context->state)
|
|
|
{
|
|
{
|
|
|
- case ENEMY_IDLE:
|
|
|
|
|
|
|
+ case ENTITY_IDLE:
|
|
|
// Increment the elapsed_move_timer
|
|
// Increment the elapsed_move_timer
|
|
|
enemy_context->elapsed_move_timer += delta_time;
|
|
enemy_context->elapsed_move_timer += delta_time;
|
|
|
|
|
|
|
@@ -451,21 +425,21 @@ static void enemy_update(Entity *self, GameManager *manager, void *context)
|
|
|
if (fabs(current_pos.x - enemy_context->start_position.x) < (double)1.0 &&
|
|
if (fabs(current_pos.x - enemy_context->start_position.x) < (double)1.0 &&
|
|
|
fabs(current_pos.y - enemy_context->start_position.y) < (double)1.0)
|
|
fabs(current_pos.y - enemy_context->start_position.y) < (double)1.0)
|
|
|
{
|
|
{
|
|
|
- enemy_context->state = ENEMY_MOVING_TO_END;
|
|
|
|
|
|
|
+ enemy_context->state = ENTITY_MOVING_TO_END;
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
- enemy_context->state = ENEMY_MOVING_TO_START;
|
|
|
|
|
|
|
+ enemy_context->state = ENTITY_MOVING_TO_START;
|
|
|
}
|
|
}
|
|
|
enemy_context->elapsed_move_timer = 0.0f;
|
|
enemy_context->elapsed_move_timer = 0.0f;
|
|
|
}
|
|
}
|
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
- case ENEMY_MOVING_TO_END:
|
|
|
|
|
- case ENEMY_MOVING_TO_START:
|
|
|
|
|
|
|
+ case ENTITY_MOVING_TO_END:
|
|
|
|
|
+ case ENTITY_MOVING_TO_START:
|
|
|
{
|
|
{
|
|
|
// Determine the target position based on the current state
|
|
// Determine the target position based on the current state
|
|
|
- Vector target_position = (enemy_context->state == ENEMY_MOVING_TO_END) ? enemy_context->end_position : enemy_context->start_position;
|
|
|
|
|
|
|
+ Vector target_position = (enemy_context->state == ENTITY_MOVING_TO_END) ? enemy_context->end_position : enemy_context->start_position;
|
|
|
|
|
|
|
|
// Get current position
|
|
// Get current position
|
|
|
Vector current_pos = entity_pos_get(self);
|
|
Vector current_pos = entity_pos_get(self);
|
|
@@ -475,23 +449,23 @@ static void enemy_update(Entity *self, GameManager *manager, void *context)
|
|
|
if (current_pos.x < target_position.x)
|
|
if (current_pos.x < target_position.x)
|
|
|
{
|
|
{
|
|
|
direction_vector.x = 1.0f;
|
|
direction_vector.x = 1.0f;
|
|
|
- enemy_context->direction = ENEMY_RIGHT;
|
|
|
|
|
|
|
+ enemy_context->direction = ENTITY_RIGHT;
|
|
|
}
|
|
}
|
|
|
else if (current_pos.x > target_position.x)
|
|
else if (current_pos.x > target_position.x)
|
|
|
{
|
|
{
|
|
|
direction_vector.x = -1.0f;
|
|
direction_vector.x = -1.0f;
|
|
|
- enemy_context->direction = ENEMY_LEFT;
|
|
|
|
|
|
|
+ enemy_context->direction = ENTITY_LEFT;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (current_pos.y < target_position.y)
|
|
if (current_pos.y < target_position.y)
|
|
|
{
|
|
{
|
|
|
direction_vector.y = 1.0f;
|
|
direction_vector.y = 1.0f;
|
|
|
- enemy_context->direction = ENEMY_DOWN;
|
|
|
|
|
|
|
+ enemy_context->direction = ENTITY_DOWN;
|
|
|
}
|
|
}
|
|
|
else if (current_pos.y > target_position.y)
|
|
else if (current_pos.y > target_position.y)
|
|
|
{
|
|
{
|
|
|
direction_vector.y = -1.0f;
|
|
direction_vector.y = -1.0f;
|
|
|
- enemy_context->direction = ENEMY_UP;
|
|
|
|
|
|
|
+ enemy_context->direction = ENTITY_UP;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Normalize direction vector
|
|
// Normalize direction vector
|
|
@@ -529,7 +503,7 @@ static void enemy_update(Entity *self, GameManager *manager, void *context)
|
|
|
// If reached the target position on both axes, transition to IDLE
|
|
// If reached the target position on both axes, transition to IDLE
|
|
|
if (reached_x && reached_y)
|
|
if (reached_x && reached_y)
|
|
|
{
|
|
{
|
|
|
- enemy_context->state = ENEMY_IDLE;
|
|
|
|
|
|
|
+ enemy_context->state = ENTITY_IDLE;
|
|
|
enemy_context->elapsed_move_timer = 0.0f;
|
|
enemy_context->elapsed_move_timer = 0.0f;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -545,8 +519,12 @@ static void enemy_free(Entity *self, GameManager *manager, void *context)
|
|
|
{
|
|
{
|
|
|
UNUSED(self);
|
|
UNUSED(self);
|
|
|
UNUSED(manager);
|
|
UNUSED(manager);
|
|
|
- if (context)
|
|
|
|
|
- enemy_generic_free(context);
|
|
|
|
|
|
|
+ UNUSED(context);
|
|
|
|
|
+ if (enemy_context_generic)
|
|
|
|
|
+ {
|
|
|
|
|
+ free(enemy_context_generic);
|
|
|
|
|
+ enemy_context_generic = NULL;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Enemy behavior structure
|
|
// Enemy behavior structure
|
|
@@ -557,7 +535,7 @@ static const EntityDescription _generic_enemy = {
|
|
|
.render = enemy_render,
|
|
.render = enemy_render,
|
|
|
.collision = enemy_collision,
|
|
.collision = enemy_collision,
|
|
|
.event = NULL,
|
|
.event = NULL,
|
|
|
- .context_size = sizeof(EnemyContext),
|
|
|
|
|
|
|
+ .context_size = sizeof(EntityContext),
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// Enemy function to return the entity description
|
|
// Enemy function to return the entity description
|
|
@@ -580,7 +558,7 @@ const EntityDescription *enemy(
|
|
|
return NULL;
|
|
return NULL;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Allocate a new EnemyContext with provided parameters
|
|
|
|
|
|
|
+ // Allocate a new EntityContext with provided parameters
|
|
|
enemy_context_generic = enemy_generic_alloc(
|
|
enemy_context_generic = enemy_generic_alloc(
|
|
|
id,
|
|
id,
|
|
|
index,
|
|
index,
|
|
@@ -594,54 +572,45 @@ const EntityDescription *enemy(
|
|
|
health);
|
|
health);
|
|
|
if (!enemy_context_generic)
|
|
if (!enemy_context_generic)
|
|
|
{
|
|
{
|
|
|
- FURI_LOG_E("Game", "Failed to allocate EnemyContext");
|
|
|
|
|
|
|
+ FURI_LOG_E("Game", "Failed to allocate EntityContext");
|
|
|
return NULL;
|
|
return NULL;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // assign sprites to the context
|
|
|
enemy_context_generic->sprite_right = game_manager_sprite_load(manager, sprite_context->right_file_name);
|
|
enemy_context_generic->sprite_right = game_manager_sprite_load(manager, sprite_context->right_file_name);
|
|
|
enemy_context_generic->sprite_left = game_manager_sprite_load(manager, sprite_context->left_file_name);
|
|
enemy_context_generic->sprite_left = game_manager_sprite_load(manager, sprite_context->left_file_name);
|
|
|
|
|
|
|
|
// Set initial direction based on start and end positions
|
|
// Set initial direction based on start and end positions
|
|
|
if (start_position.x < end_position.x)
|
|
if (start_position.x < end_position.x)
|
|
|
{
|
|
{
|
|
|
- enemy_context_generic->direction = ENEMY_RIGHT;
|
|
|
|
|
|
|
+ enemy_context_generic->direction = ENTITY_RIGHT;
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
- enemy_context_generic->direction = ENEMY_LEFT;
|
|
|
|
|
|
|
+ enemy_context_generic->direction = ENTITY_LEFT;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Set initial state based on movement
|
|
// Set initial state based on movement
|
|
|
if (start_position.x != end_position.x || start_position.y != end_position.y)
|
|
if (start_position.x != end_position.x || start_position.y != end_position.y)
|
|
|
{
|
|
{
|
|
|
- enemy_context_generic->state = ENEMY_MOVING_TO_END;
|
|
|
|
|
|
|
+ enemy_context_generic->state = ENTITY_MOVING_TO_END;
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
- enemy_context_generic->state = ENEMY_IDLE;
|
|
|
|
|
|
|
+ enemy_context_generic->state = ENTITY_IDLE;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+ free(sprite_context);
|
|
|
return &_generic_enemy;
|
|
return &_generic_enemy;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void spawn_enemy_json_furi(Level *level, GameManager *manager, FuriString *json)
|
|
|
|
|
|
|
+void spawn_enemy(Level *level, GameManager *manager, FuriString *json)
|
|
|
{
|
|
{
|
|
|
- if (!level)
|
|
|
|
|
|
|
+ if (!level || !manager || !json)
|
|
|
{
|
|
{
|
|
|
- FURI_LOG_E("Game", "Level is NULL");
|
|
|
|
|
|
|
+ FURI_LOG_E("Game", "Level, GameManager, or JSON is NULL");
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- if (!json)
|
|
|
|
|
- {
|
|
|
|
|
- FURI_LOG_E("Game", "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 *id = get_json_value_furi("id", json);
|
|
|
FuriString *_index = get_json_value_furi("index", json);
|
|
FuriString *_index = get_json_value_furi("index", json);
|
|
|
//
|
|
//
|
|
@@ -673,13 +642,13 @@ void spawn_enemy_json_furi(Level *level, GameManager *manager, FuriString *json)
|
|
|
manager,
|
|
manager,
|
|
|
furi_string_get_cstr(id),
|
|
furi_string_get_cstr(id),
|
|
|
atoi(furi_string_get_cstr(_index)),
|
|
atoi(furi_string_get_cstr(_index)),
|
|
|
- (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)));
|
|
|
|
|
|
|
+ (Vector){atof_furi(start_position_x), atof_furi(start_position_y)},
|
|
|
|
|
+ (Vector){atof_furi(end_position_x), atof_furi(end_position_y)},
|
|
|
|
|
+ atof_furi(move_timer),
|
|
|
|
|
+ atof_furi(speed),
|
|
|
|
|
+ atof_furi(attack_timer),
|
|
|
|
|
+ atof_furi(strength),
|
|
|
|
|
+ atof_furi(health)));
|
|
|
game_context->enemy_count++;
|
|
game_context->enemy_count++;
|
|
|
}
|
|
}
|
|
|
|
|
|