transition_function #7

Closed
doylet wants to merge 2 commits from transition_function into main
4 changed files with 83 additions and 33 deletions

View File

@ -415,7 +415,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
switch (*state) { switch (*state) {
case FP_EntityTerryState_Nil: { case FP_EntityTerryState_Nil: {
action->next_state = FP_EntityTerryState_Idle; FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Idle);
} break; } break;
case FP_EntityTerryState_Idle: { 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) || if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) {
} else if (dir_vector.x || dir_vector.y) { } else if (dir_vector.x || dir_vector.y) {
action->next_state = FP_EntityTerryState_Run; FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Run);
} }
} }
} break; } break;
@ -452,7 +452,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
} }
if (action_has_finished) { if (action_has_finished) {
action->next_state = FP_EntityTerryState_Idle; FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Idle);
} }
} break; } break;
@ -475,15 +475,15 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
if (we_are_clicked_entity) { if (we_are_clicked_entity) {
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { 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) || } else if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_LeftShift) ||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_A)) { 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) { if (!entity_has_velocity) {
action->next_state = FP_EntityTerryState_Idle; FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Idle);
} }
} break; } break;
@ -511,9 +511,9 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
if (action_has_finished) { if (action_has_finished) {
if (entity_has_velocity) { if (entity_has_velocity) {
// TODO(doyle): Not sure if this branch triggers properly. // TODO(doyle): Not sure if this branch triggers properly.
action->next_state = FP_EntityTerryState_Run; FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Run);
} else { } else {
action->next_state = FP_EntityTerryState_Idle; FP_Game_EntityTransitionState(game, entity, FP_EntityTerryState_Idle);
} }
} }
} break; } break;
@ -571,7 +571,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
switch (*state) { switch (*state) {
case FP_EntitySmoochieState_Nil: { case FP_EntitySmoochieState_Nil: {
action->next_state = FP_EntitySmoochieState_Idle; FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Idle);
} break; } break;
case FP_EntitySmoochieState_Idle: { 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 (we_are_clicked_entity) {
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { 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) { } 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) { if (entity_has_velocity) {
action->next_state = FP_EntitySmoochieState_Run; FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Run);
} }
} break; } break;
@ -641,7 +641,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
} }
// NOTE: Transition out of the action // NOTE: Transition out of the action
action->next_state = FP_EntitySmoochieState_Idle; FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Idle);
} }
} break; } break;
@ -654,7 +654,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
} }
if (action_has_finished) if (action_has_finished)
action->next_state = FP_EntitySmoochieState_Idle; FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Idle);
} break; } break;
case FP_EntitySmoochieState_AttackHeart: { 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 (we_are_clicked_entity) {
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { 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) { if (!entity_has_velocity) {
action->next_state = FP_EntitySmoochieState_Idle; FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Idle);
} }
} break; } break;
} }
if (entity->is_dying && *state != FP_EntitySmoochieState_Death) { 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) { if (*state == FP_EntitySmoochieState_Attack) {
@ -743,7 +743,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
switch (*state) { switch (*state) {
case FP_EntityClingerState_Nil: { case FP_EntityClingerState_Nil: {
action->next_state = FP_EntityClingerState_Idle; FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Idle);
} break; } break;
case FP_EntityClingerState_Idle: { 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 (we_are_clicked_entity) {
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { 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) { } 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) { if (entity_has_velocity) {
action->next_state = FP_EntityClingerState_Run; FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Run);
} }
} break; } break;
@ -786,7 +786,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
} }
if (action_has_finished) if (action_has_finished)
action->next_state = FP_EntityClingerState_Idle; FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Idle);
} break; } break;
case FP_EntityClingerState_Death: { 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 (we_are_clicked_entity) {
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) ||
TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { 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) { if (!entity_has_velocity) {
action->next_state = FP_EntityClingerState_Idle; FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Idle);
} }
} break; } break;
} }
if (entity->is_dying && *state != FP_EntityClingerState_Death) { 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 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; TELY_AssetSpriteSheet *sheet = &game->terry_merchant_sprite_sheet;
switch (*state) { switch (*state) {
case FP_EntityTerryMerchantState_Nil: { case FP_EntityTerryMerchantState_Nil: {
action->next_state = FP_EntityTerryMerchantState_Idle; FP_Game_EntityTransitionState(game, entity, FP_EntityClingerState_Idle);
} break; } break;
case FP_EntityTerryMerchantState_Idle: { case FP_EntityTerryMerchantState_Idle: {
@ -890,7 +890,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
switch (*state) { switch (*state) {
case FP_EntityClubTerryState_Nil: { case FP_EntityClubTerryState_Nil: {
action->next_state = FP_EntityClubTerryState_Idle; FP_Game_EntityTransitionState(game, entity, FP_EntityClubTerryState_Idle);
} break; } break;
case FP_EntityClubTerryState_Idle: { 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; patron->base_acceleration_per_s.meters *= .5f;
entity->club_terry_patron = {}; entity->club_terry_patron = {};
} }
action->next_state = FP_EntityClubTerryState_Idle; FP_Game_EntityTransitionState(game, entity, FP_EntityClubTerryState_Idle);
} }
} break; } break;
} }
@ -929,7 +929,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_Audio *audio, TELY_Platform
switch (*state) { switch (*state) {
case FP_EntityMapState_Nil: { case FP_EntityMapState_Nil: {
action->next_state = FP_EntityMapState_Idle; FP_Game_EntityTransitionState(game, entity, FP_EntityMapState_Idle);
} break; } break;
case FP_EntityMapState_Idle: { 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; FP_GameEntity *club = waypoint_entity;
if (FP_Game_IsNilEntityHandle(game, club->club_terry_patron)) { if (FP_Game_IsNilEntityHandle(game, club->club_terry_patron)) {
club->club_terry_patron = entity->handle; 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; entity->flags |= FP_GameEntityFlag_PartyingAtClubTerry;
Dqn_Rect hit_box = FP_Game_CalcEntityWorldHitBox(game, club->handle); 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 // TODO(doyle): We should check if it's valid to enter this new state
// from the entity's current state // from the entity's current state
if (entity->type == FP_EntityType_Terry) { 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) { } else if (entity->type == FP_EntityType_Smoochie) {
entity->action.next_state = FP_EntitySmoochieState_Attack; FP_Game_EntityTransitionState(game, entity, FP_EntitySmoochieState_Attack);
} else { } else {
DQN_ASSERT(entity->type == FP_EntityType_Clinger); 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; 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->base_acceleration_per_s.meters = 8.f;
entity->local_pos = pos; entity->local_pos = pos;
entity->sprite_height.meters = 1.6f; 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); entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, entity->sprite_height.meters * .5f);
FP_Entity_AddDebugEditorFlags(game, entity->handle); 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->is_dying = false;
entity->local_pos = pos; entity->local_pos = pos;
entity->sprite_height.meters = 1.6f; 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); entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, entity->sprite_height.meters * .6f);
FP_Entity_AddDebugEditorFlags(game, entity->handle); FP_Entity_AddDebugEditorFlags(game, entity->handle);
entity->flags |= FP_GameEntityFlag_NonTraversable; 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->local_pos = pos;
entity->base_acceleration_per_s.meters = 16.f; entity->base_acceleration_per_s.meters = 16.f;
entity->sprite_height.meters = 1.8f; 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); entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.5f, entity->sprite_height.meters * .6f);
FP_Entity_AddDebugEditorFlags(game, result); FP_Entity_AddDebugEditorFlags(game, result);
entity->flags |= FP_GameEntityFlag_NonTraversable; entity->flags |= FP_GameEntityFlag_NonTraversable;
@ -191,3 +194,4 @@ static FP_GameEntityHandle FP_Entity_CreateClubTerry(FP_Game *game, Dqn_V2 pos,
return result; return result;
} }

