diff --git a/External/tely b/External/tely index b135bb4..92ae772 160000 --- a/External/tely +++ b/External/tely @@ -1 +1 @@ -Subproject commit b135bb46769657e730b49c173db7476160855984 +Subproject commit 92ae772b6fa8f79bb0d5a93b643305fe482a6ed3 diff --git a/feely_pona.cpp b/feely_pona.cpp index b90b227..b339f11 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -106,6 +106,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; } @@ -167,35 +168,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(&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(&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(&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(&platform->arena, 1024 * 8); @@ -207,7 +182,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 +196,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,35 +291,25 @@ 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) -{ - 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_GameEntityActionSprite 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_GameEntityActionSprite result = {}; switch (entity->type) { case FP_EntityType_Terry: { - FP_EntityTerryState *state = DQN_CAST(FP_EntityTerryState *)&action->state; + FP_EntityTerryState *state = DQN_CAST(FP_EntityTerryState *) & action->state; + TELY_AssetSpriteSheet *sheet = &game->terry_sprite_sheet; + result.sheet = sheet; + if (*state == FP_EntityTerryState_Nil) FP_Game_EntityActionSetState(action, FP_EntityTerryState_Idle); if (*state == FP_EntityTerryState_Idle) { - result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_walk_idle); + result.anim = TELY_Asset_GetSpriteAnimation(sheet, g_anim_names.terry_walk_idle); if (action->flags & FP_GameEntityActionFlag_StateTransition) { FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, result); @@ -377,11 +340,11 @@ FP_ActionToAnimationMapping FP_EntityActionStateMachine(FP_Game *game, TELY_Plat } if (*state == FP_EntityTerryState_AttackSide) { - result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_attack_side); + result.anim = TELY_Asset_GetSpriteAnimation(sheet, 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); + 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; @@ -402,18 +365,18 @@ FP_ActionToAnimationMapping FP_EntityActionStateMachine(FP_Game *game, TELY_Plat } if (*state == FP_EntityTerryState_AttackUp) { - result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_attack_up); + result.anim = TELY_Asset_GetSpriteAnimation(sheet, 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); + 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); + result.anim = TELY_Asset_GetSpriteAnimation(sheet, g_anim_names.terry_walk_down); if (action->flags & FP_GameEntityActionFlag_StateTransition) { - FP_Game_EntityActionReset(action, result.anim.count * result.anim.seconds_per_frame, result); + 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); } @@ -427,7 +390,7 @@ FP_ActionToAnimationMapping FP_EntityActionStateMachine(FP_Game *game, TELY_Plat 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); + result.anim = TELY_Asset_GetSpriteAnimation(sheet, desired_action_name); if (we_are_clicked_entity) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || @@ -456,7 +419,7 @@ FP_ActionToAnimationMapping FP_EntityActionStateMachine(FP_Game *game, TELY_Plat } // NOTE: Also handles state transition - if (action->mapping.anim.label != result.anim.label) { + if (action->sprite.anim->label != result.anim->label) { FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, result); } @@ -466,10 +429,10 @@ FP_ActionToAnimationMapping FP_EntityActionStateMachine(FP_Game *game, TELY_Plat } if (*state == FP_EntityTerryState_Dash) { - result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_walk_right); + result.anim = TELY_Asset_GetSpriteAnimation(sheet, 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); + FP_Game_EntityActionReset(action, result.anim->count * result.anim->seconds_per_frame, result); Dqn_V2 dash_dir = {}; switch (entity->direction) { @@ -512,12 +475,15 @@ FP_ActionToAnimationMapping FP_EntityActionStateMachine(FP_Game *game, TELY_Plat } break; case FP_EntityType_Smoochie: { - FP_EntitySmoochieState *state = DQN_CAST(FP_EntitySmoochieState *)&action->state; + FP_EntitySmoochieState *state = DQN_CAST(FP_EntitySmoochieState *) & action->state; + TELY_AssetSpriteSheet *sheet = &game->smoochie_sprite_sheet; + result.sheet = sheet; + if (*state == FP_EntitySmoochieState_Nil) FP_Game_EntityActionSetState(action, FP_EntitySmoochieState_Idle); if (*state == FP_EntitySmoochieState_Idle) { - result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.smoochie_walk_down); + result.anim = TELY_Asset_GetSpriteAnimation(sheet, g_anim_names.smoochie_walk_down); if (action->flags & FP_GameEntityActionFlag_StateTransition) { FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, result); @@ -543,19 +509,19 @@ FP_ActionToAnimationMapping FP_EntityActionStateMachine(FP_Game *game, TELY_Plat } if (*state == FP_EntitySmoochieState_AttackDown) { - result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.smoochie_attack_down); + result.anim = TELY_Asset_GetSpriteAnimation(sheet, 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); + 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 (*state == FP_EntitySmoochieState_AttackSide) { - result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.smoochie_attack_side); + result.anim = TELY_Asset_GetSpriteAnimation(sheet, 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); + 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; @@ -570,7 +536,7 @@ FP_ActionToAnimationMapping FP_EntityActionStateMachine(FP_Game *game, TELY_Plat 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); + result.anim = TELY_Asset_GetSpriteAnimation(sheet, desired_action_name); if (we_are_clicked_entity) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J) || @@ -591,7 +557,7 @@ FP_ActionToAnimationMapping FP_EntityActionStateMachine(FP_Game *game, TELY_Plat } // NOTE: Also handles state transition - if (action->mapping.anim.label != result.anim.label) { + if (action->sprite.anim->label != result.anim->label) { FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, result); } @@ -618,11 +584,14 @@ FP_ActionToAnimationMapping FP_EntityActionStateMachine(FP_Game *game, TELY_Plat case FP_EntityType_Merchant: { FP_EntityTerryMerchantState *state = DQN_CAST(FP_EntityTerryMerchantState *)&action->state; + TELY_AssetSpriteSheet *sheet = &game->terry_merchant_sprite_sheet; + result.sheet = sheet; + if (*state == FP_EntityTerryMerchantState_Nil) FP_Game_EntityActionSetState(action, FP_EntityTerryMerchantState_Idle); if (*state == FP_EntityTerryMerchantState_Idle) { - result = FP_Game_GetActionAnimMappingWithName(game, entity->handle, g_anim_names.terry_merchant); + result.anim = TELY_Asset_GetSpriteAnimation(sheet, g_anim_names.terry_merchant); if (action->flags & FP_GameEntityActionFlag_StateTransition) { FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, result); } @@ -887,7 +856,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,10 +1005,10 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer) } // NOTE: Render entity sprites ============================================================= - if (entity->action.mapping.anim.label.size) { + if (entity->action.sprite.anim) { FP_GameEntityAction const *action = &entity->action; - TELY_AssetSpriteSheet const *sprite_sheet = action->mapping.sheet; - TELY_AssetSpriteAnimation const *sprite_anim = &action->mapping.anim; + TELY_AssetSpriteSheet const *sprite_sheet = action->sprite.sheet; + TELY_AssetSpriteAnimation const *sprite_anim = action->sprite.anim; uint16_t anim_frame = DQN_CAST(uint16_t)(action->timer_s / sprite_anim->seconds_per_frame) % sprite_anim->count; Dqn_usize sprite_index = sprite_anim->index + anim_frame; diff --git a/feely_pona_game.cpp b/feely_pona_game.cpp index 99f84ea..7a21e91 100644 --- a/feely_pona_game.cpp +++ b/feely_pona_game.cpp @@ -437,14 +437,14 @@ static void FP_Game_EntityActionSetState(FP_GameEntityAction *action, uint32_t s // 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_GameEntityAction *action, Dqn_f32 new_action_duration, FP_GameEntityActionSprite sprite) { if (!action) return; - action->mapping = mapping; - action->timer_s = 0.f; - action->end_at_s = new_action_duration; - action->flags = 0; + action->sprite = sprite; + action->timer_s = 0.f; + action->end_at_s = new_action_duration; + action->flags = 0; } static bool FP_Game_EntityActionHasFailed(FP_GameEntityAction const *action) @@ -674,7 +674,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; diff --git a/feely_pona_game.h b/feely_pona_game.h index 0cb8484..5e17c98 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -66,21 +66,21 @@ enum FP_GameEntityActionFlag 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; 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; + bool flip_on_x; + uint32_t state; + uint32_t flags; // Bit flags corresponding with `FP_GameEntityActionFlag` + FP_GameEntityActionSprite sprite; + Dqn_f32 timer_s; + Dqn_f32 end_at_s; }; enum FP_GameDirection @@ -103,8 +103,6 @@ struct FP_GameEntity 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; Dqn_V2 velocity; @@ -167,29 +165,24 @@ struct FP_Game Dqn_FArray parent_entity_stack; Dqn_VArray entities; - TELY_AssetSpriteSheet terry_sprite_sheet; - Dqn_Slice 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 smoochie_action_mappings; + FP_GameEntity *root_entity; + FP_GameEntity *entity_free_list; - TELY_AssetSpriteSheet terry_merchant_sprite_sheet; - Dqn_Slice 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_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; + FP_GameCamera camera; + TELY_RFui rfui; }; struct FP_GameAStarNode