Compare commits
	
		
			3 Commits
		
	
	
		
			30a69e40e9
			...
			677486e094
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 677486e094 | |||
| 858a66786a | |||
| 468c9995bd | 
							
								
								
									
										2
									
								
								External/tely
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								External/tely
									
									
									
									
										vendored
									
									
								
							| @ -1 +1 @@ | ||||
| Subproject commit b135bb46769657e730b49c173db7476160855984 | ||||
| Subproject commit 96ddc4d93441f1fdccf04d9f7faed96de7971c52 | ||||
							
								
								
									
										620
									
								
								feely_pona.cpp
									
									
									
									
									
								
							
							
						
						
									
										620
									
								
								feely_pona.cpp
									
									
									
									
									
								
							| @ -83,7 +83,8 @@ TELY_AssetSpriteSheet FP_LoadSpriteSheetFromSpec(TELY_Platform *platform, TELY_A | ||||
|             anim->label                     = Dqn_String8_Copy(allocator, anim_name); | ||||
|             anim->index                     = DQN_CAST(uint16_t)sprite_rect_index; | ||||
|             anim->count                     = DQN_CAST(uint16_t)frame_count.value; | ||||
|             anim->seconds_per_frame         = 1.f / frames_per_second.value; | ||||
|             anim->ms_per_frame              = DQN_CAST(uint32_t)(1000.f / frames_per_second.value); | ||||
|             DQN_ASSERT(anim->ms_per_frame != 0); | ||||
|         } else { | ||||
|             DQN_ASSERTF(line_splits.size == 4, "Expected 4 splits for sprite frame lines"); | ||||
|             Dqn_String8ToU64Result x = Dqn_String8_ToU64(line_splits.data[0], 0); | ||||
| @ -106,6 +107,7 @@ TELY_AssetSpriteSheet FP_LoadSpriteSheetFromSpec(TELY_Platform *platform, TELY_A | ||||
| 
 | ||||
|     DQN_ASSERT(result.rects.size == sprite_rect_index); | ||||
|     DQN_ASSERT(result.anims.size == sprite_anim_index); | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| @ -139,26 +141,26 @@ void TELY_DLL_Init(void *user_data) | ||||
|         sheet->sprite_size           = Dqn_V2I_InitNx2(50, 37); | ||||
| 
 | ||||
|         TELY_AssetSpriteAnimation hero_anims[] = { | ||||
|             {DQN_STRING8("Everything"),     /*index*/ 0,   /*count*/ sheet->sprite_count, /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Idle"),           /*index*/ 0,   /*count*/ 3,                   /*seconds_per_frame*/ 1 / 4.f}, | ||||
|             {DQN_STRING8("Run"),            /*index*/ 8,   /*count*/ 6,                   /*seconds_per_frame*/ 1 / 8.f}, | ||||
|             {DQN_STRING8("Jump"),           /*index*/ 14,  /*count*/ 10,                  /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Floor slide"),    /*index*/ 24,  /*count*/ 5,                   /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Unknown"),        /*index*/ 29,  /*count*/ 9,                   /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Attack A"),       /*index*/ 42,  /*count*/ 7,                   /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Attack B"),       /*index*/ 49,  /*count*/ 4,                   /*seconds_per_frame*/ 1 / 8.f}, | ||||
|             {DQN_STRING8("Attack C"),       /*index*/ 53,  /*count*/ 6,                   /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Hurt A"),         /*index*/ 59,  /*count*/ 5,                   /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Hurt B"),         /*index*/ 64,  /*count*/ 5,                   /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Unsheath sword"), /*index*/ 69,  /*count*/ 4,                   /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Sheath sword"),   /*index*/ 73,  /*count*/ 4,                   /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Air drift"),      /*index*/ 77,  /*count*/ 2,                   /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Air drop"),       /*index*/ 79,  /*count*/ 2,                   /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Ladder climb"),   /*index*/ 81,  /*count*/ 4,                   /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Chi push"),       /*index*/ 85,  /*count*/ 8,                   /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Leap slice A"),   /*index*/ 93,  /*count*/ 7,                   /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Leap slice B"),   /*index*/ 100, /*count*/ 3,                   /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Leap slice C"),   /*index*/ 103, /*count*/ 6,                   /*seconds_per_frame*/ 1 / 12.f}, | ||||
|             {DQN_STRING8("Everything"),     /*index*/ 0,   /*count*/ sheet->sprite_count, /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Idle"),           /*index*/ 0,   /*count*/ 3,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 4.f)}, | ||||
|             {DQN_STRING8("Run"),            /*index*/ 8,   /*count*/ 6,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 8.f)}, | ||||
|             {DQN_STRING8("Jump"),           /*index*/ 14,  /*count*/ 10,                  /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Floor slide"),    /*index*/ 24,  /*count*/ 5,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Unknown"),        /*index*/ 29,  /*count*/ 9,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Attack A"),       /*index*/ 42,  /*count*/ 7,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Attack B"),       /*index*/ 49,  /*count*/ 4,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 8.f)}, | ||||
|             {DQN_STRING8("Attack C"),       /*index*/ 53,  /*count*/ 6,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Hurt A"),         /*index*/ 59,  /*count*/ 5,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Hurt B"),         /*index*/ 64,  /*count*/ 5,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Unsheath sword"), /*index*/ 69,  /*count*/ 4,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Sheath sword"),   /*index*/ 73,  /*count*/ 4,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Air drift"),      /*index*/ 77,  /*count*/ 2,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Air drop"),       /*index*/ 79,  /*count*/ 2,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Ladder climb"),   /*index*/ 81,  /*count*/ 4,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Chi push"),       /*index*/ 85,  /*count*/ 8,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Leap slice A"),   /*index*/ 93,  /*count*/ 7,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Leap slice B"),   /*index*/ 100, /*count*/ 3,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|             {DQN_STRING8("Leap slice C"),   /*index*/ 103, /*count*/ 6,                   /*ms_per_frame*/ DQN_CAST(uint32_t)(1000.f / 12.f)}, | ||||
|         }; | ||||
| 
 | ||||
