From a9fba6f01d8c305093f7343b18a9d22858cf2af7 Mon Sep 17 00:00:00 2001 From: doyle Date: Tue, 26 Sep 2023 23:58:48 +1000 Subject: [PATCH] fp: Smoochie attack on aggro, add heart anim on attack --- Data/Textures/smoochie_resized_25%.png | 4 +- Data/Textures/smoochie_resized_25%.txt | 4 +- ...ttack_1.png => smoochie_attack_down_1.png} | 0 ...ttack_2.png => smoochie_attack_down_2.png} | 0 ...ck_side_1.png => smoochie_hurt_side_1.png} | 0 ...ck_side_2.png => smoochie_hurt_side_2.png} | 0 Data/Textures/sprite_spec.txt | 4 +- External/tely | 2 +- feely_pona.cpp | 200 +++++++++++++----- feely_pona_entity.cpp | 4 +- feely_pona_entity.h | 2 +- feely_pona_game.cpp | 34 +-- feely_pona_game.h | 38 +++- 13 files changed, 198 insertions(+), 94 deletions(-) rename Data/Textures/smoochie_resized_25%/{smoochie_attack_1.png => smoochie_attack_down_1.png} (100%) rename Data/Textures/smoochie_resized_25%/{smoochie_attack_2.png => smoochie_attack_down_2.png} (100%) rename Data/Textures/smoochie_resized_25%/{smoochie_attack_side_1.png => smoochie_hurt_side_1.png} (100%) rename Data/Textures/smoochie_resized_25%/{smoochie_attack_side_2.png => smoochie_hurt_side_2.png} (100%) diff --git a/Data/Textures/smoochie_resized_25%.png b/Data/Textures/smoochie_resized_25%.png index 5b9e967..7fb6f77 100644 --- a/Data/Textures/smoochie_resized_25%.png +++ b/Data/Textures/smoochie_resized_25%.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:06112eecf7cd2ee33610864aaf8c365348960f304bc5bcdf796fd5f1ff7298f1 -size 1753854 +oid sha256:58a577a5d120a92819aa057647ae2e4132b488597c7b4478e4220e00e4193c5b +size 1753245 diff --git a/Data/Textures/smoochie_resized_25%.txt b/Data/Textures/smoochie_resized_25%.txt index acb05d0..8df6d7a 100644 --- a/Data/Textures/smoochie_resized_25%.txt +++ b/Data/Textures/smoochie_resized_25%.txt @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bda589dba1308d987ebe213f91b876a0ded6bc43305322902488fb4802598985 -size 713 +oid sha256:bdff6be2e0bcb8f3bb4ba36be89e6166eabb3245aacf803ba56e78393a9d5ad8 +size 716 diff --git a/Data/Textures/smoochie_resized_25%/smoochie_attack_1.png b/Data/Textures/smoochie_resized_25%/smoochie_attack_down_1.png similarity index 100% rename from Data/Textures/smoochie_resized_25%/smoochie_attack_1.png rename to Data/Textures/smoochie_resized_25%/smoochie_attack_down_1.png diff --git a/Data/Textures/smoochie_resized_25%/smoochie_attack_2.png b/Data/Textures/smoochie_resized_25%/smoochie_attack_down_2.png similarity index 100% rename from Data/Textures/smoochie_resized_25%/smoochie_attack_2.png rename to Data/Textures/smoochie_resized_25%/smoochie_attack_down_2.png diff --git a/Data/Textures/smoochie_resized_25%/smoochie_attack_side_1.png b/Data/Textures/smoochie_resized_25%/smoochie_hurt_side_1.png similarity index 100% rename from Data/Textures/smoochie_resized_25%/smoochie_attack_side_1.png rename to Data/Textures/smoochie_resized_25%/smoochie_hurt_side_1.png diff --git a/Data/Textures/smoochie_resized_25%/smoochie_attack_side_2.png b/Data/Textures/smoochie_resized_25%/smoochie_hurt_side_2.png similarity index 100% rename from Data/Textures/smoochie_resized_25%/smoochie_attack_side_2.png rename to Data/Textures/smoochie_resized_25%/smoochie_hurt_side_2.png diff --git a/Data/Textures/sprite_spec.txt b/Data/Textures/sprite_spec.txt index 91b6506..4426fd0 100644 --- a/Data/Textures/sprite_spec.txt +++ b/Data/Textures/sprite_spec.txt @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39887e579aa5d7d46f033445b0b4335b84b567c3b67583e15c346b9fe69d1bd1 -size 350 +oid sha256:303c69a1100db4d100f81eabb313f92930e807f1dd9c53fca52294859c0f2305 +size 353 diff --git a/External/tely b/External/tely index e86fb0c..f28426b 160000 --- a/External/tely +++ b/External/tely @@ -1 +1 @@ -Subproject commit e86fb0cad3342a12713c400da017648741c29234 +Subproject commit f28426b027a5d63974ff8901459b8fb8454f39fe diff --git a/feely_pona.cpp b/feely_pona.cpp index bc3205e..4f76e33 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -19,8 +19,8 @@ struct FP_GlobalAnimations Dqn_String8 smoochie_walk_down = DQN_STRING8("smoochie_walk_down"); Dqn_String8 smoochie_walk_left = DQN_STRING8("smoochie_walk_left"); Dqn_String8 smoochie_walk_right = DQN_STRING8("smoochie_walk_right"); - Dqn_String8 smoochie_attack_down = DQN_STRING8("smoochie_attack"); - Dqn_String8 smoochie_attack_side = DQN_STRING8("smoochie_attack_side"); + Dqn_String8 smoochie_attack_down = DQN_STRING8("smoochie_attack_down"); + Dqn_String8 smoochie_hurt_side = DQN_STRING8("smoochie_hurt_side"); Dqn_String8 smoochie_attack_heart = DQN_STRING8("smoochie_attack_heart"); Dqn_String8 smoochie_death = DQN_STRING8("smoochie_death"); } @@ -132,6 +132,7 @@ void TELY_DLL_Init(void *user_data) FP_Game *game = Dqn_Arena_New(&platform->arena, FP_Game, Dqn_ZeroMem_Yes); game->chunk_pool = &platform->chunk_pool; game->meters_to_pixels = 65.416f; + Dqn_PCG32_Seed(&game->rng, 0xABCDEF); platform->user_data = game; { @@ -464,18 +465,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga if (we_are_clicked_entity) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { - - switch (entity->direction) { - case FP_GameDirection_Up: /*FALLTHRU*/ - case FP_GameDirection_Right: /*FALLTHRU*/ - case FP_GameDirection_Left: { - action->next_state = FP_EntitySmoochieState_AttackSide; - } break; - - case FP_GameDirection_Down: { - action->next_state = FP_EntitySmoochieState_AttackDown; - } break; - } + action->next_state = FP_EntitySmoochieState_AttackDown; } else if (dir_vector.x || dir_vector.y) { action->next_state = FP_EntitySmoochieState_Run; } @@ -485,20 +475,57 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga case FP_EntitySmoochieState_AttackDown: { if (entering_new_state) { TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.smoochie_attack_down, TELY_AssetFlip_No); - uint64_t duration_ms = sprite.anim->count * sprite.anim->ms_per_frame; + uint64_t duration_ms = sprite.anim->count * sprite.anim->ms_per_frame; FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); } + // NOTE: Check if the heart animation is playing + bool has_heart_flourish = false; + DQN_FOR_UINDEX(anim_index, entity->extra_cosmetic_anims.size) { + FP_GameRenderSprite *sprite = entity->extra_cosmetic_anims.data + anim_index; + if (sprite->asset.anim->label == g_anim_names.smoochie_attack_heart) { + has_heart_flourish = true; + break; + } + } + + // NOTE: If we don't have the anim playing make one + if (!has_heart_flourish) { + FP_GameRenderSprite *cosmetic_sprite = Dqn_FArray_Make(&entity->extra_cosmetic_anims, Dqn_ZeroMem_Yes); + if (cosmetic_sprite) { + cosmetic_sprite->asset = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.smoochie_attack_heart, TELY_AssetFlip_No); + cosmetic_sprite->started_at_clock_ms = game->clock_ms; + cosmetic_sprite->height.meters = entity->sprite_height.meters * .35f; + cosmetic_sprite->loop = true; + + uint32_t max_rng_dist_x = DQN_CAST(uint32_t)(FP_Game_MetersToPixelsNx1(game, entity->sprite_height.meters) * 1.f); + uint32_t rng_x = Dqn_PCG32_Range(&game->rng, DQN_CAST(uint32_t)0, max_rng_dist_x); + cosmetic_sprite->offset.x = rng_x - (max_rng_dist_x * .5f); + + uint32_t max_rng_dist_y = DQN_CAST(uint32_t)(FP_Game_MetersToPixelsNx1(game, entity->sprite_height.meters) * .25f); + uint32_t rng_y = Dqn_PCG32_Range(&game->rng, DQN_CAST(uint32_t)0, max_rng_dist_y); + cosmetic_sprite->offset.y = -DQN_CAST(Dqn_f32)rng_y; + } + } + if (action_has_finished) { + // NOTE: Ensure the heart animation terminates by removing the loop + DQN_FOR_UINDEX(anim_index, entity->extra_cosmetic_anims.size) { + FP_GameRenderSprite *sprite = entity->extra_cosmetic_anims.data + anim_index; + if (sprite->asset.anim->label == g_anim_names.smoochie_attack_heart) + sprite->loop = false; + } + + // NOTE: Transition out of the action action->next_state = FP_EntitySmoochieState_Idle; } } break; - case FP_EntitySmoochieState_AttackSide: { + case FP_EntitySmoochieState_HurtSide: { if (entering_new_state) { TELY_AssetFlip flip = entity->direction == FP_GameDirection_Right ? TELY_AssetFlip_X : TELY_AssetFlip_No; - TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.smoochie_attack_side, flip); - uint64_t duration_ms = sprite.anim->count * sprite.anim->ms_per_frame; + TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.smoochie_hurt_side, flip); + uint64_t duration_ms = sprite.anim->count * sprite.anim->ms_per_frame; FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); } @@ -527,28 +554,17 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga if (we_are_clicked_entity) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { - - switch (entity->direction) { - case FP_GameDirection_Up: /*FALLTHRU*/ - case FP_GameDirection_Right: /*FALLTHRU*/ - case FP_GameDirection_Left: { - action->next_state = FP_EntitySmoochieState_AttackSide; - } break; - - case FP_GameDirection_Down: { - action->next_state = FP_EntitySmoochieState_AttackDown; - } break; - } + action->next_state = FP_EntitySmoochieState_AttackDown; } } - if (!entity_has_velocity /*&& !has_collision*/) { + if (!entity_has_velocity) { action->next_state = FP_EntitySmoochieState_Idle; } } break; } - if (*state == FP_EntitySmoochieState_AttackSide || *state == FP_EntitySmoochieState_AttackDown) { + if (*state == FP_EntitySmoochieState_AttackDown) { entity->attack_box_size = entity->local_hit_box_size; // NOTE: Position the attack box if (entity->direction == FP_GameDirection_Left) { @@ -689,18 +705,18 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input } } - if (closest_terry_dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 5.f))) { - FP_SentinelListLink *first_waypoint = FP_SentinelList_Front(&entity->waypoints); - if (first_waypoint->data != closest_terry->handle) { - FP_SentinelListLink *link = FP_SentinelList_MakeBefore(&entity->waypoints, first_waypoint, game->chunk_pool); - link->data = closest_terry->handle; + if (closest_terry_dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 2.f))) { + FP_SentinelListLink *first_waypoint = FP_SentinelList_Front(&entity->waypoints); + if (first_waypoint->data.entity != closest_terry->handle) { + FP_SentinelListLink *link = FP_SentinelList_MakeBefore(&entity->waypoints, first_waypoint, game->chunk_pool); + link->data.entity = closest_terry->handle; } } } while (entity->waypoints.size) { - FP_SentinelListLink *waypoint_link = entity->waypoints.sentinel->next; - FP_GameEntity *waypoint = FP_Game_GetEntity(game, waypoint_link->data); + FP_SentinelListLink *waypoint_link = entity->waypoints.sentinel->next; + FP_GameEntity *waypoint = FP_Game_GetEntity(game, waypoint_link->data.entity); if (FP_Game_IsNilEntity(waypoint)) { FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool); continue; @@ -712,16 +728,50 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input // NOTE: Check if we've arrived at the waypoint Dqn_f32 dist_to_waypoint_sq = Dqn_V2_LengthSq(entity_to_waypoint); - Dqn_f32 arrival_threshold = DQN_MIN(DQN_SQUARED(entity->local_hit_box_size.x * .5f), 10.f); - if (dist_to_waypoint_sq < arrival_threshold) { - FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool); - continue; + + Dqn_f32 arrival_threshold = {}; + switch (waypoint_link->data.arrive) { + case FP_GameWaypointArrive_Default: { + arrival_threshold = FP_Game_MetersToPixelsNx1(game, 1.f); + } break; + + case FP_GameWaypointArrive_WhenWithinEntitySize: { + arrival_threshold = DQN_MAX(waypoint->local_hit_box_size.w, waypoint->local_hit_box_size.h) * waypoint_link->data.value; + } break; } // NOTE: We haven't arrived yet, calculate an acceleration vector to the waypoint - Dqn_V2 entity_to_waypoint_norm = Dqn_V2_Normalise(entity_to_waypoint); - acceleration_meters_per_s = entity_to_waypoint_norm * 4.f; - break; + if (dist_to_waypoint_sq > DQN_SQUARED(arrival_threshold)) { + Dqn_V2 entity_to_waypoint_norm = Dqn_V2_Normalise(entity_to_waypoint); + acceleration_meters_per_s = entity_to_waypoint_norm * 4.f; + break; + } + + // NOTE: We have arrived at the waypoint + if ((entity->flags & FP_GameEntityFlag_AggrosWhenNearTerry) && waypoint->type == FP_EntityType_Terry) { + // NOTE: We had a waypoint to move to Terry because he has + // drawn our aggro and we've arrived at Terry + + switch (entity->type) { + case FP_EntityType_Nil: /*FALLTHRU*/ + case FP_EntityType_Merchant: break; + + case FP_EntityType_Terry: { + // TODO(doyle): We should check if it's valid to enter this new state + // from the entity's current state + entity->action.next_state = FP_EntityTerryState_AttackSide; + } break; + + case FP_EntityType_Smoochie: { + // TODO(doyle): We should check if it's valid to enter this new state + // from the entity's current state + entity->action.next_state = FP_EntitySmoochieState_AttackDown; + } break; + } + break; + } else { + FP_SentinelList_Erase(&entity->waypoints, waypoint_link, game->chunk_pool); + } } } @@ -897,7 +947,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input // NOTE: Setup the mob with waypoints FP_GameEntity *mob = FP_Game_GetEntity(game, link->data); - mob->waypoints = FP_SentinelList_Init(game->chunk_pool); + mob->waypoints = FP_SentinelList_Init(game->chunk_pool); mob->flags |= FP_GameEntityFlag_AggrosWhenNearTerry; for (FP_GameEntity *waypoint_entity = entity->first_child; waypoint_entity; waypoint_entity = waypoint_entity->next) { @@ -905,8 +955,10 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input continue; // NOTE: Add the waypoint - FP_SentinelListLink *waypoint = FP_SentinelList_Make(&mob->waypoints, game->chunk_pool); - waypoint->data = waypoint_entity->handle; + FP_SentinelListLink *waypoint = FP_SentinelList_Make(&mob->waypoints, game->chunk_pool); + waypoint->data.entity = waypoint_entity->handle; + waypoint->data.arrive = FP_GameWaypointArrive_WhenWithinEntitySize; + waypoint->data.value = 1.5f; } } } @@ -1032,7 +1084,7 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) } break; } - Dqn_f32 sprite_in_meters = src_rect.size.y * FP_Game_PixelsToMeters(game); + Dqn_f32 sprite_in_meters = FP_Game_PixelsToMetersNx1(game, src_rect.size.y); Dqn_f32 size_scale = entity->sprite_height.meters / sprite_in_meters; Dqn_Rect dest_rect = {}; @@ -1048,6 +1100,50 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) TELY_Render_TextureColourV4(renderer, sprite.sheet->tex_handle, src_rect, dest_rect, TELY_COLOUR_WHITE_V4); } + DQN_FOR_UINDEX(anim_index, entity->extra_cosmetic_anims.size) { + FP_GameRenderSprite *sprite = entity->extra_cosmetic_anims.data + anim_index; + uint64_t elapsed_ms = game->clock_ms - sprite->started_at_clock_ms; + uint16_t raw_anim_frame = DQN_CAST(uint16_t)(elapsed_ms / sprite->asset.anim->ms_per_frame); + if (raw_anim_frame > sprite->asset.anim->count && !sprite->loop) { + anim_index = Dqn_FArray_EraseRange(&entity->extra_cosmetic_anims, anim_index, 1, Dqn_ArrayErase_Unstable).it_index; + continue; + } + + uint16_t anim_frame = raw_anim_frame % sprite->asset.anim->count; + Dqn_usize sprite_index = sprite->asset.anim->index + anim_frame; + Dqn_Rect src_rect = {}; + switch (sprite->asset.sheet->type) { + case TELY_AssetSpriteSheetType_Uniform: { + Dqn_usize sprite_sheet_row = sprite_index / sprite->asset.sheet->sprites_per_row; + Dqn_usize sprite_sheet_column = sprite_index % sprite->asset.sheet->sprites_per_row; + src_rect.pos.x = DQN_CAST(Dqn_f32)(sprite_sheet_column * sprite->asset.sheet->sprite_size.w); + src_rect.pos.y = DQN_CAST(Dqn_f32)(sprite_sheet_row * sprite->asset.sheet->sprite_size.y); + src_rect.size.w = DQN_CAST(Dqn_f32)sprite->asset.sheet->sprite_size.w; + src_rect.size.h = DQN_CAST(Dqn_f32)sprite->asset.sheet->sprite_size.h; + } break; + + case TELY_AssetSpriteSheetType_Rects: { + DQN_ASSERT(sprite_index < sprite->asset.sheet->rects.size); + src_rect = sprite->asset.sheet->rects.data[sprite_index]; + } break; + } + + Dqn_f32 sprite_in_meters = FP_Game_PixelsToMetersNx1(game, src_rect.size.y); + Dqn_f32 size_scale = sprite->height.meters / sprite_in_meters; + + Dqn_Rect dest_rect = {}; + dest_rect.size = src_rect.size * size_scale; + dest_rect.pos = world_pos - (dest_rect.size * .5f) + sprite->offset; + + if (sprite->asset.flip & TELY_AssetFlip_X) + dest_rect.size.w *= -1.f; // NOTE: Flip the texture horizontally + + if (sprite->asset.flip & TELY_AssetFlip_Y) + dest_rect.size.h *= -1.f; // NOTE: Flip the texture vertically + + TELY_Render_TextureColourV4(renderer, sprite->asset.sheet->tex_handle, src_rect, dest_rect, TELY_COLOUR_WHITE_V4); + } + if (entity->flags & FP_GameEntityFlag_MobSpawner) { Dqn_V2 start = world_pos; for (FP_GameEntity *waypoint_entity = entity->first_child; waypoint_entity; waypoint_entity = waypoint_entity->next) { @@ -1077,8 +1173,8 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) // NOTE: Draw the waypoints that the entity is moving along if (entity->waypoints.size) { Dqn_V2 start = world_pos; - for (FP_SentinelListLink *link = nullptr; FP_SentinelList_Iterate(&entity->waypoints, &link); ) { - FP_GameEntity *waypoint = FP_Game_GetEntity(game, link->data); + for (FP_SentinelListLink *link = nullptr; FP_SentinelList_Iterate(&entity->waypoints, &link); ) { + FP_GameEntity *waypoint = FP_Game_GetEntity(game, link->data.entity); if (FP_Game_IsNilEntity(waypoint)) continue; diff --git a/feely_pona_entity.cpp b/feely_pona_entity.cpp index 01a1ff3..aaa50e7 100644 --- a/feely_pona_entity.cpp +++ b/feely_pona_entity.cpp @@ -47,7 +47,7 @@ static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, D entity->hp = 3; entity->local_pos = pos; entity->sprite_height.meters = 1.6f; - entity->local_hit_box_size = Dqn_V2_InitNx2(0.4f, 1.6f) * FP_Game_MetersToPixels(game); + entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, 1.6f); FP_Entity_AddDebugEditorFlags(game, entity->handle); entity->flags |= FP_GameEntityFlag_NonTraversable; entity->flags |= FP_GameEntityFlag_Attackable; @@ -110,7 +110,7 @@ static FP_GameEntityHandle FP_Entity_CreateTerry(FP_Game *game, Dqn_V2 pos, DQN_ entity->type = FP_EntityType_Terry; entity->local_pos = pos; entity->sprite_height.meters = 1.8f; - entity->local_hit_box_size = Dqn_V2_InitNx2(0.5f, entity->sprite_height.meters) * FP_Game_MetersToPixels(game); + entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.5f, entity->sprite_height.meters); FP_Entity_AddDebugEditorFlags(game, result); entity->flags |= FP_GameEntityFlag_NonTraversable; return result; diff --git a/feely_pona_entity.h b/feely_pona_entity.h index c618462..e14e11f 100644 --- a/feely_pona_entity.h +++ b/feely_pona_entity.h @@ -27,7 +27,7 @@ enum FP_EntitySmoochieState FP_EntitySmoochieState_Nil, FP_EntitySmoochieState_Idle, FP_EntitySmoochieState_AttackDown, - FP_EntitySmoochieState_AttackSide, + FP_EntitySmoochieState_HurtSide, FP_EntitySmoochieState_AttackHeart, FP_EntitySmoochieState_Run, }; diff --git a/feely_pona_game.cpp b/feely_pona_game.cpp index 5cc85f6..b1692cd 100644 --- a/feely_pona_game.cpp +++ b/feely_pona_game.cpp @@ -3,35 +3,13 @@ #include "feely_pona_unity.h" #endif -static Dqn_f32 FP_Game_PixelsToMeters(FP_Game const *game) -{ - Dqn_f32 result = game ? (1.f / game->meters_to_pixels) : 10.f; - return result; -} +#define FP_Game_MetersToPixelsNx1(game, val) ((val) * (game)->meters_to_pixels) +#define FP_Game_MetersToPixelsNx2(game, x, y) (Dqn_V2_InitNx2(x, y) * (game)->meters_to_pixels) +#define FP_Game_MetersToPixelsV2(game, xy) (xy * (game)->meters_to_pixels) -static Dqn_f32 FP_Game_MetersToPixels(FP_Game const *game) -{ - Dqn_f32 result = game ? game->meters_to_pixels : 1 / 10.f; - return result; -} - -static Dqn_V2 FP_Game_MetersToPixelsV2(FP_Game const *game, Dqn_V2 pos) -{ - Dqn_V2 result = pos * FP_Game_MetersToPixels(game); - return result; -} - -static Dqn_f32 FP_Game_MetersToPixelsNx1(FP_Game const *game, Dqn_f32 val) -{ - Dqn_f32 result = val * FP_Game_MetersToPixels(game); - return result; -} - -static Dqn_V2 FP_Game_MetersToPixelsNx2(FP_Game const *game, Dqn_f32 x, Dqn_f32 y) -{ - Dqn_V2 result = Dqn_V2_InitNx2(x, y) * FP_Game_MetersToPixels(game); - return result; -} +#define FP_Game_PixelsToMetersNx1(game, val) ((val) * (1.f/(game)->meters_to_pixels)) +#define FP_Game_PixelsToMetersNx2(game, x, y) (Dqn_V2_InitNx2(x, y) * (1.f/(game)->meters_to_pixels)) +#define FP_Game_PixelsToMetersV2(game, xy) (xy * (1.f/(game)->meters_to_pixels)) static bool operator==(FP_GameEntityHandle const &lhs, FP_GameEntityHandle const &rhs) { diff --git a/feely_pona_game.h b/feely_pona_game.h index 50c202f..fb9c5b4 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -48,11 +48,26 @@ struct FP_GameEntityHandle uint64_t id; }; +enum FP_GameWaypointArrive +{ + // Considered arrived when within 1 meter of the target (`value` is ignored). + FP_GameWaypointArrive_Default, + + // If set, we consider the entity as arriving at the waypoint when it's + // distance to the target is: + // + // `arrived = dist <= (entity_hit_box_size * value)` + // + // A value of 0 for example means we are considered arrived when the entity + // is positioned exactly on top of the target's position. + FP_GameWaypointArrive_WhenWithinEntitySize, +}; + struct FP_GameWaypoint { - Dqn_V2I pos; - FP_GameWaypoint *next; - FP_GameWaypoint *prev; + FP_GameEntityHandle entity; + FP_GameWaypointArrive arrive; + Dqn_f32 value; }; struct FP_GameEntitySpawnList @@ -86,6 +101,15 @@ enum FP_GameDirection FP_GameDirection_Right, }; +struct FP_GameRenderSprite +{ + Dqn_V2 offset; + TELY_AssetAnimatedSprite asset; + bool loop; + FP_Meters height; + uint64_t started_at_clock_ms; +}; + struct FP_GameEntity { FP_GameEntity *next; @@ -104,7 +128,12 @@ struct FP_GameEntity FP_GameEntityAction action; Dqn_V2 velocity; - FP_SentinelList waypoints; + // Extra animations that are to be executed, but, don't affect the state + // of the entity. For example, when Smoochie attacks, we have a heart + // animation that is a seperate sprite that will play out. + Dqn_FArray extra_cosmetic_anims; + + FP_SentinelList waypoints; // NOTE: The entity hit box is positioned at the center of the entity. Dqn_V2 local_hit_box_size; @@ -181,6 +210,7 @@ struct FP_Game Dqn_f32 meters_to_pixels; uint64_t clock_ms; + Dqn_PCG32 rng; }; struct FP_GameAStarNode