diff --git a/feely_pona.cpp b/feely_pona.cpp index 193e94a..23a76eb 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -3,6 +3,23 @@ #include "feely_pona_unity.h" #endif +struct FP_GlobalAnimations +{ + Dqn_String8 terry_walk_idle = DQN_STRING8("terry_walk_idle"); + Dqn_String8 terry_walk_up = DQN_STRING8("terry_walk_up"); + Dqn_String8 terry_walk_down = DQN_STRING8("terry_walk_down"); + Dqn_String8 terry_walk_left = DQN_STRING8("terry_walk_left"); + Dqn_String8 terry_walk_right = DQN_STRING8("terry_walk_right"); + + Dqn_String8 smoochie_walk_up = DQN_STRING8("smoochie_walk_up"); + Dqn_String8 smoochie_walk_down = DQN_STRING8("smoochie_walk_down"); + Dqn_String8 smoochie_walk_left = DQN_STRING8("smoochie_walk_left"); + Dqn_String8 smoochie_walk_right = DQN_STRING8("smoochie_walk_right"); + Dqn_String8 smoochie_attack = DQN_STRING8("smoochie_attack"); + Dqn_String8 smoochie_death = DQN_STRING8("smoochie_death"); +} +g_anim_names; + TELY_AssetSpriteSheet FP_LoadSpriteSheetFromSpec(TELY_Platform *platform, TELY_Assets *assets, Dqn_Arena *arena, Dqn_String8 sheet_name) { TELY_AssetSpriteSheet result = {}; @@ -486,21 +503,22 @@ void TELY_DLL_Init(void *user_data) { game->terry_sprite_sheet = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("terry_walk_resized_25%")); game->terry_action_mappings = Dqn_Slice_CopyArray(&platform->arena, { - {FP_ActionType_Idle, &game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, DQN_STRING8("terry_walk_idle"))}, - {FP_ActionType_WalkUp, &game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, DQN_STRING8("terry_walk_up"))}, - {FP_ActionType_WalkDown, &game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, DQN_STRING8("terry_walk_down"))}, - {FP_ActionType_WalkLeft, &game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, DQN_STRING8("terry_walk_left"))}, - {FP_ActionType_WalkRight, &game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, DQN_STRING8("terry_walk_right"))}, + {FP_ActionType_Idle, &game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, g_anim_names.terry_walk_idle)}, + {FP_ActionType_WalkUp, &game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, g_anim_names.terry_walk_up)}, + {FP_ActionType_WalkDown, &game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, g_anim_names.terry_walk_down)}, + {FP_ActionType_WalkLeft, &game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, g_anim_names.terry_walk_left)}, + {FP_ActionType_WalkRight, &game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, g_anim_names.terry_walk_right)}, }); game->smoochie_sprite_sheet = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("smoochie_resized_25%")); game->smoochie_action_mappings = Dqn_Slice_CopyArray(&platform->arena, { - {FP_ActionType_Idle, &game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, DQN_STRING8("smoochie_walk_down"))}, - {FP_ActionType_WalkUp, &game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, DQN_STRING8("smoochie_walk_up"))}, - {FP_ActionType_WalkDown, &game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, DQN_STRING8("smoochie_walk_down"))}, - {FP_ActionType_WalkLeft, &game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, DQN_STRING8("smoochie_walk_left"))}, - {FP_ActionType_WalkRight, &game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, DQN_STRING8("smoochie_walk_right"))}, - {FP_ActionType_Death, &game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, DQN_STRING8("smoochie_death"))}, + {FP_ActionType_Idle, &game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_walk_down)}, + {FP_ActionType_WalkUp, &game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_walk_up)}, + {FP_ActionType_WalkDown, &game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_walk_down)}, + {FP_ActionType_WalkLeft, &game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_walk_left)}, + {FP_ActionType_WalkRight, &game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_walk_right)}, + {FP_ActionType_Attack, &game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_attack)}, + {FP_ActionType_Death, &game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_death)}, }); } @@ -604,7 +622,7 @@ void TELY_DLL_Init(void *user_data) Dqn_V2I max_tile = platform->core.window_size / game->tile_size; // NOTE: Wall ================================================================================== - { + if (0) { Dqn_V2I vert_wall_tile_size = Dqn_V2I_InitNx2(1, 12); Dqn_V2I right_wall_tile_pos = Dqn_V2I_InitNx2(max_tile.x - vert_wall_tile_size.x - 0, (max_tile.y / 2.f) - (vert_wall_tile_size.y * .5f)); @@ -679,6 +697,252 @@ FP_ActionToAnimationMapping FP_Game_GetActionAnimMappingForType(FP_Game *game, F return result; } +FP_ActionToAnimationMapping FP_Game_GetActionAnimMappingWithName(FP_Game *game, FP_GameEntityHandle entity_handle, Dqn_String8 name) +{ + FP_GameEntity *entity = FP_Game_GetEntity(game, entity_handle); + FP_ActionToAnimationMapping result = {}; + for (FP_ActionToAnimationMapping const &mapping : entity->action_to_anim_mapping) { + if (mapping.anim.label == name) { + result = mapping; + break; + } + } + return result; +} + + +FP_ActionToAnimationMapping FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_GameEntity *entity, Dqn_V2 dir_vector) +{ + FP_GameEntityAction *action = &entity->action; + bool we_are_clicked_entity = entity->handle == game->clicked_entity; + bool action_has_finished = action->timer_s != FP_GAME_ENTITY_ACTION_INFINITE_TIMER && action->timer_s >= action->end_at_s; + bool entity_has_velocity = entity->velocity.x || entity->velocity.y; + FP_ActionToAnimationMapping result = {}; + + switch (entity->type) { + case FP_GameEntityType_Terry: { + FP_GameTerryState *state = DQN_CAST(FP_GameTerryState *)&action->state; + if (*state == FP_GameTerryState_Nil) + FP_Game_EntityActionSetState(action, FP_GameTerryState_Idle); + + if (*state == FP_GameTerryState_Idle) { + result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_walk_idle); + + if (action->flags & FP_GameEntityActionFlag_StateTransition) { + FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, result); + } else if (we_are_clicked_entity) { + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || + TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { + FP_Game_EntityActionSetState(action, FP_GameTerryState_AttackA); + } else if (dir_vector.x || dir_vector.y) { + FP_Game_EntityActionSetState(action, FP_GameTerryState_Run); + } + } + } + + if (*state == FP_GameTerryState_AttackA) { + result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_walk_right); + + if (action->flags & FP_GameEntityActionFlag_StateTransition) { + FP_Game_EntityActionReset(action, result.anim.count * result.anim.seconds_per_frame, result); + } else if (action_has_finished) { + FP_Game_EntityActionSetState(action, FP_GameTerryState_Idle); + } else if (!FP_Game_EntityActionHasFailed(action) && we_are_clicked_entity) { + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || + TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { + Dqn_f32 t01 = action->timer_s / action->end_at_s; + if (t01 > 0.5f) + FP_Game_EntityActionSetState(action, FP_GameTerryState_AttackB); + else + action->flags |= FP_GameEntityActionFlag_Failed; + } + } + } + + if (*state == FP_GameTerryState_AttackB) { + result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_walk_right); + + if (action->flags & FP_GameEntityActionFlag_StateTransition) { + FP_Game_EntityActionReset(action, result.anim.count * result.anim.seconds_per_frame, result); + } else if (action_has_finished) { + FP_Game_EntityActionSetState(action, FP_GameTerryState_Idle); + } else if (!FP_Game_EntityActionHasFailed(action) && we_are_clicked_entity) { + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || + TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { + Dqn_f32 t01 = action->timer_s / action->end_at_s; + if (t01 > 0.5f) + FP_Game_EntityActionSetState(action, FP_GameTerryState_AttackC); + else + action->flags |= FP_GameEntityActionFlag_Failed; + } + } + } + + if (*state == FP_GameTerryState_AttackC) { + result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_walk_right); + + if (action->flags & FP_GameEntityActionFlag_StateTransition) { + FP_Game_EntityActionReset(action, result.anim.count * result.anim.seconds_per_frame, result); + } else if (action_has_finished) { + FP_Game_EntityActionSetState(action, FP_GameTerryState_Idle); + } + } + + if (*state == FP_GameTerryState_Run) { + Dqn_String8 desired_action_name = {}; + switch (entity->direction) { + case FP_GameDirection_Up: desired_action_name = g_anim_names.terry_walk_up; break; + case FP_GameDirection_Down: desired_action_name = g_anim_names.terry_walk_down; break; + case FP_GameDirection_Left: desired_action_name = g_anim_names.terry_walk_left; break; + case FP_GameDirection_Right: desired_action_name = g_anim_names.terry_walk_right; break; + } + result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, desired_action_name); + + if (we_are_clicked_entity) { + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || + TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { + FP_Game_EntityActionSetState(action, FP_GameTerryState_AttackA); + } else if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_LeftShift) || + TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_A)) { + FP_Game_EntityActionSetState(action, FP_GameTerryState_Dash); + } + } + + // NOTE: Also handles state transition + if (action->mapping.action_type != result.action_type) { + FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, result); + } + + if (!entity_has_velocity /*&& !has_collision*/) { + FP_Game_EntityActionSetState(action, FP_GameTerryState_Idle); + } + } + + if (*state == FP_GameTerryState_Dash) { + result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_walk_right); + + if (action->flags & FP_GameEntityActionFlag_StateTransition) { + FP_Game_EntityActionReset(action, result.anim.count * result.anim.seconds_per_frame, result); + + Dqn_V2 dash_dir = {}; + switch (entity->direction) { + case FP_GameDirection_Up: dash_dir.y = -1.f; break; + case FP_GameDirection_Down: dash_dir.y = +1.f; break; + case FP_GameDirection_Left: dash_dir.x = -1.f; break; + case FP_GameDirection_Right: dash_dir.x = +1.f; break; + } + + Dqn_V2 dash_acceleration = dash_dir * 400'000'000.f; + Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(input->delta_s); + entity->velocity = (dash_acceleration * t) + entity->velocity * 2.0f; + + } else if (action_has_finished) { + if (entity_has_velocity) { + // TODO(doyle): Not sure if this branch triggers properly. + FP_Game_EntityActionSetState(action, FP_GameTerryState_Run); + } else { + FP_Game_EntityActionSetState(action, FP_GameTerryState_Idle); + } + } + } + + if (*state == FP_GameTerryState_AttackA || + *state == FP_GameTerryState_AttackB || + *state == FP_GameTerryState_AttackC) { + entity->attack_box_size = entity->local_hit_box_size; + + // NOTE: Position the attack box + if (entity->direction == FP_GameDirection_Left) { + entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x - entity->attack_box_size.w, + entity->local_hit_box_offset.y); + } else { + entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x + entity->local_hit_box_size.w, + entity->local_hit_box_offset.y); + } + } else { + entity->attack_box_size = {}; + } + } break; + + case FP_GameEntityType_Smoochie: { + FP_GameSmoochieState *state = DQN_CAST(FP_GameSmoochieState *)&action->state; + if (*state == FP_GameSmoochieState_Nil) + FP_Game_EntityActionSetState(action, FP_GameSmoochieState_Idle); + + if (*state == FP_GameSmoochieState_Idle) { + result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.smoochie_walk_down); + + if (action->flags & FP_GameEntityActionFlag_StateTransition) { + FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, result); + } else if (we_are_clicked_entity) { + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || + TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { + FP_Game_EntityActionSetState(action, FP_GameSmoochieState_Attack); + } else if (dir_vector.x || dir_vector.y) { + FP_Game_EntityActionSetState(action, FP_GameSmoochieState_Run); + } + } + } + + if (*state == FP_GameSmoochieState_Attack) { + result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.smoochie_attack); + + if (action->flags & FP_GameEntityActionFlag_StateTransition) { + FP_Game_EntityActionReset(action, result.anim.count * result.anim.seconds_per_frame, result); + } else if (action_has_finished) { + FP_Game_EntityActionSetState(action, FP_GameSmoochieState_Idle); + } + } + + if (*state == FP_GameSmoochieState_Run) { + Dqn_String8 desired_action_name = {}; + switch (entity->direction) { + case FP_GameDirection_Up: desired_action_name = g_anim_names.smoochie_walk_up; break; + case FP_GameDirection_Down: desired_action_name = g_anim_names.smoochie_walk_down; break; + case FP_GameDirection_Left: desired_action_name = g_anim_names.smoochie_walk_left; break; + case FP_GameDirection_Right: desired_action_name = g_anim_names.smoochie_walk_right; break; + } + result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, desired_action_name); + + if (we_are_clicked_entity) { + if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || + TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { + FP_Game_EntityActionSetState(action, FP_GameSmoochieState_Attack); + } + } + + // NOTE: Also handles state transition + if (action->mapping.action_type != result.action_type) { + FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, result); + } + + if (!entity_has_velocity /*&& !has_collision*/) { + FP_Game_EntityActionSetState(action, FP_GameSmoochieState_Idle); + } + } + + if (*state == FP_GameSmoochieState_Attack) { + entity->attack_box_size = entity->local_hit_box_size; + // NOTE: Position the attack box + if (entity->direction == FP_GameDirection_Left) { + entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x - entity->attack_box_size.w, + entity->local_hit_box_offset.y); + } else { + entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x + entity->local_hit_box_size.w, + entity->local_hit_box_offset.y); + } + } else { + entity->attack_box_size = {}; + } + + } break; + } + + // NOTE: Tick entity action ================================================================ + action->timer_s += DQN_CAST(Dqn_f32)input->delta_s; + return result; +} + void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, TELY_PlatformInput *input) { Dqn_Profiler_ZoneScopeWithIndex("FP_Update", FP_ProfileZone_FPUpdate); @@ -726,9 +990,21 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, // NOTE: Move entity by keyboard and gamepad =============================================== Dqn_V2 acceleration = {}; - if (game->clicked_entity == entity->handle && - (entity->action.state == FP_GameEntityState_Run || entity->action.state == FP_GameEntityState_Idle)) { - if (entity->flags & (FP_GameEntityFlag_MoveByKeyboard || FP_GameEntityFlag_MoveByGamepad)) { + if (game->clicked_entity == entity->handle) { + if (entity->flags & (FP_GameEntityFlag_MoveByKeyboard | FP_GameEntityFlag_MoveByGamepad)) { + bool move_entity = false; + switch (entity->type) { + case FP_GameEntityType_Terry: { + FP_GameTerryState *state = DQN_CAST(FP_GameTerryState *)&entity->action.state; + move_entity = *state == FP_GameTerryState_Run || *state == FP_GameTerryState_Idle; + } break; + + case FP_GameEntityType_Smoochie: { + FP_GameSmoochieState *state = DQN_CAST(FP_GameSmoochieState *)&entity->action.state; + move_entity = *state == FP_GameSmoochieState_Run || *state == FP_GameSmoochieState_Idle; + } break; + } + acceleration = dir_vector * 10000000.f; if (dir_vector.x) entity->direction = dir_vector.x > 0.f ? FP_GameDirection_Right : FP_GameDirection_Left; @@ -878,7 +1154,6 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, Dqn_V2 pos_just_before_collide = entity_pos + (d * earliest_t); Dqn_V2 new_delta_p = pos_just_before_collide - entity_pos; entity->local_pos += new_delta_p; - entity->velocity = {}; has_collision = true; } } @@ -917,156 +1192,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, } // NOTE: Handle input on entity ============================================================ - FP_GameEntityAction *action = &entity->action; - FP_ActionToAnimationMapping action_to_anim_mapping = {}; - { - bool we_are_clicked_entity = entity->handle == game->clicked_entity; - bool action_has_finished = action->timer_s != FP_GAME_ENTITY_ACTION_INFINITE_TIMER && action->timer_s >= action->end_at_s; - bool entity_has_velocity = entity->velocity.x || entity->velocity.y; - - if (action->state == FP_GameEntityState_Nil) - FP_Game_EntityActionSetState(action, FP_GameEntityState_Idle); - - if (action->state == FP_GameEntityState_Idle) { - action_to_anim_mapping = FP_Game_GetActionAnimMappingForType(game, entity->handle, FP_ActionType_Idle); - - if (action->flags & FP_GameEntityActionFlag_StateTransition) { - FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, action_to_anim_mapping); - } else if (we_are_clicked_entity) { - if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || - TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { - FP_Game_EntityActionSetState(action, FP_GameEntityState_AttackA); - } else if (dir_vector.x || dir_vector.y) { - FP_Game_EntityActionSetState(action, FP_GameEntityState_Run); - } - } - } - - if (action->state == FP_GameEntityState_AttackA) { - action_to_anim_mapping = FP_Game_GetActionAnimMappingForType(game, entity->handle, FP_ActionType_Attack); - - if (action->flags & FP_GameEntityActionFlag_StateTransition) { - FP_Game_EntityActionReset(action, action_to_anim_mapping.anim.count * action_to_anim_mapping.anim.seconds_per_frame, action_to_anim_mapping); - } else if (action_has_finished) { - FP_Game_EntityActionSetState(action, FP_GameEntityState_Idle); - } else if (!FP_Game_EntityActionHasFailed(action) && we_are_clicked_entity) { - if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || - TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { - Dqn_f32 t01 = action->timer_s / action->end_at_s; - if (t01 > 0.5f) - FP_Game_EntityActionSetState(action, FP_GameEntityState_AttackB); - else - action->flags |= FP_GameEntityActionFlag_Failed; - } - } - } - - if (action->state == FP_GameEntityState_AttackB) { - action_to_anim_mapping = FP_Game_GetActionAnimMappingForType(game, entity->handle, FP_ActionType_Attack); - - if (action->flags & FP_GameEntityActionFlag_StateTransition) { - FP_Game_EntityActionReset(action, action_to_anim_mapping.anim.count * action_to_anim_mapping.anim.seconds_per_frame, action_to_anim_mapping); - } else if (action_has_finished) { - FP_Game_EntityActionSetState(action, FP_GameEntityState_Idle); - } else if (!FP_Game_EntityActionHasFailed(action) && we_are_clicked_entity) { - if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || - TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { - Dqn_f32 t01 = action->timer_s / action->end_at_s; - if (t01 > 0.5f) - FP_Game_EntityActionSetState(action, FP_GameEntityState_AttackC); - else - action->flags |= FP_GameEntityActionFlag_Failed; - } - } - } - - if (action->state == FP_GameEntityState_AttackC) { - action_to_anim_mapping = FP_Game_GetActionAnimMappingForType(game, entity->handle, FP_ActionType_Attack); - - if (action->flags & FP_GameEntityActionFlag_StateTransition) { - FP_Game_EntityActionReset(action, action_to_anim_mapping.anim.count * action_to_anim_mapping.anim.seconds_per_frame, action_to_anim_mapping); - } else if (action_has_finished) { - FP_Game_EntityActionSetState(action, FP_GameEntityState_Idle); - } - } - - if (action->state == FP_GameEntityState_Run) { - switch (entity->direction) { - case FP_GameDirection_Up: action_to_anim_mapping = FP_Game_GetActionAnimMappingForType(game, entity->handle, FP_ActionType_WalkUp); break; - case FP_GameDirection_Down: action_to_anim_mapping = FP_Game_GetActionAnimMappingForType(game, entity->handle, FP_ActionType_WalkDown); break; - case FP_GameDirection_Left: action_to_anim_mapping = FP_Game_GetActionAnimMappingForType(game, entity->handle, FP_ActionType_WalkLeft); break; - case FP_GameDirection_Right: action_to_anim_mapping = FP_Game_GetActionAnimMappingForType(game, entity->handle, FP_ActionType_WalkRight); break; - } - - if (we_are_clicked_entity) { - if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || - TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { - FP_Game_EntityActionSetState(action, FP_GameEntityState_AttackA); - } else if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_LeftShift) || - TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_A)) { - FP_Game_EntityActionSetState(action, FP_GameEntityState_Dash); - } - } - - // NOTE: Also handles state transition - if (action->mapping.action_type != action_to_anim_mapping.action_type) { - FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, action_to_anim_mapping); - } - - if (!entity_has_velocity && !has_collision) { - FP_Game_EntityActionSetState(action, FP_GameEntityState_Idle); - } - } - - if (action->state == FP_GameEntityState_Dash) { - action_to_anim_mapping = FP_Game_GetActionAnimMappingForType(game, entity->handle, FP_ActionType_WalkRight); - - if (action->flags & FP_GameEntityActionFlag_StateTransition) { - FP_Game_EntityActionReset(action, action_to_anim_mapping.anim.count * action_to_anim_mapping.anim.seconds_per_frame, action_to_anim_mapping); - - Dqn_V2 dash_dir = {}; - switch (entity->direction) { - case FP_GameDirection_Up: dash_dir.y = -1.f; break; - case FP_GameDirection_Down: dash_dir.y = +1.f; break; - case FP_GameDirection_Left: dash_dir.x = -1.f; break; - case FP_GameDirection_Right: dash_dir.x = +1.f; break; - } - - Dqn_V2 dash_acceleration = dash_dir * 400'000'000.f; - Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(input->delta_s); - entity->velocity = (dash_acceleration * t) + entity->velocity * 2.0f; - - } else if (action_has_finished) { - if (entity_has_velocity) { - // TODO(doyle): Not sure if this branch triggers properly. - FP_Game_EntityActionSetState(action, FP_GameEntityState_Run); - } else { - FP_Game_EntityActionSetState(action, FP_GameEntityState_Idle); - } - } - } - - // NOTE: Tick entity action ================================================================ - action->timer_s += DQN_CAST(Dqn_f32)input->delta_s; - } - - // NOTE: Calculate entity attack box ======================================================= - if (action->state == FP_GameEntityState_AttackA || - action->state == FP_GameEntityState_AttackB || - action->state == FP_GameEntityState_AttackC) { - entity->attack_box_size = entity->local_hit_box_size; - - // NOTE: Position the attack box - if (entity->direction == FP_GameDirection_Left) { - entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x - entity->attack_box_size.w, - entity->local_hit_box_offset.y); - } else { - entity->attack_box_offset = Dqn_V2_InitNx2(entity->local_hit_box_offset.x + entity->local_hit_box_size.w, - entity->local_hit_box_offset.y); - } - } else { - entity->attack_box_size = {}; - } + FP_ActionToAnimationMapping action_to_anim_mapping = FP_EntityActionStateMachine(game, input, entity, dir_vector); // NOTE: Mob spawner ======================================================================= if (entity->flags & FP_GameEntityFlag_MobSpawner) { diff --git a/feely_pona_game.cpp b/feely_pona_game.cpp index af332fc..1178cad 100644 --- a/feely_pona_game.cpp +++ b/feely_pona_game.cpp @@ -424,7 +424,7 @@ static Dqn_Rect FP_Game_CalcEntityWorldBoundingBox(FP_Game *game, FP_GameEntityH // Transition the action into the desire state and set the flag to indicate it // has just transitioned -static void FP_Game_EntityActionSetState(FP_GameEntityAction *action, FP_GameEntityState state) +static void FP_Game_EntityActionSetState(FP_GameEntityAction *action, uint32_t state) { if (!action) return; @@ -668,6 +668,7 @@ static FP_GameEntityHandle FP_Game_EntityAddWallAtTile(FP_Game *game, Dqn_String static FP_GameEntityHandle FP_Game_EntityAddMob(FP_Game *game, Dqn_V2 pos) { FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Mob"); + entity->type = FP_GameEntityType_Smoochie; entity->local_pos = pos; entity->size_scale = Dqn_V2_InitNx1(.25f); entity->action_to_anim_mapping = game->smoochie_action_mappings; diff --git a/feely_pona_game.h b/feely_pona_game.h index c1b27f4..4049980 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -45,17 +45,6 @@ struct FP_GameEntityHandle uint64_t id; }; -enum FP_GameEntityState -{ - FP_GameEntityState_Nil, - FP_GameEntityState_Idle, - FP_GameEntityState_AttackA, - FP_GameEntityState_AttackB, - FP_GameEntityState_AttackC, - FP_GameEntityState_Run, - FP_GameEntityState_Dash, -}; - struct FP_GameWaypoint { Dqn_V2I pos; @@ -90,7 +79,7 @@ struct FP_ActionToAnimationMapping Dqn_f32 const FP_GAME_ENTITY_ACTION_INFINITE_TIMER = -1.f; struct FP_GameEntityAction { - FP_GameEntityState state; + uint32_t state; uint32_t flags; // Bit flags corresponding with `FP_GameEntityActionFlag` FP_ActionToAnimationMapping mapping; Dqn_f32 timer_s; @@ -105,6 +94,31 @@ enum FP_GameDirection FP_GameDirection_Right, }; +enum FP_GameEntityType +{ + FP_GameEntityType_Terry, + FP_GameEntityType_Smoochie, +}; + +enum FP_GameTerryState +{ + FP_GameTerryState_Nil, + FP_GameTerryState_Idle, + FP_GameTerryState_AttackA, + FP_GameTerryState_AttackB, + FP_GameTerryState_AttackC, + FP_GameTerryState_Run, + FP_GameTerryState_Dash, +}; + +enum FP_GameSmoochieState +{ + FP_GameSmoochieState_Nil, + FP_GameSmoochieState_Idle, + FP_GameSmoochieState_Attack, + FP_GameSmoochieState_Run, +}; + struct FP_GameEntity { FP_GameEntity *next; @@ -113,8 +127,11 @@ struct FP_GameEntity FP_GameEntity *last_child; FP_GameEntity *parent; + FP_GameEntityType type; Dqn_String8 name; FP_GameEntityHandle handle; + + // TODO(doyle): Deprecate this, it is over engineered and doesn't work Dqn_Slice action_to_anim_mapping; Dqn_V2 size_scale; FP_GameEntityAction action;