|         game->hero_sprite_anims = Dqn_Slice_Alloc<TELY_AssetSpriteAnimation>(&platform->arena, DQN_ARRAY_UCOUNT(hero_anims), Dqn_ZeroMem_No); | ||||
| @ -167,35 +169,9 @@ void TELY_DLL_Init(void *user_data) | ||||
| 
 | ||||
|     // NOTE: Load sprite sheets ====================================================================
 | ||||
|     { | ||||
|         game->terry_sprite_sheet    = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("terry_resized_25%")); | ||||
|         game->terry_action_mappings = Dqn_Slice_CopyArray<FP_ActionToAnimationMapping>(&platform->arena, { | ||||
|             {&game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, g_anim_names.terry_walk_idle)}, | ||||
|             {&game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, g_anim_names.terry_walk_up)}, | ||||
|             {&game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, g_anim_names.terry_walk_down)}, | ||||
|             {&game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, g_anim_names.terry_walk_left)}, | ||||
|             {&game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, g_anim_names.terry_walk_right)}, | ||||
|             {&game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, g_anim_names.terry_attack_up)}, | ||||
|             {&game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, g_anim_names.terry_attack_side)}, | ||||
|             {&game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, g_anim_names.terry_attack_down)}, | ||||
|         }); | ||||
| 
 | ||||
|         game->terry_merchant_sprite_sheet    = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("terry_merchant_resized_25%")); | ||||
|         game->terry_merchant_action_mappings = Dqn_Slice_CopyArray<FP_ActionToAnimationMapping>(&platform->arena, { | ||||
|             {&game->terry_merchant_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_merchant_sprite_sheet, g_anim_names.terry_merchant)}, | ||||
|         }); | ||||
| 
 | ||||
|         game->smoochie_sprite_sheet    = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("smoochie_resized_25%")); | ||||
|         game->smoochie_action_mappings = Dqn_Slice_CopyArray<FP_ActionToAnimationMapping>(&platform->arena, { | ||||
|             {&game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_walk_down)}, | ||||
|             {&game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_walk_up)}, | ||||
|             {&game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_walk_down)}, | ||||
|             {&game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_walk_left)}, | ||||
|             {&game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_walk_right)}, | ||||
|             {&game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_attack_down)}, | ||||
|             {&game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_attack_side)}, | ||||
|             {&game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_attack_heart)}, | ||||
|             {&game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, g_anim_names.smoochie_death)}, | ||||
|         }); | ||||
|         game->terry_sprite_sheet          = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("terry_resized_25%")); | ||||
|         game->terry_merchant_sprite_sheet = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("terry_merchant_resized_25%")); | ||||
|         game->smoochie_sprite_sheet       = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("smoochie_resized_25%")); | ||||
|     } | ||||
| 
 | ||||
|     game->entities    = Dqn_VArray_Init<FP_GameEntity>(&platform->arena, 1024 * 8); | ||||
| @ -207,7 +183,6 @@ void TELY_DLL_Init(void *user_data) | ||||
|         FP_GameEntity *entity           = FP_Game_MakeEntityPointerF(game, "Terry"); | ||||
|         entity->type                    = FP_EntityType_Terry; | ||||
|         entity->local_pos               = Dqn_V2_InitNx2(1334, 396); | ||||
|         entity->action_to_anim_mapping  = game->terry_action_mappings; | ||||
|         entity->size_scale              = Dqn_V2_InitNx1(0.25f); | ||||
|         entity->local_hit_box_size      = Dqn_V2_InitNx2(428, 471) * entity->size_scale; | ||||
|         entity->flags                  |= FP_GameEntityFlag_Clickable; | ||||
| @ -222,17 +197,16 @@ void TELY_DLL_Init(void *user_data) | ||||
| 
 | ||||
|     // NOTE: Merchant
 | ||||
|     { | ||||
|         FP_GameEntity *entity           = FP_Game_MakeEntityPointerF(game, "Merchant"); | ||||
|         entity->type                    = FP_EntityType_Merchant; | ||||
|         entity->local_pos               = Dqn_V2_InitNx2(1000, 124); | ||||
|         entity->local_hit_box_size      = Dqn_V2_InitNx2(50, 50); | ||||
|         entity->size_scale              = Dqn_V2_InitNx1(0.25f); | ||||
|         entity->action_to_anim_mapping  = game->terry_merchant_action_mappings; | ||||
|         entity->flags                  |= FP_GameEntityFlag_Clickable; | ||||
|         entity->flags                  |= FP_GameEntityFlag_MoveByKeyboard; | ||||
|         entity->flags                  |= FP_GameEntityFlag_MoveByMouse; | ||||
|         entity->flags                  |= FP_GameEntityFlag_MoveByGamepad; | ||||
|         entity->flags                  |= FP_GameEntityFlag_NonTraversable; | ||||
|         FP_GameEntity *entity       = FP_Game_MakeEntityPointerF(game, "Merchant"); | ||||
|         entity->type                = FP_EntityType_Merchant; | ||||
|         entity->local_pos           = Dqn_V2_InitNx2(1000, 124); | ||||
|         entity->local_hit_box_size  = Dqn_V2_InitNx2(50, 50); | ||||
|         entity->size_scale          = Dqn_V2_InitNx1(0.25f); | ||||
|         entity->flags              |= FP_GameEntityFlag_Clickable; | ||||
|         entity->flags              |= FP_GameEntityFlag_MoveByKeyboard; | ||||
|         entity->flags              |= FP_GameEntityFlag_MoveByMouse; | ||||
|         entity->flags              |= FP_GameEntityFlag_MoveByGamepad; | ||||
|         entity->flags              |= FP_GameEntityFlag_NonTraversable; | ||||
|     } | ||||
| 
 | ||||
|     game->tile_size  = 37; | ||||
| @ -318,179 +292,171 @@ void TELY_DLL_Init(void *user_data) | ||||
|     game->test_audio          = platform->func_load_audio(assets, DQN_STRING8("Test Audio"),                  DQN_STRING8("Data/Audio/Purrple Cat - Moonwinds.qoa")); | ||||
| } | ||||
| 
 | ||||