View File

@ -715,3 +715,47 @@ FP_GameFindClosestEntityResult FP_Game_FindClosestEntityWithType(FP_Game *game,
return result; 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 (entity->type) {
case FP_EntityType_Terry: {
if (desired_state == FP_EntityTerryState_Attack) {
if (!FP_Game_CanEntityAttack(entity, game->clock_ms)) {
// NOTE: Cooldown not met do not transition
return;
}
entity->last_attack_timestamp = game->clock_ms;
}
} break;
case FP_EntityType_Smoochie: {
if (desired_state == FP_EntitySmoochieState_Attack) {
if (!FP_Game_CanEntityAttack(entity, game->clock_ms)) {
// NOTE: Cooldown not met do not transition
return;
}
entity->last_attack_timestamp = game->clock_ms;
}
} break;
case FP_EntityType_Clinger: {
if (desired_state == FP_EntityClingerState_Attack) {
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; Dqn_V2 attack_box_offset;
bool attack_processed; bool attack_processed;
bool is_dying; bool is_dying;
uint64_t last_attack_timestamp;
uint64_t attack_cooldown_ms;
Dqn_FArray<Dqn_V2, 8> spawner_waypoints; Dqn_FArray<Dqn_V2, 8> spawner_waypoints;
FP_SentinelList<FP_GameEntityHandle> spawn_list; FP_SentinelList<FP_GameEntityHandle> spawn_list;