diff --git a/feely_pona.cpp b/feely_pona.cpp index d9af23d..ad16883 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -415,7 +415,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: { @@ -429,7 +429,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; @@ -452,7 +452,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; @@ -475,15 +475,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; @@ -511,9 +511,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; @@ -571,7 +571,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform 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: { @@ -585,14 +585,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; @@ -641,7 +641,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; @@ -654,7 +654,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: { @@ -692,18 +692,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) { @@ -743,7 +743,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform 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: { @@ -756,14 +756,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; @@ -786,7 +786,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: { @@ -821,18 +821,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 @@ -870,7 +870,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform TELY_AssetSpriteSheet *sheet = &game->terry_merchant_sprite_sheet; switch (*state) { case FP_EntityTerryMerchantState_Nil: { - action->next_state = FP_EntityTerryMerchantState_Idle; + FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Idle); } break; case FP_EntityTerryMerchantState_Idle: { @@ -890,7 +890,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform 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: { @@ -916,7 +916,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; } @@ -929,7 +929,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform 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: { @@ -1173,7 +1173,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); @@ -1216,12 +1216,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 8e77c85..b5182e1 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; @@ -191,3 +194,4 @@ static FP_GameEntityHandle FP_Entity_CreateClubTerry(FP_Game *game, Dqn_V2 pos, return result; } + diff --git a/feely_pona_game.cpp b/feely_pona_game.cpp index 7e84ca5..8a37eaa 100644 --- a/feely_pona_game.cpp +++ b/feely_pona_game.cpp @@ -715,3 +715,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 c5ae81a..8495415 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -168,6 +168,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;