| FP_ActionToAnimationMapping FP_Game_GetActionAnimMappingWithName(FP_Game *game, FP_GameEntityHandle entity_handle, Dqn_String8 name) | ||||
| void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_GameEntity *entity, Dqn_V2 dir_vector) | ||||
| { | ||||
|     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 = {}; | ||||
|     FP_GameEntityAction *action      = &entity->action; | ||||
|     bool const we_are_clicked_entity = entity->handle == game->clicked_entity; | ||||
|     bool const entity_has_velocity   = entity->velocity.x || entity->velocity.y; | ||||
|     bool const entering_new_state    = action->state != action->next_state; | ||||
|     bool const action_has_finished   = !entering_new_state && game->clock_ms >= action->end_at_clock_ms; | ||||
|     action->state                    = action->next_state; | ||||
| 
 | ||||
|     switch (entity->type) { | ||||
|         case FP_EntityType_Nil: { | ||||
|         } break; | ||||
| 
 | ||||
|         case FP_EntityType_Terry: { | ||||
|             FP_EntityTerryState *state = DQN_CAST(FP_EntityTerryState *)&action->state; | ||||
|             if (*state == FP_EntityTerryState_Nil) | ||||
|                 FP_Game_EntityActionSetState(action, FP_EntityTerryState_Idle); | ||||
|             FP_EntityTerryState   *state = DQN_CAST(FP_EntityTerryState *) & action->state; | ||||
|             TELY_AssetSpriteSheet *sheet = &game->terry_sprite_sheet; | ||||
| 
 | ||||
|             if (*state == FP_EntityTerryState_Idle) { | ||||
|                 result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_walk_idle); | ||||
|             switch (*state) { | ||||
|                 case FP_EntityTerryState_Nil: { | ||||
|                     action->next_state = FP_EntityTerryState_Idle; | ||||
|                 } break; | ||||
| 
 | ||||
|                 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)) { | ||||
|                         switch (entity->direction) { | ||||
|                             case FP_GameDirection_Up: { | ||||
|                                 FP_Game_EntityActionSetState(action, FP_EntityTerryState_AttackUp); | ||||
|                             } break; | ||||
|                 case FP_EntityTerryState_Idle: { | ||||
|                     if (entering_new_state) { | ||||
|                         TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.terry_walk_idle, TELY_AssetFlip_No); | ||||
|                         uint64_t duration_ms            = FP_GAME_ENTITY_ACTION_INFINITE_TIMER; | ||||
|                         FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); | ||||
|                     } | ||||
| 
 | ||||
|                             case FP_GameDirection_Left: { | ||||
|                                 FP_Game_EntityActionSetState(action, FP_EntityTerryState_AttackSide); | ||||
|                             } break; | ||||
|                     if (we_are_clicked_entity) { | ||||
|                         if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || | ||||
|                             TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { | ||||
|                             switch (entity->direction) { | ||||
|                                 case FP_GameDirection_Up: { | ||||
|                                     action->next_state = FP_EntityTerryState_AttackUp; | ||||
|                                 } break; | ||||
| 
 | ||||
|                             case FP_GameDirection_Right: { | ||||
|                                 FP_Game_EntityActionSetState(action, FP_EntityTerryState_AttackSide); | ||||
|                             } break; | ||||
|                                 case FP_GameDirection_Left: { | ||||
|                                     action->next_state = FP_EntityTerryState_AttackSide; | ||||
|                                 } break; | ||||
| 
 | ||||
|                             case FP_GameDirection_Down: { | ||||
|                                 FP_Game_EntityActionSetState(action, FP_EntityTerryState_AttackDown); | ||||
|                             } break; | ||||
|                                 case FP_GameDirection_Right: { | ||||
|                                     action->next_state = FP_EntityTerryState_AttackSide; | ||||
|                                 } break; | ||||
| 
 | ||||
|                                 case FP_GameDirection_Down: { | ||||
|                                     action->next_state = FP_EntityTerryState_AttackDown; | ||||
|                                 } break; | ||||
|                             } | ||||
|                         } else if (dir_vector.x || dir_vector.y) { | ||||
|                             action->next_state = FP_EntityTerryState_Run; | ||||
|                         } | ||||
|                     } else if (dir_vector.x || dir_vector.y) { | ||||
|                         FP_Game_EntityActionSetState(action, FP_EntityTerryState_Run); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|                 } break; | ||||
| 
 | ||||
|             if (*state == FP_EntityTerryState_AttackSide) { | ||||
|                 result            = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_attack_side); | ||||
|                 action->flip_on_x = entity->direction == FP_GameDirection_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_EntityTerryState_Idle); | ||||
|                     action->flip_on_x = false; | ||||
|                 } | ||||
| 
 | ||||
|                 #if 0 | ||||
|                 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_EntityTerryState_AttackB); | ||||
|                         else | ||||
|                             action->flags |= FP_GameEntityActionFlag_Failed; | ||||
|                 case FP_EntityTerryState_AttackUp: { | ||||
|                     if (entering_new_state) { | ||||
|                         TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.terry_attack_up, TELY_AssetFlip_No); | ||||
|                         uint64_t duration_ms            = sprite.anim->count * sprite.anim->ms_per_frame; | ||||
|                         FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); | ||||
|                     } | ||||
|                 } | ||||
|                 #endif | ||||
|             } | ||||
| 
 | ||||
