tely: Decouple entity from sprite sheet

This commit is contained in:
doyle 2023-09-24 15:44:52 +10:00
parent 481da3a6dd
commit 8568996914
4 changed files with 133 additions and 102 deletions

2
External/tely vendored

@ -1 +1 @@
Subproject commit f874ab4d3d127c7caa9ccdffebdb9ba1edfa5067 Subproject commit b135bb46769657e730b49c173db7476160855984

View File

@ -3,20 +3,12 @@
#include "feely_pona_unity.h" #include "feely_pona_unity.h"
#endif #endif
struct FP_LoadSpriteSheetFromSpecResult TELY_AssetSpriteSheet FP_LoadSpriteSheetFromSpec(TELY_Platform *platform, TELY_Assets *assets, Dqn_Arena *arena, Dqn_String8 sheet_name)
{ {
TELY_AssetSpriteSheet sheet; TELY_AssetSpriteSheet result = {};
Dqn_Slice<TELY_AssetSpriteAnimation> anims; Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena);
}; result.sprite_size = Dqn_V2I_InitNx2(185, 170);
result.type = TELY_AssetSpriteSheetType_Rects;
FP_LoadSpriteSheetFromSpecResult FP_LoadSpriteSheetFromSpec(TELY_Platform *platform, TELY_Assets *assets, Dqn_Arena *arena, Dqn_String8 sheet_name)
{
FP_LoadSpriteSheetFromSpecResult result = {};
TELY_AssetSpriteSheet *sheet = &result.sheet;
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena);
sheet->sprite_size = Dqn_V2I_InitNx2(185, 170);
sheet->type = TELY_AssetSpriteSheetType_Rects;
// NOTE: Load the sprite meta file ========================================================= // NOTE: Load the sprite meta file =========================================================
Dqn_String8 sprite_spec_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s.txt", DQN_STRING_FMT(assets->textures_dir), DQN_STRING_FMT(sheet_name)); Dqn_String8 sprite_spec_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s.txt", DQN_STRING_FMT(assets->textures_dir), DQN_STRING_FMT(sheet_name));
@ -35,13 +27,13 @@ FP_LoadSpriteSheetFromSpecResult FP_LoadSpriteSheetFromSpec(TELY_Platform *platf
// NOTE: Sprite sheet path // NOTE: Sprite sheet path
Dqn_String8 sprite_sheet_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s", DQN_STRING_FMT(assets->textures_dir), DQN_STRING_FMT(line_splits.data[1])); Dqn_String8 sprite_sheet_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s", DQN_STRING_FMT(assets->textures_dir), DQN_STRING_FMT(line_splits.data[1]));
sheet->tex_handle = platform->func_load_texture(assets, sheet_name, sprite_sheet_path); result.tex_handle = platform->func_load_texture(assets, sheet_name, sprite_sheet_path);
DQN_ASSERTF(Dqn_Fs_Exists(sprite_sheet_path), "Required file does not exist '%.*s'", DQN_STRING_FMT(sprite_sheet_path)); DQN_ASSERTF(Dqn_Fs_Exists(sprite_sheet_path), "Required file does not exist '%.*s'", DQN_STRING_FMT(sprite_sheet_path));
// NOTE: Total sprite frame count // NOTE: Total sprite frame count
Dqn_String8ToU64Result total_frame_count = Dqn_String8_ToU64(line_splits.data[2], 0); Dqn_String8ToU64Result total_frame_count = Dqn_String8_ToU64(line_splits.data[2], 0);
DQN_ASSERT(total_frame_count.success); DQN_ASSERT(total_frame_count.success);
sheet->rects = Dqn_Slice_Alloc<Dqn_Rect>(arena, total_frame_count.value, Dqn_ZeroMem_No); result.rects = Dqn_Slice_Alloc<Dqn_Rect>(arena, total_frame_count.value, Dqn_ZeroMem_No);
// NOTE: Total animation count // NOTE: Total animation count
Dqn_String8ToU64Result total_anim_count = Dqn_String8_ToU64(line_splits.data[3], 0); Dqn_String8ToU64Result total_anim_count = Dqn_String8_ToU64(line_splits.data[3], 0);
@ -82,7 +74,7 @@ FP_LoadSpriteSheetFromSpecResult FP_LoadSpriteSheetFromSpec(TELY_Platform *platf
DQN_ASSERT(w.success); DQN_ASSERT(w.success);
DQN_ASSERT(h.success); DQN_ASSERT(h.success);
sheet->rects.data[sprite_rect_index++] = result.rects.data[sprite_rect_index++] =
Dqn_Rect_InitNx4(DQN_CAST(Dqn_f32) x.value, Dqn_Rect_InitNx4(DQN_CAST(Dqn_f32) x.value,
DQN_CAST(Dqn_f32) y.value, DQN_CAST(Dqn_f32) y.value,
DQN_CAST(Dqn_f32) w.value, DQN_CAST(Dqn_f32) w.value,
@ -90,7 +82,7 @@ FP_LoadSpriteSheetFromSpecResult FP_LoadSpriteSheetFromSpec(TELY_Platform *platf
} }
} }
DQN_ASSERT(sheet->rects.size == sprite_rect_index); DQN_ASSERT(result.rects.size == sprite_rect_index);
DQN_ASSERT(result.anims.size == sprite_anim_index); DQN_ASSERT(result.anims.size == sprite_anim_index);
return result; return result;
} }
@ -492,17 +484,24 @@ void TELY_DLL_Init(void *user_data)
// NOTE: Load sprite sheets ==================================================================== // NOTE: Load sprite sheets ====================================================================
{ {
{ game->terry_sprite_sheet = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("terry_walk_resized_25%"));
FP_LoadSpriteSheetFromSpecResult terry = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("terry_walk_resized_25%")); game->terry_action_mappings = Dqn_Slice_CopyArray<FP_ActionToAnimationMapping>(&platform->arena, {
game->terry_sprite_sheet = terry.sheet; {FP_ActionType_Idle, &game->terry_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->terry_sprite_sheet, DQN_STRING8("terry_walk_idle"))},
game->terry_sprite_anims = terry.anims; {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"))},
});
{ game->smoochie_sprite_sheet = FP_LoadSpriteSheetFromSpec(platform, assets, &platform->arena, DQN_STRING8("smoochie_resized_25%"));
FP_LoadSpriteSheetFromSpecResult smoochie = 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 = smoochie.sheet; {FP_ActionType_Idle, &game->smoochie_sprite_sheet, TELY_Asset_GetSpriteAnimation(&game->smoochie_sprite_sheet, DQN_STRING8("smoochie_walk_down"))},
game->smoochie_sprite_anims = smoochie.anims; {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"))},
});
} }
game->entities = Dqn_VArray_Init<FP_GameEntity>(&platform->arena, 1024 * 8); game->entities = Dqn_VArray_Init<FP_GameEntity>(&platform->arena, 1024 * 8);
@ -585,22 +584,19 @@ void TELY_DLL_Init(void *user_data)
// NOTE: Hero // NOTE: Hero
{ {
TELY_AssetSpriteSheet *sheet = &game->terry_sprite_sheet; TELY_AssetSpriteSheet *sheet = &game->terry_sprite_sheet;
Dqn_Slice<TELY_AssetSpriteAnimation> anims = game->terry_sprite_anims; FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Terry");
entity->local_pos = Dqn_V2_InitNx2(1334, 396);
FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Terry"); entity->action_to_anim_mapping = game->terry_action_mappings;
entity->local_pos = Dqn_V2_InitNx2(1334, 396); entity->size_scale = Dqn_V2_InitNx1(0.25f);
entity->sprite_sheet = sheet; entity->local_hit_box_size = Dqn_V2_InitV2I(sheet->sprite_size);
entity->sprite_anims = anims; entity->flags |= FP_GameEntityFlag_Clickable;
entity->size_scale = Dqn_V2_InitNx1(0.5f); entity->flags |= FP_GameEntityFlag_MoveByKeyboard;
entity->local_hit_box_size = Dqn_V2_InitV2I(sheet->sprite_size); entity->flags |= FP_GameEntityFlag_MoveByMouse;
entity->flags |= FP_GameEntityFlag_Clickable; entity->flags |= FP_GameEntityFlag_NonTraversable;
entity->flags |= FP_GameEntityFlag_MoveByKeyboard; entity->facing_left = true;
entity->flags |= FP_GameEntityFlag_MoveByMouse; game->clicked_entity = entity->handle;
entity->flags |= FP_GameEntityFlag_NonTraversable; game->player = entity->handle;
entity->facing_left = true;
game->clicked_entity = entity->handle;
game->player = entity->handle;
} }
game->tile_size = 37; game->tile_size = 37;
@ -668,6 +664,19 @@ 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")); game->test_audio = platform->func_load_audio(assets, DQN_STRING8("Test Audio"), DQN_STRING8("Data/Audio/Purrple Cat - Moonwinds.qoa"));
} }
FP_ActionToAnimationMapping FP_Game_GetActionAnimMappingForType(FP_Game *game, FP_GameEntityHandle entity_handle, FP_ActionType type)
{
FP_GameEntity *entity = FP_Game_GetEntity(game, entity_handle);
FP_ActionToAnimationMapping result = {};
for (FP_ActionToAnimationMapping const &mapping : entity->action_to_anim_mapping) {
if (mapping.action_type == type) {
result = mapping;
break;
}
}
return result;
}
void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, TELY_PlatformInput *input) void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer, TELY_PlatformInput *input)
{ {
Dqn_Profiler_ZoneScopeWithIndex("FP_Update", FP_ProfileZone_FPUpdate); Dqn_Profiler_ZoneScopeWithIndex("FP_Update", FP_ProfileZone_FPUpdate);
@ -695,7 +704,6 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
} }
} }
if (dir_vector.x && dir_vector.y) { if (dir_vector.x && dir_vector.y) {
dir_vector.x *= 0.7071067811865475244f; dir_vector.x *= 0.7071067811865475244f;
dir_vector.y *= 0.7071067811865475244f; dir_vector.y *= 0.7071067811865475244f;
@ -726,6 +734,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
// NOTE: Stalk entity ====================================================================== // NOTE: Stalk entity ======================================================================
Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle); Dqn_V2 entity_world_pos = FP_Game_CalcEntityWorldPos(game, entity->handle);
#if 0
{ {
FP_GameEntity *stalk_entity = FP_Game_GetEntity(game, entity->stalk_entity); FP_GameEntity *stalk_entity = FP_Game_GetEntity(game, entity->stalk_entity);
if (stalk_entity->handle.id) { if (stalk_entity->handle.id) {
@ -774,10 +783,13 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
TELY_ChunkPool_Dealloc(game->chunk_pool, waypoint); TELY_ChunkPool_Dealloc(game->chunk_pool, waypoint);
} else { } else {
Dqn_V2 entity_to_target_pos_norm = Dqn_V2_Normalise(entity_to_target_pos); Dqn_V2 entity_to_target_pos_norm = Dqn_V2_Normalise(entity_to_target_pos);
entity->local_pos += entity_to_target_pos_norm * (entity->local_hit_box_size.x * .05f); if (acceleration.x == 0 && acceleration.y == 0) {
acceleration = entity_to_target_pos_norm * 700'000.f;
}
} }
} }
} }
#endif
// NOTE: Core equations of motion ========================================================== // NOTE: Core equations of motion ==========================================================
{ {
@ -886,7 +898,8 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
} }
// NOTE: Handle input on entity ============================================================ // NOTE: Handle input on entity ============================================================
FP_GameEntityAction *action = &entity->action; FP_GameEntityAction *action = &entity->action;
FP_ActionToAnimationMapping action_to_anim_mapping = {};
{ {
bool we_are_clicked_entity = entity->handle == game->clicked_entity; 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 action_has_finished = action->timer_s != FP_GAME_ENTITY_ACTION_INFINITE_TIMER && action->timer_s >= action->end_at_s;
@ -896,9 +909,10 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
FP_Game_EntityActionSetState(action, FP_GameEntityState_Idle); FP_Game_EntityActionSetState(action, FP_GameEntityState_Idle);
if (action->state == 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) { if (action->flags & FP_GameEntityActionFlag_StateTransition) {
TELY_AssetSpriteAnimation *anim = entity->sprite_anims.data + TELY_Asset_GetSpriteAnimation(entity->sprite_anims, DQN_STRING8("terry_walk_idle")).index; FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, action_to_anim_mapping);
FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, anim);
} else if (we_are_clicked_entity) { } else if (we_are_clicked_entity) {
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) {
FP_Game_EntityActionSetState(action, FP_GameEntityState_AttackA); FP_Game_EntityActionSetState(action, FP_GameEntityState_AttackA);
@ -909,9 +923,10 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
} }
if (action->state == FP_GameEntityState_AttackA) { 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) { if (action->flags & FP_GameEntityActionFlag_StateTransition) {
TELY_AssetSpriteAnimation *anim = entity->sprite_anims.data + TELY_Asset_GetSpriteAnimation(entity->sprite_anims, DQN_STRING8("Attack A")).index; FP_Game_EntityActionReset(action, action_to_anim_mapping.anim.count * action_to_anim_mapping.anim.seconds_per_frame, action_to_anim_mapping);
FP_Game_EntityActionReset(action, anim->count * anim->seconds_per_frame, anim);
} else if (action_has_finished) { } else if (action_has_finished) {
FP_Game_EntityActionSetState(action, FP_GameEntityState_Idle); FP_Game_EntityActionSetState(action, FP_GameEntityState_Idle);
} else if (!FP_Game_EntityActionHasFailed(action) && we_are_clicked_entity) { } else if (!FP_Game_EntityActionHasFailed(action) && we_are_clicked_entity) {
@ -926,9 +941,10 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
} }
if (action->state == FP_GameEntityState_AttackB) { 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) { if (action->flags & FP_GameEntityActionFlag_StateTransition) {
TELY_AssetSpriteAnimation *anim = entity->sprite_anims.data + TELY_Asset_GetSpriteAnimation(entity->sprite_anims, DQN_STRING8("Attack B")).index; FP_Game_EntityActionReset(action, action_to_anim_mapping.anim.count * action_to_anim_mapping.anim.seconds_per_frame, action_to_anim_mapping);
FP_Game_EntityActionReset(action, anim->count * anim->seconds_per_frame, anim);
} else if (action_has_finished) { } else if (action_has_finished) {
FP_Game_EntityActionSetState(action, FP_GameEntityState_Idle); FP_Game_EntityActionSetState(action, FP_GameEntityState_Idle);
} else if (!FP_Game_EntityActionHasFailed(action) && we_are_clicked_entity) { } else if (!FP_Game_EntityActionHasFailed(action) && we_are_clicked_entity) {
@ -943,18 +959,20 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
} }
if (action->state == FP_GameEntityState_AttackC) { 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) { if (action->flags & FP_GameEntityActionFlag_StateTransition) {
TELY_AssetSpriteAnimation *anim = entity->sprite_anims.data + TELY_Asset_GetSpriteAnimation(entity->sprite_anims, DQN_STRING8("Attack C")).index; FP_Game_EntityActionReset(action, action_to_anim_mapping.anim.count * action_to_anim_mapping.anim.seconds_per_frame, action_to_anim_mapping);
FP_Game_EntityActionReset(action, anim->count * anim->seconds_per_frame, anim);
} else if (action_has_finished) { } else if (action_has_finished) {
FP_Game_EntityActionSetState(action, FP_GameEntityState_Idle); FP_Game_EntityActionSetState(action, FP_GameEntityState_Idle);
} }
} }
if (action->state == FP_GameEntityState_Run) { if (action->state == FP_GameEntityState_Run) {
action_to_anim_mapping = FP_Game_GetActionAnimMappingForType(game, entity->handle, FP_ActionType_WalkRight);
if (action->flags & FP_GameEntityActionFlag_StateTransition) { if (action->flags & FP_GameEntityActionFlag_StateTransition) {
TELY_AssetSpriteAnimation *anim = entity->sprite_anims.data + TELY_Asset_GetSpriteAnimation(entity->sprite_anims, DQN_STRING8("terry_walk_right")).index; FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, action_to_anim_mapping);
FP_Game_EntityActionReset(action, FP_GAME_ENTITY_ACTION_INFINITE_TIMER, anim);
} else if (we_are_clicked_entity) { } else if (we_are_clicked_entity) {
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) { if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_J)) {
FP_Game_EntityActionSetState(action, FP_GameEntityState_AttackA); FP_Game_EntityActionSetState(action, FP_GameEntityState_AttackA);
@ -969,9 +987,10 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
} }
if (action->state == FP_GameEntityState_Dash) { 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) { if (action->flags & FP_GameEntityActionFlag_StateTransition) {
TELY_AssetSpriteAnimation *anim = entity->sprite_anims.data + TELY_Asset_GetSpriteAnimation(entity->sprite_anims, DQN_STRING8("Floor slide")).index; FP_Game_EntityActionReset(action, action_to_anim_mapping.anim.count * action_to_anim_mapping.anim.seconds_per_frame, action_to_anim_mapping);
FP_Game_EntityActionReset(action, anim->count * anim->seconds_per_frame, anim);
Dqn_V2 dash_dir = {entity->facing_left ? -1.f : 1.f, 0.f}; Dqn_V2 dash_dir = {entity->facing_left ? -1.f : 1.f, 0.f};
Dqn_V2 dash_acceleration = dash_dir * 400'000'000.f; Dqn_V2 dash_acceleration = dash_dir * 400'000'000.f;
@ -996,11 +1015,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_Renderer *renderer,
if (action->state == FP_GameEntityState_AttackA || if (action->state == FP_GameEntityState_AttackA ||
action->state == FP_GameEntityState_AttackB || action->state == FP_GameEntityState_AttackB ||
action->state == FP_GameEntityState_AttackC) { action->state == FP_GameEntityState_AttackC) {
entity->attack_box_size = entity->local_hit_box_size; entity->attack_box_size = entity->local_hit_box_size;
TELY_AssetSpriteSheet const *sprite_sheet = entity->sprite_sheet;
if (sprite_sheet) {
entity->attack_box_size = Dqn_V2_InitV2I(sprite_sheet->sprite_size);
}
// NOTE: Position the attack box // NOTE: Position the attack box
if (entity->facing_left) { if (entity->facing_left) {
@ -1122,10 +1137,10 @@ void FP_Render(FP_Game *game, TELY_Platform *platform, TELY_Renderer *renderer)
} }
// NOTE: Render entity sprites ============================================================= // NOTE: Render entity sprites =============================================================
if (entity->sprite_sheet && entity->action.anim) { if (entity->action.mapping.anim.label.size) {
TELY_AssetSpriteSheet const *sprite_sheet = entity->sprite_sheet;
FP_GameEntityAction const *action = &entity->action; FP_GameEntityAction const *action = &entity->action;
TELY_AssetSpriteAnimation const *sprite_anim = action->anim; 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; 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; Dqn_usize sprite_index = sprite_anim->index + anim_frame;

View File

@ -434,11 +434,11 @@ static void FP_Game_EntityActionSetState(FP_GameEntityAction *action, FP_GameEnt
// Reset the timers and animation for the current action and set the duration // Reset the timers and animation for the current action and set the duration
// for the new action. // for the new action.
static void FP_Game_EntityActionReset(FP_GameEntityAction *action, Dqn_f32 new_action_duration, TELY_AssetSpriteAnimation *anim) static void FP_Game_EntityActionReset(FP_GameEntityAction *action, Dqn_f32 new_action_duration, FP_ActionToAnimationMapping mapping)
{ {
if (!action) if (!action)
return; return;
action->anim = anim; action->mapping = mapping;
action->timer_s = 0.f; action->timer_s = 0.f;
action->end_at_s = new_action_duration; action->end_at_s = new_action_duration;
action->flags = 0; action->flags = 0;
@ -667,17 +667,16 @@ static FP_GameEntityHandle FP_Game_EntityAddWallAtTile(FP_Game *game, Dqn_String
static FP_GameEntityHandle FP_Game_EntityAddMob(FP_Game *game, Dqn_V2 pos) static FP_GameEntityHandle FP_Game_EntityAddMob(FP_Game *game, Dqn_V2 pos)
{ {
FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Mob"); FP_GameEntity *entity = FP_Game_MakeEntityPointerF(game, "Mob");
entity->local_pos = pos; entity->local_pos = pos;
entity->size_scale = Dqn_V2_InitNx1(.5f); entity->size_scale = Dqn_V2_InitNx1(.25f);
entity->sprite_sheet = &game->smoochie_sprite_sheet; entity->action_to_anim_mapping = game->smoochie_action_mappings;
entity->sprite_anims = game->smoochie_sprite_anims; entity->local_hit_box_size = Dqn_V2_InitV2I(game->smoochie_sprite_sheet.sprite_size);
entity->local_hit_box_size = Dqn_V2_InitV2I(game->smoochie_sprite_sheet.sprite_size); entity->flags |= FP_GameEntityFlag_Clickable;
entity->flags |= FP_GameEntityFlag_Clickable; entity->flags |= FP_GameEntityFlag_MoveByKeyboard;
entity->flags |= FP_GameEntityFlag_MoveByKeyboard; entity->flags |= FP_GameEntityFlag_MoveByMouse;
entity->flags |= FP_GameEntityFlag_MoveByMouse; entity->flags |= FP_GameEntityFlag_NonTraversable;
entity->flags |= FP_GameEntityFlag_NonTraversable; entity->stalk_entity = game->player;
entity->stalk_entity = game->player;
FP_GameEntityHandle result = entity->handle; FP_GameEntityHandle result = entity->handle;
return result; return result;

View File

@ -68,14 +68,32 @@ enum FP_GameEntityActionFlag
FP_GameEntityActionFlag_Failed = 1 << 1, FP_GameEntityActionFlag_Failed = 1 << 1,
}; };
enum FP_ActionType
{
FP_ActionType_Idle,
FP_ActionType_Attack,
FP_ActionType_WalkUp,
FP_ActionType_WalkDown,
FP_ActionType_WalkLeft,
FP_ActionType_WalkRight,
FP_ActionType_Death,
};
struct FP_ActionToAnimationMapping
{
FP_ActionType action_type;
TELY_AssetSpriteSheet *sheet;
TELY_AssetSpriteAnimation anim;
};
Dqn_f32 const FP_GAME_ENTITY_ACTION_INFINITE_TIMER = -1.f; Dqn_f32 const FP_GAME_ENTITY_ACTION_INFINITE_TIMER = -1.f;
struct FP_GameEntityAction struct FP_GameEntityAction
{ {
FP_GameEntityState state; FP_GameEntityState state;
uint32_t flags; // Bit flags corresponding with `FP_GameEntityActionFlag` uint32_t flags; // Bit flags corresponding with `FP_GameEntityActionFlag`
TELY_AssetSpriteAnimation *anim; FP_ActionToAnimationMapping mapping;
Dqn_f32 timer_s; Dqn_f32 timer_s;
Dqn_f32 end_at_s; Dqn_f32 end_at_s;
}; };
struct FP_GameEntity struct FP_GameEntity
@ -88,8 +106,7 @@ struct FP_GameEntity
Dqn_String8 name; Dqn_String8 name;
FP_GameEntityHandle handle; FP_GameEntityHandle handle;
TELY_AssetSpriteSheet *sprite_sheet; Dqn_Slice<FP_ActionToAnimationMapping> action_to_anim_mapping;
Dqn_Slice<TELY_AssetSpriteAnimation> sprite_anims;
Dqn_V2 size_scale; Dqn_V2 size_scale;
FP_GameEntityAction action; FP_GameEntityAction action;
Dqn_V2 velocity; Dqn_V2 velocity;
@ -150,26 +167,26 @@ struct FP_Game
Dqn_FArray<FP_GameEntityHandle, 8> parent_entity_stack; Dqn_FArray<FP_GameEntityHandle, 8> parent_entity_stack;
Dqn_VArray<FP_GameEntity> entities; Dqn_VArray<FP_GameEntity> entities;
TELY_AssetSpriteSheet terry_sprite_sheet; TELY_AssetSpriteSheet terry_sprite_sheet;
Dqn_Slice<TELY_AssetSpriteAnimation> terry_sprite_anims; Dqn_Slice<FP_ActionToAnimationMapping> terry_action_mappings;
TELY_AssetSpriteSheet smoochie_sprite_sheet; TELY_AssetSpriteSheet smoochie_sprite_sheet;
Dqn_Slice<TELY_AssetSpriteAnimation> smoochie_sprite_anims; Dqn_Slice<FP_ActionToAnimationMapping> smoochie_action_mappings;
FP_GameEntity *root_entity; FP_GameEntity *root_entity;
FP_GameEntity *entity_free_list; FP_GameEntity *entity_free_list;
FP_GameEntityHandle player; FP_GameEntityHandle player;
FP_GameEntityHandle clicked_entity; FP_GameEntityHandle clicked_entity;
FP_GameEntityHandle hot_entity; FP_GameEntityHandle hot_entity;
FP_GameEntityHandle active_entity; FP_GameEntityHandle active_entity;
FP_GameEntityHandle prev_clicked_entity; FP_GameEntityHandle prev_clicked_entity;
FP_GameEntityHandle prev_hot_entity; FP_GameEntityHandle prev_hot_entity;
FP_GameEntityHandle prev_active_entity; FP_GameEntityHandle prev_active_entity;
FP_GameCamera camera; FP_GameCamera camera;
TELY_RFui rfui; TELY_RFui rfui;
}; };
struct FP_GameAStarNode struct FP_GameAStarNode