diff --git a/feely_pona.cpp b/feely_pona.cpp index 32d7e21..44966bc 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -433,16 +433,14 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga if (*state == FP_EntityTerryState_AttackUp || *state == FP_EntityTerryState_AttackDown || *state == FP_EntityTerryState_AttackSide) { + DQN_ASSERT(action->sprite.anim); - // NOTE: Grab timings to narrow down a midpoint of the attack animation - const uint32_t GRACE_PERIOD = 33; uint64_t duration_ms = action->sprite.anim->count * action->sprite.anim->ms_per_frame; + DQN_ASSERT(duration_ms >= PHYSICS_STEP); uint64_t midpoint_clock_ms = action->end_at_clock_ms - (duration_ms / 2); - uint64_t window_start = midpoint_clock_ms - GRACE_PERIOD; - uint64_t window_end = midpoint_clock_ms + GRACE_PERIOD; // NOTE: Adding an attack_processed bool to make sure things only fire once - if (!entity->attack_processed && game->clock_ms >= window_start && game->clock_ms < window_end) { + if (!entity->attack_processed && game->clock_ms >= midpoint_clock_ms) { entity->attack_box_size = entity->local_hit_box_size; // NOTE: Position the attack box @@ -564,6 +562,19 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga case FP_EntitySmoochieState_AttackHeart: { } break; + case FP_EntitySmoochieState_Death: { + if (entering_new_state) { + TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.smoochie_death, TELY_AssetFlip_No); + uint64_t duration_ms = sprite.anim->count * sprite.anim->ms_per_frame; + FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); + } + + if (action_has_finished) { + FP_Game_DeleteEntity(game, entity->handle); + } + } break; + + case FP_EntitySmoochieState_Run: { Dqn_String8 desired_action_name = {}; switch (entity->direction) { @@ -592,6 +603,10 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga } break; } + if (entity->is_dying && *state != FP_EntitySmoochieState_Death) { + action->next_state = FP_EntitySmoochieState_Death; + } + if (*state == FP_EntitySmoochieState_AttackDown) { entity->attack_box_size = entity->local_hit_box_size; // NOTE: Position the attack box @@ -644,6 +659,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga } break; case FP_EntityClingerState_Attack: { + uint32_t asset_flip = {}; Dqn_String8 desired_action_name = {}; switch (entity->direction) { @@ -664,7 +680,16 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga } break; case FP_EntityClingerState_Death: { - // TODO(doyle): Implement + if (entering_new_state) { + TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.clinger_death, TELY_AssetFlip_No); + uint64_t duration_ms = sprite.anim->count * sprite.anim->ms_per_frame; + FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); + } + + if (action_has_finished) { + FP_Game_DeleteEntity(game, entity->handle); + } + } break; case FP_EntityClingerState_Run: { @@ -695,6 +720,10 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga } break; } + if (entity->is_dying && *state != FP_EntityClingerState_Death) { + action->next_state = FP_EntityClingerState_Death; + } + if (*state == FP_EntityClingerState_Attack) { // NOTE: Position the attack box entity->attack_box_size = entity->local_hit_box_size; switch (entity->direction) { @@ -895,25 +924,29 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input // 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; + bool can_attack = !entity->is_dying; - 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; + if (can_attack) { + switch (entity->type) { + case FP_EntityType_Nil: /*FALLTHRU*/ + case FP_EntityType_Merchant: 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; + 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_Clinger: { - entity->action.next_state = FP_EntityClingerState_Attack; - } 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; + + case FP_EntityType_Clinger: { + entity->action.next_state = FP_EntityClingerState_Attack; + } break; + } } // NOTE: Aggro makes the entity attack Terry, we will @@ -1030,7 +1063,6 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input if (target->handle == entity->handle) continue; - // TODO(josh): This check should be updated based on an entity attackable flag if (target->flags & FP_GameEntityFlag_Attackable) { Dqn_Rect target_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, target->handle); @@ -1038,7 +1070,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input target->hp -= 1; if (target->hp <= 0) { - FP_Game_DeleteEntity(game, target->handle); + target->is_dying = true; } } } diff --git a/feely_pona_entity.cpp b/feely_pona_entity.cpp index cea8f48..f00231b 100644 --- a/feely_pona_entity.cpp +++ b/feely_pona_entity.cpp @@ -44,7 +44,8 @@ static FP_GameEntityHandle FP_Entity_CreateClinger(FP_Game *game, Dqn_V2 pos, DQ va_end(args); entity->type = FP_EntityType_Clinger; - entity->hp = 3; + entity->hp = 1; + entity->is_dying = false; entity->local_pos = pos; entity->sprite_height.meters = 1.6f; entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, 1.7f); @@ -64,6 +65,7 @@ static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, D entity->type = FP_EntityType_Smoochie; entity->hp = 1; + entity->is_dying = false; entity->local_pos = pos; entity->sprite_height.meters = 1.6f; entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, 1.6f); diff --git a/feely_pona_entity.h b/feely_pona_entity.h index fa08f36..db62281 100644 --- a/feely_pona_entity.h +++ b/feely_pona_entity.h @@ -30,6 +30,7 @@ enum FP_EntitySmoochieState FP_EntitySmoochieState_AttackDown, FP_EntitySmoochieState_HurtSide, FP_EntitySmoochieState_AttackHeart, + FP_EntitySmoochieState_Death, FP_EntitySmoochieState_Run, }; diff --git a/feely_pona_game.h b/feely_pona_game.h index b9bfb00..26bed44 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -142,6 +142,7 @@ struct FP_GameEntity Dqn_V2 attack_box_size; Dqn_V2 attack_box_offset; bool attack_processed; + bool is_dying; Dqn_FArray spawner_waypoints; FP_SentinelList spawn_list;