|             if (*state == FP_EntityTerryState_AttackUp) { | ||||
|                 result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_attack_up); | ||||
|                 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_EntityTerryState_Idle); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (*state == FP_EntityTerryState_AttackDown) { | ||||
|                 result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_attack_down); | ||||
|                 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_EntityTerryState_Idle); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (*state == FP_EntityTerryState_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)) { | ||||
|                         switch (entity->direction) { | ||||
|                             case FP_GameDirection_Up: { | ||||
|                                 FP_Game_EntityActionSetState(action, FP_EntityTerryState_AttackUp); | ||||
|                             } break; | ||||
| 
 | ||||
|                             case FP_GameDirection_Left: { | ||||
|                                 FP_Game_EntityActionSetState(action, FP_EntityTerryState_AttackSide); | ||||
|                             } break; | ||||
| 
 | ||||
|                             case FP_GameDirection_Right: { | ||||
|                                 FP_Game_EntityActionSetState(action, FP_EntityTerryState_AttackSide); | ||||
|                             } break; | ||||
| 
 | ||||
|                             case FP_GameDirection_Down: { | ||||
|                                 FP_Game_EntityActionSetState(action, FP_EntityTerryState_AttackDown); | ||||
|                             } break; | ||||
|                         } | ||||
|                     } else if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_LeftShift) || | ||||
|                                TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_A)) { | ||||
|                         FP_Game_EntityActionSetState(action, FP_EntityTerryState_Dash); | ||||
|                     if (action_has_finished) { | ||||
|                         action->next_state = FP_EntityTerryState_Idle; | ||||
|                     } | ||||
|                 } | ||||
|                 } break; | ||||
| 
 | ||||
|                 // NOTE: Also handles state transition
 | ||||
|                 if (action->mapping.anim.label != result.anim.label) { | ||||
|                     FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, result); | ||||
|                 } | ||||
|                 case FP_EntityTerryState_AttackDown: { | ||||
|                     if (entering_new_state) { | ||||
|                         TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.terry_attack_down, TELY_AssetFlip_No); | ||||
|                         uint64_t duration_ms             = sprite.anim->count * sprite.anim->ms_per_frame; | ||||
|                         FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); | ||||
|                     } | ||||
| 
 | ||||
|                 if (!entity_has_velocity /*&& !has_collision*/) { | ||||
|                     FP_Game_EntityActionSetState(action, FP_EntityTerryState_Idle); | ||||
|                 } | ||||
|             } | ||||
|                     if (action_has_finished) | ||||
|                         action->next_state = FP_EntityTerryState_Idle; | ||||
|                 } break; | ||||
| 
 | ||||
|             if (*state == FP_EntityTerryState_Dash) { | ||||
|                 result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_walk_right); | ||||
|                 case FP_EntityTerryState_AttackSide: { | ||||
|                     if (entering_new_state) { | ||||
|                         TELY_AssetFlip flip             = entity->direction == FP_GameDirection_Right ? TELY_AssetFlip_X : TELY_AssetFlip_No; | ||||
|                         TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.terry_attack_side, flip); | ||||
|                         uint64_t duration_ms              = sprite.anim->count * sprite.anim->ms_per_frame; | ||||
|                         FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); | ||||
|                     } | ||||
| 
 | ||||
|                 if (action->flags & FP_GameEntityActionFlag_StateTransition) { | ||||
|                     FP_Game_EntityActionReset(action, result.anim.count * result.anim.seconds_per_frame, result); | ||||
|                     if (action_has_finished) | ||||
|                         action->next_state = FP_EntityTerryState_Idle; | ||||
|                 } break; | ||||
| 
 | ||||
|                     Dqn_V2 dash_dir          = {}; | ||||
|                 case FP_EntityTerryState_Run: { | ||||
|                     Dqn_String8 desired_action_name = {}; | ||||
|                     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; | ||||
|                         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; | ||||
|                     } | ||||
| 
 | ||||
|                     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_EntityTerryState_Run); | ||||
|                     } else { | ||||
|                         FP_Game_EntityActionSetState(action, FP_EntityTerryState_Idle); | ||||
|                     if (entering_new_state || action->sprite.anim->label != desired_action_name) { | ||||
|                         TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, desired_action_name, TELY_AssetFlip_No); | ||||
|                         uint64_t duration_ms              = FP_GAME_ENTITY_ACTION_INFINITE_TIMER; | ||||
|                         FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                     if (we_are_clicked_entity) { | ||||
|                         if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || | ||||
|                             TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { | ||||
|                             switch (entity->direction) { | ||||
|                                 case FP_GameDirection_Up: { | ||||
|                                     action->next_state = FP_EntityTerryState_AttackUp; | ||||
|                                 } break; | ||||
| 
 | ||||
|                                 case FP_GameDirection_Left: { | ||||
|                                     action->next_state = FP_EntityTerryState_AttackSide; | ||||
|                                 } break; | ||||
| 
 | ||||
|                                 case FP_GameDirection_Right: { | ||||
|                                     action->next_state = FP_EntityTerryState_AttackSide; | ||||
|                                 } break; | ||||
| 
 | ||||
|                                 case FP_GameDirection_Down: { | ||||
|                                     action->next_state = FP_EntityTerryState_AttackDown; | ||||
|                                 } break; | ||||
|                             } | ||||
|                         } else if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_LeftShift) || | ||||
|                                    TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_A)) { | ||||
|                             action->next_state = FP_EntityTerryState_Dash; | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     if (!entity_has_velocity /*&& !has_collision*/) { | ||||
|                         action->next_state = FP_EntityTerryState_Idle; | ||||
|                     } | ||||
|                 } break; | ||||
| 
 | ||||
|                 case FP_EntityTerryState_Dash: { | ||||
| 
 | ||||
|                     if (entering_new_state) { | ||||
|                         TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.terry_walk_right, TELY_AssetFlip_No); | ||||
|                         uint64_t duration_ms              = sprite.anim->count * sprite.anim->ms_per_frame; | ||||
|                         FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); | ||||
| 
 | ||||
|                         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; | ||||
|                     } | ||||
| 
 | ||||
|                     if (action_has_finished) { | ||||
|                         if (entity_has_velocity) { | ||||
|                             // TODO(doyle): Not sure if this branch triggers properly.
 | ||||
|                             action->next_state = FP_EntityTerryState_Run; | ||||
|                         } else { | ||||
|                             action->next_state = FP_EntityTerryState_Idle; | ||||
|                         } | ||||
|                     } | ||||
|                 } break; | ||||
|             } | ||||
| 
 | ||||
