fp: Add cooldowns to attacks

This commit is contained in:
Joshalosh 2023-10-01 16:47:40 +11:00
parent 47cf075c3b
commit 3f8f46027b
4 changed files with 65 additions and 33 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<Dqn_V2, 8> spawner_waypoints;
FP_SentinelList<FP_GameEntityHandle> spawn_list;