diff --git a/feely_pona.cpp b/feely_pona.cpp index ebc7d0e..24b6d51 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -1995,26 +1995,8 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input } if (entity->flags & FP_GameEntityFlag_CameraTracking) { - FP_GameCamera *camera = &game->play.camera; - - // NOTE: calculate camera position based on camera shake - Dqn_V2 camera_position = camera->world_pos; - if (camera->shake_duration > 0) { - Dqn_f32 offset_x = (Dqn_PCG32_NextF32(&game->play.rng) * 1000 - 500) * camera->shake_intensity; - Dqn_f32 offset_y = (Dqn_PCG32_NextF32(&game->play.rng) * 1000 - 500) * camera->shake_intensity; - - camera_position.x += offset_x; - camera_position.y += offset_y; - - camera->shake_duration -= input->delta_s; - } - - Dqn_f64 camera_smoothing = 5.0f; - - camera->world_pos.x += (camera_position.x - camera->world_pos.x) * (camera_smoothing * input->delta_s); - camera->world_pos.y += (camera_position.y - camera->world_pos.y) * (camera_smoothing * input->delta_s); - - camera->world_pos_target = FP_Game_CalcEntityWorldPos(game, entity->handle) * camera->scale; + game->play.camera_tracking_entity = entity->handle; + game->play.camera.world_pos_target = FP_Game_CalcEntityWorldPos(game, entity->handle) * game->play.camera.scale; } FP_GamePlaceableBuilding placeable_building = PLACEABLE_BUILDINGS[game->play.build_mode_building_index]; @@ -2149,6 +2131,8 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input } } + entity->trauma01 = DQN_MAX(0.f, entity->trauma01 - 0.05f); + // NOTE: Derive dynmamic bounding boxes ==================================================== if (entity->flags & FP_GameEntityFlag_DeriveHitBoxFromChildrenBoundingBox) { Dqn_Rect children_bbox = {}; @@ -2262,13 +2246,9 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input continue; } - defender->hp = defender->hp >= attacker->base_attack ? defender->hp - attacker->base_attack : 0; - defender->hit_on_frame = game->play.update_counter; - - if (game->play.player == defender->handle) { - game->play.camera.shake_intensity = 0.1f; - game->play.camera.shake_duration = 0.25f; - } + defender->hp = defender->hp >= attacker->base_attack ? defender->hp - attacker->base_attack : 0; + defender->hit_on_clock_ms = game->play.update_counter; + defender->trauma01 = 1.f - (defender->hp / DQN_CAST(Dqn_f32)defender->hp_cap); if (defender->hp <= 0) { if (!defender->is_dying) { @@ -2352,7 +2332,29 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer, TELY_RFui_PushFont(rfui, game->jetbrains_mono_font); TELY_RFui_PushLabelColourV4(rfui, TELY_COLOUR_BLACK_MIDNIGHT_V4); - FP_GameCameraM2x3 camera_xforms = FP_Game_CameraModelViewM2x3(game->play.camera); + FP_GameCamera shake_camera = game->play.camera; + { + // NOTE: Calculate camera position based on camera shake + FP_GameEntity *camera_entity = FP_Game_GetEntity(game, game->play.camera_tracking_entity); + Dqn_f32 trauma01 = DQN_SQUARED(camera_entity->trauma01); + + if (camera_entity->type == FP_EntityType_Terry) { + // NOTE: The heart shake is trauma^3 to emphasise the severity of losing heart health + FP_GameEntity *heart = FP_Game_GetEntity(game, game->play.heart); + trauma01 = DQN_MAX(trauma01, DQN_SQUARED(heart->trauma01) * heart->trauma01); + } + + Dqn_f32 max_shake_dist = 400.f; + Dqn_f32 half_shake_dist = max_shake_dist * .5f; + Dqn_V2 shake_offset = {}; + shake_offset.x = (Dqn_PCG32_NextF32(&game->play.rng) * max_shake_dist - half_shake_dist) * trauma01; + shake_offset.y = (Dqn_PCG32_NextF32(&game->play.rng) * max_shake_dist - half_shake_dist) * trauma01; + + Dqn_f32 interp_rate = 5.0f * DQN_CAST(Dqn_f32)input->delta_s; + shake_camera.world_pos += shake_offset * interp_rate; + } + + FP_GameCameraM2x3 camera_xforms = FP_Game_CameraModelViewM2x3(shake_camera); TELY_Render_PushTransform(renderer, camera_xforms.model_view); Dqn_V2 world_mouse_p = Dqn_M2x3_MulV2(camera_xforms.view_model, input->mouse_p); @@ -2459,14 +2461,14 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer, dest_rect.size.h *= -1.f; // NOTE: Flip the texture vertically Dqn_V4 sprite_colour = TELY_COLOUR_WHITE_V4; - if (entity->hit_on_frame) { - DQN_ASSERT(game->play.update_counter >= entity->hit_on_frame); - Dqn_usize frames_elapsed_since_hit = game->play.update_counter - entity->hit_on_frame; - Dqn_usize const HIT_CONFIRM_DURATION = 8; - if (frames_elapsed_since_hit < HIT_CONFIRM_DURATION) { + if (entity->hit_on_clock_ms) { + DQN_ASSERT(game->play.clock_ms >= entity->hit_on_clock_ms); + Dqn_usize ms_since_hit = game->play.clock_ms - entity->hit_on_clock_ms; + Dqn_usize const HIT_CONFIRM_DURATION_MS = 8 * 16; + if (ms_since_hit < HIT_CONFIRM_DURATION_MS) { sprite_colour = TELY_COLOUR_RED_V4; - sprite_colour.g = ((1.f / HIT_CONFIRM_DURATION) * frames_elapsed_since_hit); - sprite_colour.b = ((1.f / HIT_CONFIRM_DURATION) * frames_elapsed_since_hit); + sprite_colour.g = ((1.f / HIT_CONFIRM_DURATION_MS) * ms_since_hit); + sprite_colour.b = ((1.f / HIT_CONFIRM_DURATION_MS) * ms_since_hit); } } @@ -2483,7 +2485,6 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer, Dqn_V2 label_p = Dqn_Rect_InterpolatedPoint(dest_rect, Dqn_V2_InitNx2(0, -0.25f)); TELY_Render_TextF(renderer, label_p, Dqn_V2_InitNx2(0.f, 0.1f), "CONVERTED"); } - } DQN_FOR_UINDEX(anim_index, entity->extra_cosmetic_anims.size) { diff --git a/feely_pona_game.h b/feely_pona_game.h index d2c20e0..0cfee92 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -191,11 +191,12 @@ struct FP_GameEntity // NOTE: The entity hit box is positioned at the center of the entity. Dqn_V2 local_hit_box_size; Dqn_V2 local_hit_box_offset; + Dqn_f32 trauma01; Dqn_V2 attack_box_size; Dqn_V2 attack_box_offset; bool attack_processed; - Dqn_usize hit_on_frame; + Dqn_usize hit_on_clock_ms; bool is_dying; uint64_t last_attack_timestamp; uint64_t attack_cooldown_ms; @@ -261,8 +262,6 @@ struct FP_GameCamera Dqn_V2 world_pos_target; Dqn_f32 rotate_rads; Dqn_V2 scale; - Dqn_f32 shake_intensity; - Dqn_f64 shake_duration; }; enum FP_GameAudio @@ -332,6 +331,7 @@ struct FP_GamePlay FP_GameEntityHandle prev_hot_entity; FP_GameEntityHandle prev_active_entity; + FP_GameEntityHandle camera_tracking_entity; FP_GameCamera camera; Dqn_f32 meters_to_pixels; uint64_t clock_ms;