|             if (*state == FP_EntityTerryState_AttackUp || | ||||
| @ -512,92 +478,107 @@ FP_ActionToAnimationMapping FP_EntityActionStateMachine(FP_Game *game, TELY_Plat | ||||
|         } break; | ||||
| 
 | ||||
|         case FP_EntityType_Smoochie: { | ||||
|             FP_EntitySmoochieState *state = DQN_CAST(FP_EntitySmoochieState *)&action->state; | ||||
|             if (*state == FP_EntitySmoochieState_Nil) | ||||
|                 FP_Game_EntityActionSetState(action, FP_EntitySmoochieState_Idle); | ||||
|             FP_EntitySmoochieState *state = DQN_CAST(FP_EntitySmoochieState *) & action->state; | ||||
|             TELY_AssetSpriteSheet  *sheet = &game->smoochie_sprite_sheet; | ||||
| 
 | ||||
|             if (*state == FP_EntitySmoochieState_Idle) { | ||||
|                 result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.smoochie_walk_down); | ||||
|             switch (*state) { | ||||
|                 case FP_EntitySmoochieState_Nil: { | ||||
|                     action->next_state = FP_EntitySmoochieState_Idle; | ||||
|                 } break; | ||||
| 
 | ||||
|                 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)) { | ||||
|                 case FP_EntitySmoochieState_Idle: { | ||||
| 
 | ||||
|                         switch (entity->direction) { | ||||
|                             case FP_GameDirection_Up: /*FALLTHRU*/ | ||||
|                             case FP_GameDirection_Right: /*FALLTHRU*/ | ||||
|                             case FP_GameDirection_Left: { | ||||
|                                 FP_Game_EntityActionSetState(action, FP_EntitySmoochieState_AttackSide); | ||||
|                             } break; | ||||
| 
 | ||||
|                             case FP_GameDirection_Down: { | ||||
|                                 FP_Game_EntityActionSetState(action, FP_EntitySmoochieState_AttackDown); | ||||
|                             } break; | ||||
|                         } | ||||
|                     } else if (dir_vector.x || dir_vector.y) { | ||||
|                         FP_Game_EntityActionSetState(action, FP_EntitySmoochieState_Run); | ||||
|                     if (entering_new_state) { | ||||
|                         TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.smoochie_walk_down, TELY_AssetFlip_No); | ||||
|                         uint64_t duration_ms              = FP_GAME_ENTITY_ACTION_INFINITE_TIMER; | ||||
|                         FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (*state == FP_EntitySmoochieState_AttackDown) { | ||||
|                 result            = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.smoochie_attack_down); | ||||
|                 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_EntitySmoochieState_Idle); | ||||
|                 } | ||||
|             } | ||||
|                     if (we_are_clicked_entity) { | ||||
|                         if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || | ||||
|                             TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { | ||||
| 
 | ||||
|             if (*state == FP_EntitySmoochieState_AttackSide) { | ||||
|                 result            = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.smoochie_attack_side); | ||||
|                 action->flip_on_x = entity->direction == FP_GameDirection_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_EntitySmoochieState_Idle); | ||||
|                     action->flip_on_x = false; | ||||
|                 } | ||||
|             } | ||||
|                             switch (entity->direction) { | ||||
|                                 case FP_GameDirection_Up: /*FALLTHRU*/ | ||||
|                                 case FP_GameDirection_Right: /*FALLTHRU*/ | ||||
|                                 case FP_GameDirection_Left: { | ||||
|                                     action->next_state = FP_EntitySmoochieState_AttackSide; | ||||
|                                 } break; | ||||
| 
 | ||||
|             if (*state == FP_EntitySmoochieState_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)) { | ||||
| 
 | ||||
|                         switch (entity->direction) { | ||||
|                             case FP_GameDirection_Up: /*FALLTHRU*/ | ||||
|                             case FP_GameDirection_Right: /*FALLTHRU*/ | ||||
|                             case FP_GameDirection_Left: { | ||||
|                                 FP_Game_EntityActionSetState(action, FP_EntitySmoochieState_AttackSide); | ||||
|                             } break; | ||||
| 
 | ||||
|                             case FP_GameDirection_Down: { | ||||
|                                 FP_Game_EntityActionSetState(action, FP_EntitySmoochieState_AttackDown); | ||||
|                             } break; | ||||
|                                 case FP_GameDirection_Down: { | ||||
|                                     action->next_state = FP_EntitySmoochieState_AttackDown; | ||||
|                                 } break; | ||||
|                             } | ||||
|                         } else if (dir_vector.x || dir_vector.y) { | ||||
|                             action->next_state = FP_EntitySmoochieState_Run; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 } break; | ||||
| 
 | ||||
|                 // NOTE: Also handles state transition
 | ||||
|                 if (action->mapping.anim.label != result.anim.label) { | ||||
|                     FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, result); | ||||
|                 } | ||||
|                 case FP_EntitySmoochieState_AttackDown: { | ||||
|                     if (entering_new_state) { | ||||
|                         TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.smoochie_attack_down, TELY_AssetFlip_No); | ||||
|                         uint64_t duration_ms              = sprite.anim->count * sprite.anim->ms_per_frame; | ||||
|                         FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); | ||||
|                     } | ||||
| 
 | ||||
|                 if (!entity_has_velocity /*&& !has_collision*/) { | ||||
|                     FP_Game_EntityActionSetState(action, FP_EntitySmoochieState_Idle); | ||||
|                 } | ||||
|                     if (action_has_finished) { | ||||
|                         action->next_state = FP_EntitySmoochieState_Idle; | ||||
|                     } | ||||
|                 } break; | ||||
| 
 | ||||
|                 case FP_EntitySmoochieState_AttackSide: { | ||||
|                     if (entering_new_state) { | ||||
|                         TELY_AssetFlip flip             = entity->direction == FP_GameDirection_Right ? TELY_AssetFlip_X : TELY_AssetFlip_No; | ||||
|                         TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.smoochie_attack_side, flip); | ||||
|                         uint64_t duration_ms              = sprite.anim->count * sprite.anim->ms_per_frame; | ||||
|                         FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); | ||||
|                     } | ||||
| 
 | ||||
