From 8d01a3631ff802966a768a65709e5da7676de2f2 Mon Sep 17 00:00:00 2001 From: doyle Date: Sun, 22 Oct 2023 21:17:25 +1100 Subject: [PATCH] fp: Add hit particles --- Data/Textures/sprite_spec.txt | 4 +- feely_pona.cpp | 100 +++++++++++++++++++++++++++++++++- feely_pona.h | 14 ++++- feely_pona_game.h | 15 +++++ 4 files changed, 127 insertions(+), 6 deletions(-) diff --git a/Data/Textures/sprite_spec.txt b/Data/Textures/sprite_spec.txt index 712af29..e24a077 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:62d78af7a85007ab5eabd9078f9461018fca76b0484f1ea2d48a17d221e645f5 -size 2268 +oid sha256:ac6f59c0336437e0938eb9cd344f17f72d646da9122c61ae8cc502cfaae272ab +size 2260 diff --git a/feely_pona.cpp b/feely_pona.cpp index 83856e1..027c2a6 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -3,6 +3,37 @@ #include "feely_pona_unity.h" #endif +struct FP_ParticleDescriptor +{ + Dqn_String8 anim_name; + Dqn_V2 pos; + Dqn_V2 velocity; + Dqn_V2 velocity_variance; + Dqn_V4 colour_begin; + Dqn_V4 colour_end; + Dqn_usize duration_ms; +}; + +static void FP_EmitParticle(FP_GamePlay *play, FP_ParticleDescriptor descriptor, Dqn_usize count) +{ + DQN_FOR_UINDEX (index, count) { + uint32_t particle_index = play->particle_next_index++ & (DQN_ARRAY_UCOUNT(play->particles) - 1); + FP_Particle *particle = play->particles + particle_index; + if (particle->alive) + continue; + + particle->anim_name = descriptor.anim_name; + particle->alive = true; + particle->pos = descriptor.pos; + particle->velocity.x = descriptor.velocity.x + (descriptor.velocity_variance.x * (Dqn_PCG32_NextF32(&play->rng) - 0.5f)); + particle->velocity.y = descriptor.velocity.y + (descriptor.velocity_variance.y * (Dqn_PCG32_NextF32(&play->rng) - 0.5f)); + particle->colour_begin = descriptor.colour_begin; + particle->colour_end = descriptor.colour_end; + particle->start_ms = play->clock_ms; + particle->end_ms = play->clock_ms + descriptor.duration_ms; + } +} + static TELY_AssetSpriteSheet FP_LoadSpriteSheetFromSpec(TELY_Platform *platform, TELY_Assets *assets, Dqn_Arena *arena, Dqn_String8 sheet_name) { TELY_AssetSpriteSheet result = {}; @@ -2108,7 +2139,29 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input if (!Dqn_Rect_Intersects(attacker_box, defender_box)) continue; - // NOTE: Do HP ========================================================================= + // NOTE: Emit hit particles ======================================================== + FP_ParticleDescriptor particle_desc = {}; + Dqn_usize particle_selector = Dqn_PCG32_Range(&game->play.rng, 0, 3); + if (particle_selector == 0) { + particle_desc.anim_name = g_anim_names.particle_hit_1; + } else if (particle_selector == 1) { + particle_desc.anim_name = g_anim_names.particle_hit_2; + } else { + particle_desc.anim_name = g_anim_names.particle_hit_3; + DQN_ASSERT(particle_selector == 2); + } + + particle_desc.pos = Dqn_Rect_InterpolatedPoint(defender_box, Dqn_V2_InitNx2(0.5f, 0.0f)); + particle_desc.velocity.y = -16.f; + particle_desc.velocity_variance.y = (particle_desc.velocity.y * .5f); + particle_desc.velocity_variance.x = DQN_ABS(particle_desc.velocity.y); + particle_desc.colour_begin = TELY_COLOUR_WHITE_V4; + particle_desc.colour_end = TELY_Colour_V4Alpha(TELY_COLOUR_WHITE_V4, 0.f); + particle_desc.duration_ms = 1000; + FP_EmitParticle(&game->play, particle_desc, Dqn_PCG32_Range(&game->play.rng, 1, 3)); + + + // NOTE: God mode override ========================================================= if (game->play.heart == defender->handle) { if (game->play.god_mode) continue; @@ -2127,6 +2180,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input if (god_mode_override) continue; + // NOTE: Do hit logic ============================================================== defender->hp = defender->hp >= attacker->base_attack ? defender->hp - attacker->base_attack : 0; defender->hit_on_clock_ms = game->play.clock_ms; defender->trauma01 = 1.f - (defender->hp / DQN_CAST(Dqn_f32)defender->hp_cap); @@ -2176,6 +2230,17 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input game->play.global_camera_trauma01 = DQN_MAX(0.f, game->play.global_camera_trauma01 - 0.05f); + // NOTE: Update all particles ================================================================== + for (FP_Particle &particle_ : game->play.particles) { + FP_Particle *particle = &particle_; + if (game->play.clock_ms >= particle->end_ms) + particle->alive = false; + if (!particle->alive) + continue; + + particle->pos += particle->velocity * DQN_CAST(Dqn_f32)input->delta_s; + } + // NOTE: Camera ================================================================================ FP_GamePlay *play = &game->play; @@ -2773,6 +2838,39 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer, } } + for (FP_Particle &particle_ : game->play.particles) { + FP_Particle *particle = &particle_; + if (!particle->alive) + continue; + + Dqn_usize elapsed_ms = game->play.clock_ms - particle->start_ms; + Dqn_usize duration_ms = particle->end_ms - particle->start_ms; + + Dqn_f32 t = DQN_MIN(1.f, elapsed_ms / DQN_CAST(Dqn_f32)duration_ms); + Dqn_V4 colour = {}; + colour.r = Dqn_Lerp_F32(particle->colour_begin.r, t, particle->colour_end.r); + colour.b = Dqn_Lerp_F32(particle->colour_begin.b, t, particle->colour_end.b); + colour.g = Dqn_Lerp_F32(particle->colour_begin.g, t, particle->colour_end.g); + colour.a = Dqn_Lerp_F32(particle->colour_begin.a, t, particle->colour_end.a); + + if (particle->anim_name.size) { + TELY_AssetSpriteAnimation *anim = TELY_Asset_GetSpriteAnimation(&game->atlas_sprite_sheet, particle->anim_name); + Dqn_Rect tex_rect = game->atlas_sprite_sheet.rects.data[anim->index]; + Dqn_Rect dest_rect = {}; + dest_rect.size = tex_rect.size; + dest_rect.pos = particle->pos - (tex_rect.size * .5f); + TELY_Render_TextureColourV4(renderer, + game->atlas_sprite_sheet.tex_handle, + tex_rect, + dest_rect, + Dqn_V2_Zero /*rotate origin*/, + 0.f /*rotation*/, + colour); + } else { + TELY_Render_CircleColourV4(renderer, particle->pos, 20.f, TELY_RenderShapeMode_Fill, colour); + } + } + // NOTE: Render overlay UI ===================================================================== if (!game->play.debug_hide_hud && (game->play.state == FP_GameState_Pause || game->play.state == FP_GameState_Play)) { diff --git a/feely_pona.h b/feely_pona.h index f9c43e0..90ae298 100644 --- a/feely_pona.h +++ b/feely_pona.h @@ -88,9 +88,17 @@ struct FP_GlobalAnimations Dqn_String8 merchant_terry = DQN_STRING8("merchant_terry"); Dqn_String8 merchant_terry_menu = DQN_STRING8("merchant_terry_menu"); - Dqn_String8 particle_drunk = DQN_STRING8("particle_drunk"); - Dqn_String8 particle_heart = DQN_STRING8("particle_heart"); - Dqn_String8 particle_purchase = DQN_STRING8("particle_purchase"); + Dqn_String8 particle_church_halo = DQN_STRING8("particle_church_halo"); + Dqn_String8 particle_church_cross = DQN_STRING8("particle_church_cross"); + Dqn_String8 particle_drunk = DQN_STRING8("particle_drunk"); + Dqn_String8 particle_drunk_bottle = DQN_STRING8("particle_drunk_bottle"); + Dqn_String8 particle_drunk_martini = DQN_STRING8("particle_drunk_martini"); + Dqn_String8 particle_heart = DQN_STRING8("particle_heart"); + Dqn_String8 particle_hit_1 = DQN_STRING8("particle_hit_1"); + Dqn_String8 particle_hit_2 = DQN_STRING8("particle_hit_2"); + Dqn_String8 particle_hit_3 = DQN_STRING8("particle_hit_3"); + Dqn_String8 particle_purchase = DQN_STRING8("particle_purchase"); + Dqn_String8 portal = DQN_STRING8("portal"); Dqn_String8 portal_break = DQN_STRING8("portal_break"); Dqn_String8 portal_monk = DQN_STRING8("portal_monk"); diff --git a/feely_pona_game.h b/feely_pona_game.h index b12a765..d082366 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -351,6 +351,18 @@ enum FP_GamePerryJoined FP_GamePerryJoins_PostEnter, }; +struct FP_Particle +{ + Dqn_String8 anim_name; + bool alive; + Dqn_V2 pos; + Dqn_V2 velocity; + Dqn_V4 colour_begin; + Dqn_V4 colour_end; + Dqn_usize start_ms; + Dqn_usize end_ms; +}; + struct FP_GamePlay { TELY_ChunkPool *chunk_pool; @@ -410,6 +422,9 @@ struct FP_GamePlay Dqn_V2 perry_join_splash_screen_pos; uint64_t perry_join_splash_screen_end_ms; Dqn_V2 perry_join_splash_pos_offset; + + FP_Particle particles[256]; + uint32_t particle_next_index; }; struct FP_Game