From 00365ec86f917814510020365cb2acb4e02aa67a Mon Sep 17 00:00:00 2001 From: Joshalosh Date: Sun, 1 Oct 2023 16:47:40 +1100 Subject: [PATCH] fp: Add cooldowns to attacks --- feely_pona.cpp | 74 ++++++++++++++++++------------------ feely_pona_entity_create.cpp | 3 ++ feely_pona_game.cpp | 26 +++++++++++++ feely_pona_game.h | 2 + 4 files changed, 68 insertions(+), 37 deletions(-) diff --git a/feely_pona.cpp b/feely_pona.cpp index 59b4672..ee89007 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -386,7 +386,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform switch (*state) { case FP_EntityTerryState_Nil: { - action->next_state = FP_EntityTerryState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Idle); } break; case FP_EntityTerryState_Idle: { @@ -400,7 +400,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { } else if (dir_vector.x || dir_vector.y) { - action->next_state = FP_EntityTerryState_Run; + FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Run); } } } break; @@ -423,7 +423,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform } if (action_has_finished) { - action->next_state = FP_EntityTerryState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Idle); } } break; @@ -446,15 +446,15 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform if (we_are_clicked_entity) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { - action->next_state = FP_EntityTerryState_Attack; + FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Attack); } else if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_LeftShift) || TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_A)) { - action->next_state = FP_EntityTerryState_Dash; + FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Dash); } } if (!entity_has_velocity) { - action->next_state = FP_EntityTerryState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Idle); } } break; @@ -482,9 +482,9 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform if (action_has_finished) { if (entity_has_velocity) { // TODO(doyle): Not sure if this branch triggers properly. - action->next_state = FP_EntityTerryState_Run; + FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Run); } else { - action->next_state = FP_EntityTerryState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Idle); } } } break; @@ -540,7 +540,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform FP_EntitySmoochieState *state = DQN_CAST(FP_EntitySmoochieState *) & action->state; switch (*state) { case FP_EntitySmoochieState_Nil: { - action->next_state = FP_EntitySmoochieState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Idle); } break; case FP_EntitySmoochieState_Idle: { @@ -554,14 +554,14 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform if (we_are_clicked_entity) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { - action->next_state = FP_EntitySmoochieState_Attack; + FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Attack); } else if (dir_vector.x || dir_vector.y) { - action->next_state = FP_EntitySmoochieState_Run; + FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Run); } } if (entity_has_velocity) { - action->next_state = FP_EntitySmoochieState_Run; + FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Run); } } break; @@ -612,7 +612,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform } // NOTE: Transition out of the action - action->next_state = FP_EntitySmoochieState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Idle); } } break; @@ -625,7 +625,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform } if (action_has_finished) - action->next_state = FP_EntitySmoochieState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Idle); } break; case FP_EntitySmoochieState_AttackHeart: { @@ -663,18 +663,18 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform if (we_are_clicked_entity) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { - action->next_state = FP_EntitySmoochieState_Attack; + FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Attack); } } if (!entity_has_velocity) { - action->next_state = FP_EntitySmoochieState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Idle); } } break; } if (entity->is_dying && *state != FP_EntitySmoochieState_Death) { - action->next_state = FP_EntitySmoochieState_Death; + FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Death); } if (*state == FP_EntitySmoochieState_Attack) { @@ -712,7 +712,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform FP_EntityClingerState *state = DQN_CAST(FP_EntityClingerState *)&action->state; switch (*state) { case FP_EntityClingerState_Nil: { - action->next_state = FP_EntityClingerState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Idle); } break; case FP_EntityClingerState_Idle: { @@ -725,14 +725,14 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform if (we_are_clicked_entity) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { - action->next_state = FP_EntityClingerState_Attack; + FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Attack); } else if (dir_vector.x || dir_vector.y) { - action->next_state = FP_EntityClingerState_Run; + FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Run); } } if (entity_has_velocity) { - action->next_state = FP_EntityClingerState_Run; + FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Run); } } break; @@ -755,7 +755,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform } if (action_has_finished) - action->next_state = FP_EntityClingerState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Idle); } break; case FP_EntityClingerState_Death: { @@ -790,18 +790,18 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform if (we_are_clicked_entity) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { - action->next_state = FP_EntityClingerState_Attack; + FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Attack); } } if (!entity_has_velocity) { - action->next_state = FP_EntityClingerState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Idle); } } break; } if (entity->is_dying && *state != FP_EntityClingerState_Death) { - action->next_state = FP_EntityClingerState_Death; + FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Death); } if (*state == FP_EntityClingerState_Attack) { // NOTE: Position the attack box @@ -838,7 +838,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform FP_EntityMerchantTerryState *state = DQN_CAST(FP_EntityMerchantTerryState *)&action->state; switch (*state) { case FP_EntityMerchantTerryState_Nil: { - action->next_state = FP_EntityMerchantTerryState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityMerchantTerryState_Idle); } break; case FP_EntityMerchantTerryState_Idle: { @@ -856,7 +856,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform FP_EntityMerchantPhoneCompanyState *state = DQN_CAST(FP_EntityMerchantPhoneCompanyState *)&action->state; switch (*state) { case FP_EntityMerchantPhoneCompanyState_Nil: { - action->next_state = FP_EntityMerchantPhoneCompanyState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityMerchantPhoneCompanyState_Idle); } break; case FP_EntityMerchantPhoneCompanyState_Idle: { @@ -873,7 +873,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform FP_EntityMerchantGymState *state = DQN_CAST(FP_EntityMerchantGymState *)&action->state; switch (*state) { case FP_EntityMerchantGymState_Nil: { - action->next_state = FP_EntityMerchantGymState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityMerchantGymState_Idle); } break; case FP_EntityMerchantGymState_Idle: { @@ -891,7 +891,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform FP_EntityMerchantGraveyardState *state = DQN_CAST(FP_EntityMerchantGraveyardState *)&action->state; switch (*state) { case FP_EntityMerchantGraveyardState_Nil: { - action->next_state = FP_EntityMerchantGraveyardState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityMerchantGraveyardState_Idle); } break; case FP_EntityMerchantGraveyardState_Idle: { @@ -909,7 +909,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform FP_EntityClubTerryState *state = DQN_CAST(FP_EntityClubTerryState *)&action->state; switch (*state) { case FP_EntityClubTerryState_Nil: { - action->next_state = FP_EntityClubTerryState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityClubTerryState_Idle); } break; case FP_EntityClubTerryState_Idle: { @@ -935,7 +935,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform patron->base_acceleration_per_s.meters *= .5f; entity->club_terry_patron = {}; } - action->next_state = FP_EntityClubTerryState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityClubTerryState_Idle); } } break; } @@ -946,7 +946,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform FP_EntityMapState *state = DQN_CAST(FP_EntityMapState *) & action->state; switch (*state) { case FP_EntityMapState_Nil: { - action->next_state = FP_EntityMapState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityMapState_Idle); } break; case FP_EntityMapState_Idle: { @@ -964,7 +964,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform FP_EntityHeartState *state = DQN_CAST(FP_EntityHeartState *) & action->state; switch (*state) { case FP_EntityHeartState_Nil: { - action->next_state = FP_EntityHeartState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityHeartState_Idle); } break; case FP_EntityHeartState_Idle: { @@ -1257,7 +1257,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input FP_GameEntity *club = waypoint_entity; if (FP_Game_IsNilEntityHandle(game, club->club_terry_patron)) { club->club_terry_patron = entity->handle; - club->action.next_state = FP_EntityClubTerryState_PartyTime; + FP_Game_EntityTransitionState(game, club, FP_EntityClubTerryState_PartyTime); entity->flags |= FP_GameEntityFlag_PartyingAtClubTerry; Dqn_Rect hit_box = FP_Game_CalcEntityWorldHitBox(game, club->handle); @@ -1297,12 +1297,12 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input // TODO(doyle): We should check if it's valid to enter this new state // from the entity's current state if (entity->type == FP_EntityType_Terry) { - entity->action.next_state = FP_EntityTerryState_Attack; + FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Attack); } else if (entity->type == FP_EntityType_Smoochie) { - entity->action.next_state = FP_EntitySmoochieState_Attack; + FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Attack); } else { DQN_ASSERT(entity->type == FP_EntityType_Clinger); - entity->action.next_state = FP_EntityClingerState_Attack; + FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Attack); } entity->direction = best_attack_dir; diff --git a/feely_pona_entity_create.cpp b/feely_pona_entity_create.cpp index e7ebb7f..7430467 100644 --- a/feely_pona_entity_create.cpp +++ b/feely_pona_entity_create.cpp @@ -56,6 +56,7 @@ static FP_GameEntityHandle FP_Entity_CreateClinger(FP_Game *game, Dqn_V2 pos, DQ entity->base_acceleration_per_s.meters = 8.f; entity->local_pos = pos; entity->sprite_height.meters = 1.6f; + entity->attack_cooldown_ms = 1000; entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, entity->sprite_height.meters * .5f); FP_Entity_AddDebugEditorFlags(game, entity->handle); @@ -78,6 +79,7 @@ static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, D entity->is_dying = false; entity->local_pos = pos; entity->sprite_height.meters = 1.6f; + entity->attack_cooldown_ms = 1000; entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, entity->sprite_height.meters * .6f); FP_Entity_AddDebugEditorFlags(game, entity->handle); entity->flags |= FP_GameEntityFlag_NonTraversable; @@ -142,6 +144,7 @@ static FP_GameEntityHandle FP_Entity_CreateTerry(FP_Game *game, Dqn_V2 pos, DQN_ entity->local_pos = pos; entity->base_acceleration_per_s.meters = 16.f; entity->sprite_height.meters = 1.8f; + entity->attack_cooldown_ms = 500; entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.5f, entity->sprite_height.meters * .6f); FP_Entity_AddDebugEditorFlags(game, result); entity->flags |= FP_GameEntityFlag_NonTraversable; diff --git a/feely_pona_game.cpp b/feely_pona_game.cpp index 2731ce2..c10e552 100644 --- a/feely_pona_game.cpp +++ b/feely_pona_game.cpp @@ -723,3 +723,29 @@ FP_GameFindClosestEntityResult FP_Game_FindClosestEntityWithType(FP_Game *game, return result; } + +static bool FP_Game_CanEntityAttack(FP_GameEntity *entity, uint64_t current_time_ms) +{ + bool result = (current_time_ms - entity->last_attack_timestamp) >= entity->attack_cooldown_ms; + + return result; +} + +static void FP_Game_EntityTransitionState(FP_Game *game, FP_GameEntity *entity, uint32_t desired_state) +{ + switch (desired_state) { + case FP_EntityClingerState_Attack: { + if (entity->type == FP_EntityType_ClubTerry) { + break; + } + if (!FP_Game_CanEntityAttack(entity, game->clock_ms)) { + // NOTE: Cooldown not met do not transition + return; + } + + entity->last_attack_timestamp = game->clock_ms; + } break; + } + // NOTE: If no returns are hit above we proceed with the state change + entity->action.next_state = desired_state; +} diff --git a/feely_pona_game.h b/feely_pona_game.h index 164d7c1..4ca79a2 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -169,6 +169,8 @@ struct FP_GameEntity Dqn_V2 attack_box_offset; bool attack_processed; bool is_dying; + uint64_t last_attack_timestamp; + uint64_t attack_cooldown_ms; Dqn_FArray spawner_waypoints; FP_SentinelList spawn_list;