|                     if (action_has_finished) | ||||
|                         action->next_state = FP_EntitySmoochieState_Idle; | ||||
|                 } break; | ||||
| 
 | ||||
|                 case FP_EntitySmoochieState_AttackHeart: { | ||||
|                 } break; | ||||
| 
 | ||||
|                 case FP_EntitySmoochieState_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; | ||||
|                     } | ||||
| 
 | ||||
|                     if (entering_new_state || action->sprite.anim->label != desired_action_name) { | ||||
|                         TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, desired_action_name, TELY_AssetFlip_No); | ||||
|                         uint64_t duration_ms              = FP_GAME_ENTITY_ACTION_INFINITE_TIMER; | ||||
|                         FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); | ||||
|                     } | ||||
| 
 | ||||
|                     if (we_are_clicked_entity) { | ||||
|                         if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || | ||||
|                             TELY_Platform_InputGamepadButtonCodeIsPressed(input, 0, TELY_PlatformInputGamepadButtonCode_X)) { | ||||
| 
 | ||||
|                             switch (entity->direction) { | ||||
|                                 case FP_GameDirection_Up: /*FALLTHRU*/ | ||||
|                                 case FP_GameDirection_Right: /*FALLTHRU*/ | ||||
|                                 case FP_GameDirection_Left: { | ||||
|                                     action->next_state = FP_EntitySmoochieState_AttackSide; | ||||
|                                 } break; | ||||
| 
 | ||||
|                                 case FP_GameDirection_Down: { | ||||
|                                     action->next_state = FP_EntitySmoochieState_AttackDown; | ||||
|                                 } break; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     if (!entity_has_velocity /*&& !has_collision*/) { | ||||
|                         action->next_state = FP_EntitySmoochieState_Idle; | ||||
|                     } | ||||
|                 } break; | ||||
|             } | ||||
| 
 | ||||
|             if (*state == FP_EntitySmoochieState_AttackSide || *state == FP_EntitySmoochieState_AttackDown) { | ||||
| @ -613,26 +594,28 @@ FP_ActionToAnimationMapping FP_EntityActionStateMachine(FP_Game *game, TELY_Plat | ||||
|             } else { | ||||
|                 entity->attack_box_size = {}; | ||||
|             } | ||||
| 
 | ||||
|         } break; | ||||
| 
 | ||||
|         case FP_EntityType_Merchant: { | ||||
|             FP_EntityTerryMerchantState *state = DQN_CAST(FP_EntityTerryMerchantState *)&action->state; | ||||
|             if (*state == FP_EntityTerryMerchantState_Nil) | ||||
|                 FP_Game_EntityActionSetState(action, FP_EntityTerryMerchantState_Idle); | ||||
|             TELY_AssetSpriteSheet  *sheet      = &game->terry_merchant_sprite_sheet; | ||||
| 
 | ||||
|             if (*state == FP_EntityTerryMerchantState_Idle) { | ||||
|                 result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_merchant); | ||||
|                 if (action->flags & FP_GameEntityActionFlag_StateTransition) { | ||||
|                     FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, result); | ||||
|                 } | ||||
|             switch (*state) { | ||||
|                 case FP_EntityTerryMerchantState_Nil: { | ||||
|                     action->next_state = FP_EntityTerryMerchantState_Idle; | ||||
|                 } break; | ||||
| 
 | ||||
|                 case FP_EntityTerryMerchantState_Idle: { | ||||
|                     if (entering_new_state) { | ||||
|                         TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.terry_merchant, TELY_AssetFlip_No); | ||||
|                         uint64_t duration_ms            = FP_GAME_ENTITY_ACTION_INFINITE_TIMER; | ||||
|                         FP_Game_EntityActionReset(game, entity->handle, duration_ms, sprite); | ||||
|                     } | ||||
| 
 | ||||
|                 } break; | ||||
|             } | ||||
|         } 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) | ||||
| @ -642,6 +625,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, | ||||
|     if (TELY_Platform_InputKeyIsReleased(input->mouse_left)) | ||||
|         game->clicked_entity = game->prev_active_entity; | ||||
| 
 | ||||
|     game->clock_ms    = DQN_CAST(uint64_t)(platform->input.timer_s * 1000.f); | ||||
|     Dqn_V2 dir_vector = {}; | ||||
| 
 | ||||
|     // NOTE: Keyboard movement input
 | ||||
| @ -887,7 +871,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, | ||||
|         } | ||||
| 
 | ||||
|         // NOTE: Handle input on entity ============================================================
 | ||||
|         FP_ActionToAnimationMapping action_to_anim_mapping = FP_EntityActionStateMachine(game, input, entity, dir_vector); | ||||
|         FP_EntityActionStateMachine(game, input, entity, dir_vector); | ||||
| 
 | ||||
|         // NOTE: Mob spawner =======================================================================
 | ||||
|         if (entity->flags & FP_GameEntityFlag_MobSpawner) { | ||||
| @ -1036,27 +1020,28 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) | ||||
|         } | ||||
| 
 | ||||
|         // NOTE: Render entity sprites =============================================================
 | ||||
|         if (entity->action.mapping.anim.label.size) { | ||||
|             FP_GameEntityAction       const *action       = &entity->action; | ||||
|             TELY_AssetSpriteSheet     const *sprite_sheet = action->mapping.sheet; | ||||
|             TELY_AssetSpriteAnimation const *sprite_anim  = &action->mapping.anim; | ||||
|             uint16_t                         anim_frame   = DQN_CAST(uint16_t)(action->timer_s / sprite_anim->seconds_per_frame) % sprite_anim->count; | ||||
|         if (entity->action.sprite.anim) { | ||||
|             FP_GameEntityAction      const *action = &entity->action; | ||||
|             TELY_AssetAnimatedSprite const sprite  = action->sprite; | ||||
| 
 | ||||
|             Dqn_usize sprite_index = sprite_anim->index + anim_frame; | ||||
|             uint64_t elapsed_ms = game->clock_ms - action->started_at_clock_ms; | ||||
|             uint16_t anim_frame = DQN_CAST(uint16_t)(elapsed_ms / sprite.anim->ms_per_frame) % sprite.anim->count; | ||||
| 
 | ||||
|             Dqn_usize sprite_index = sprite.anim->index + anim_frame; | ||||
|             Dqn_Rect  src_rect     = {}; | ||||
|             switch (sprite_sheet->type) { | ||||
|             switch (sprite.sheet->type) { | ||||
|                 case TELY_AssetSpriteSheetType_Uniform: { | ||||
|                     Dqn_usize sprite_sheet_row    = sprite_index / sprite_sheet->sprites_per_row; | ||||
|                     Dqn_usize sprite_sheet_column = sprite_index % sprite_sheet->sprites_per_row; | ||||
|                     src_rect.pos.x                = DQN_CAST(Dqn_f32)(sprite_sheet_column * sprite_sheet->sprite_size.w); | ||||
|                     src_rect.pos.y                = DQN_CAST(Dqn_f32)(sprite_sheet_row    * sprite_sheet->sprite_size.y); | ||||
|                     src_rect.size.w               = DQN_CAST(Dqn_f32)sprite_sheet->sprite_size.w; | ||||
|                     src_rect.size.h               = DQN_CAST(Dqn_f32)sprite_sheet->sprite_size.h; | ||||
|                     Dqn_usize sprite_sheet_row    = sprite_index / sprite.sheet->sprites_per_row; | ||||
|                     Dqn_usize sprite_sheet_column = sprite_index % sprite.sheet->sprites_per_row; | ||||
|                     src_rect.pos.x                = DQN_CAST(Dqn_f32)(sprite_sheet_column * sprite.sheet->sprite_size.w); | ||||
|                     src_rect.pos.y                = DQN_CAST(Dqn_f32)(sprite_sheet_row    * sprite.sheet->sprite_size.y); | ||||
|                     src_rect.size.w               = DQN_CAST(Dqn_f32)sprite.sheet->sprite_size.w; | ||||
|                     src_rect.size.h               = DQN_CAST(Dqn_f32)sprite.sheet->sprite_size.h; | ||||
|                 } break; | ||||
| 
 | ||||
|                 case TELY_AssetSpriteSheetType_Rects: { | ||||
|                     DQN_ASSERT(sprite_index < sprite_sheet->rects.size); | ||||
|                     src_rect = sprite_sheet->rects.data[sprite_index]; | ||||
|                     DQN_ASSERT(sprite_index < sprite.sheet->rects.size); | ||||
|                     src_rect = sprite.sheet->rects.data[sprite_index]; | ||||
|                 } break; | ||||
|             } | ||||
| 
 | ||||
| @ -1064,10 +1049,13 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) | ||||
|             dest_rect.size     = src_rect.size * entity->size_scale; | ||||
|             dest_rect.pos      = world_pos - (dest_rect.size * .5f); | ||||
| 
 | ||||
|             if (action->flip_on_x) | ||||
|             if (sprite.flip & TELY_AssetFlip_X) | ||||
|                 dest_rect.size.w *= -1.f; // NOTE: Flip the texture horizontally
 | ||||
| 
 | ||||
|             TELY_Render_TextureColourV4(renderer, sprite_sheet->tex_handle, src_rect, dest_rect, TELY_COLOUR_WHITE_V4); | ||||
|             if (sprite.flip & TELY_AssetFlip_Y) | ||||
|                 dest_rect.size.h *= -1.f; // NOTE: Flip the texture vertically
 | ||||
| 
 | ||||
|             TELY_Render_TextureColourV4(renderer, sprite.sheet->tex_handle, src_rect, dest_rect, TELY_COLOUR_WHITE_V4); | ||||
|         } | ||||
| 
 | ||||
|         // NOTE: Render attack box =================================================================
 | ||||
|  | ||||
| @ -425,32 +425,16 @@ static Dqn_Rect FP_Game_CalcEntityWorldBoundingBox(FP_Game *game, FP_GameEntityH | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| // 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, uint32_t state) | ||||
| { | ||||
|     if (!action) | ||||
|         return; | ||||
|     action->state  = state; | ||||
|     action->flags |= FP_GameEntityActionFlag_StateTransition; | ||||
| } | ||||
| 
 | ||||
| // Reset the timers and animation for the current action and set the duration
 | ||||
| // for the new action.
 | ||||
| static void FP_Game_EntityActionReset(FP_GameEntityAction *action, Dqn_f32 new_action_duration, FP_ActionToAnimationMapping mapping) | ||||
| static void FP_Game_EntityActionReset(FP_Game *game, FP_GameEntityHandle entity_handle, uint64_t duration_ms, TELY_AssetAnimatedSprite sprite) | ||||
| { | ||||
|     if (!action) | ||||
|     FP_GameEntity *entity = FP_Game_GetEntity(game, entity_handle); | ||||
|     if (!entity) | ||||
|         return; | ||||
|     action->mapping   = mapping; | ||||
|     action->timer_s   = 0.f; | ||||
|     action->end_at_s  = new_action_duration; | ||||
|     action->flags     = 0; | ||||
| } | ||||
| 
 | ||||
| static bool FP_Game_EntityActionHasFailed(FP_GameEntityAction const *action) | ||||
| { | ||||
|     bool result = action ? action->flags & FP_GameEntityActionFlag_Failed : true; | ||||
|     return result; | ||||
|     entity->action.sprite              = sprite; | ||||
|     entity->action.started_at_clock_ms = game->clock_ms; | ||||
|     entity->action.end_at_clock_ms     = DQN_MAX(duration_ms, game->clock_ms + duration_ms); | ||||
| } | ||||
| 
 | ||||
| static Dqn_V2I FP_Game_WorldPosToTilePos(FP_Game *game, Dqn_V2 world_pos) | ||||
| @ -674,7 +658,6 @@ static FP_GameEntityHandle FP_Game_EntityAddMob(FP_Game *game, Dqn_V2 pos) | ||||
|     entity->type                    = FP_EntityType_Smoochie; | ||||
|     entity->local_pos               = pos; | ||||
|     entity->size_scale              = Dqn_V2_InitNx1(.25f); | ||||
|     entity->action_to_anim_mapping  = game->smoochie_action_mappings; | ||||
|     entity->local_hit_box_size      = Dqn_V2_InitNx2(428, 471) * entity->size_scale; | ||||
|     entity->flags                  |= FP_GameEntityFlag_Clickable; | ||||
|     entity->flags                  |= FP_GameEntityFlag_MoveByKeyboard; | ||||
|  | ||||
| @ -60,27 +60,20 @@ struct FP_GameEntitySpawnList | ||||
|     FP_GameEntitySpawnList *prev; | ||||
| }; | ||||
| 
 | ||||
| enum FP_GameEntityActionFlag | ||||
| { | ||||
|     FP_GameEntityActionFlag_StateTransition = 1 << 0, | ||||
|     FP_GameEntityActionFlag_Failed          = 1 << 1, | ||||
| }; | ||||
| 
 | ||||
| struct FP_ActionToAnimationMapping | ||||
| struct FP_GameEntityActionSprite | ||||
| { | ||||
|     TELY_AssetSpriteSheet     *sheet; | ||||
|     TELY_AssetSpriteAnimation  anim; | ||||
|     TELY_AssetSpriteAnimation *anim; | ||||
| }; | ||||
| 
 | ||||
| Dqn_f32 const FP_GAME_ENTITY_ACTION_INFINITE_TIMER = -1.f; | ||||
| uint64_t const FP_GAME_ENTITY_ACTION_INFINITE_TIMER = UINT64_MAX; | ||||
| struct FP_GameEntityAction | ||||
| { | ||||
|     bool                        flip_on_x; | ||||
|     uint32_t                    state; | ||||
|     uint32_t                    flags; // Bit flags corresponding with `FP_GameEntityActionFlag`
 | ||||
|     FP_ActionToAnimationMapping mapping; | ||||
|     Dqn_f32                     timer_s; | ||||
|     Dqn_f32                     end_at_s; | ||||
|     uint32_t                 state; | ||||
|     uint32_t                 next_state; | ||||
|     TELY_AssetAnimatedSprite sprite; | ||||
|     uint64_t                 started_at_clock_ms; | ||||
|     uint64_t                 end_at_clock_ms; | ||||
| }; | ||||
| 
 | ||||
| enum FP_GameDirection | ||||
| @ -103,8 +96,6 @@ struct FP_GameEntity | ||||
|     Dqn_String8                            name; | ||||
|     FP_GameEntityHandle                    handle; | ||||
| 
 | ||||
|     // TODO(doyle): Deprecate this, it is over engineered and doesn't work
 | ||||
|     Dqn_Slice<FP_ActionToAnimationMapping> action_to_anim_mapping; | ||||
|     Dqn_V2                                 size_scale; | ||||
|     FP_GameEntityAction                    action; | ||||
|     Dqn_V2                                 velocity; | ||||
| @ -167,29 +158,26 @@ struct FP_Game | ||||
|     Dqn_FArray<FP_GameEntityHandle, 8>   parent_entity_stack; | ||||
|     Dqn_VArray<FP_GameEntity>            entities; | ||||
| 
 | ||||
|     TELY_AssetSpriteSheet                   terry_sprite_sheet; | ||||
|     Dqn_Slice<FP_ActionToAnimationMapping>  terry_action_mappings; | ||||
|     TELY_AssetSpriteSheet                 terry_sprite_sheet; | ||||
|     TELY_AssetSpriteSheet                 smoochie_sprite_sheet; | ||||
|     TELY_AssetSpriteSheet                 terry_merchant_sprite_sheet; | ||||
| 
 | ||||
|     TELY_AssetSpriteSheet                   smoochie_sprite_sheet; | ||||
|     Dqn_Slice<FP_ActionToAnimationMapping>  smoochie_action_mappings; | ||||
|     FP_GameEntity                        *root_entity; | ||||
|     FP_GameEntity                        *entity_free_list; | ||||
| 
 | ||||
|     TELY_AssetSpriteSheet                   terry_merchant_sprite_sheet; | ||||
|     Dqn_Slice<FP_ActionToAnimationMapping>  terry_merchant_action_mappings; | ||||
|     FP_GameEntityHandle                   player; | ||||
| 
 | ||||
|     FP_GameEntity                          *root_entity; | ||||
|     FP_GameEntity                          *entity_free_list; | ||||
|     FP_GameEntityHandle                   clicked_entity; | ||||
|     FP_GameEntityHandle                   hot_entity; | ||||
|     FP_GameEntityHandle                   active_entity; | ||||
|     FP_GameEntityHandle                   prev_clicked_entity; | ||||
|     FP_GameEntityHandle                   prev_hot_entity; | ||||
|     FP_GameEntityHandle                   prev_active_entity; | ||||
| 
 | ||||
|     FP_GameEntityHandle                     player; | ||||
|     FP_GameCamera                         camera; | ||||
|     TELY_RFui                             rfui; | ||||
| 
 | ||||
|     FP_GameEntityHandle                     clicked_entity; | ||||
|     FP_GameEntityHandle                     hot_entity; | ||||
|     FP_GameEntityHandle                     active_entity; | ||||
|     FP_GameEntityHandle                     prev_clicked_entity; | ||||
|     FP_GameEntityHandle                     prev_hot_entity; | ||||
|     FP_GameEntityHandle                     prev_active_entity; | ||||
| 
 | ||||
|     FP_GameCamera                           camera; | ||||
|     TELY_RFui                               rfui; | ||||
|     uint64_t                              clock_ms; | ||||
| }; | ||||
| 
 | ||||
| struct FP_GameAStarNode | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user