Add guard asserts for attack, Add death animations

This commit is contained in:
Joshalosh 2023-09-28 21:38:26 +10:00
parent 44014a8add
commit 9fde0594e1
4 changed files with 61 additions and 25 deletions

View File

@ -433,16 +433,14 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
if (*state == FP_EntityTerryState_AttackUp || if (*state == FP_EntityTerryState_AttackUp ||
*state == FP_EntityTerryState_AttackDown || *state == FP_EntityTerryState_AttackDown ||
*state == FP_EntityTerryState_AttackSide) { *state == FP_EntityTerryState_AttackSide) {
DQN_ASSERT(action->sprite.anim);
// NOTE: Grab timings to narrow down a midpoint of the attack animation
const uint32_t GRACE_PERIOD = 33;
uint64_t duration_ms = action->sprite.anim->count * action->sprite.anim->ms_per_frame; uint64_t duration_ms = action->sprite.anim->count * action->sprite.anim->ms_per_frame;
DQN_ASSERT(duration_ms >= PHYSICS_STEP);
uint64_t midpoint_clock_ms = action->end_at_clock_ms - (duration_ms / 2); uint64_t midpoint_clock_ms = action->end_at_clock_ms - (duration_ms / 2);
uint64_t window_start = midpoint_clock_ms - GRACE_PERIOD;
uint64_t window_end = midpoint_clock_ms + GRACE_PERIOD;
// NOTE: Adding an attack_processed bool to make sure things only fire once // NOTE: Adding an attack_processed bool to make sure things only fire once
if (!entity->attack_processed && game->clock_ms >= window_start && game->clock_ms < window_end) { if (!entity->attack_processed && game->clock_ms >= midpoint_clock_ms) {
entity->attack_box_size = entity->local_hit_box_size; entity->attack_box_size = entity->local_hit_box_size;
// NOTE: Position the attack box // NOTE: Position the attack box
@ -564,6 +562,19 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
case FP_EntitySmoochieState_AttackHeart: { case FP_EntitySmoochieState_AttackHeart: {
} break; } break;
case FP_EntitySmoochieState_Death: {
if (entering_new_state) {
TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.smoochie_death, 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 (action_has_finished) {
FP_Game_DeleteEntity(game, entity->handle);
}
} break;
case FP_EntitySmoochieState_Run: { case FP_EntitySmoochieState_Run: {
Dqn_String8 desired_action_name = {}; Dqn_String8 desired_action_name = {};
switch (entity->direction) { switch (entity->direction) {
@ -592,6 +603,10 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
} break; } break;
} }
if (entity->is_dying && *state != FP_EntitySmoochieState_Death) {
action->next_state = FP_EntitySmoochieState_Death;
}
if (*state == FP_EntitySmoochieState_AttackDown) { if (*state == FP_EntitySmoochieState_AttackDown) {
entity->attack_box_size = entity->local_hit_box_size; entity->attack_box_size = entity->local_hit_box_size;
// NOTE: Position the attack box // NOTE: Position the attack box
@ -644,6 +659,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
} break; } break;
case FP_EntityClingerState_Attack: { case FP_EntityClingerState_Attack: {
uint32_t asset_flip = {}; uint32_t asset_flip = {};
Dqn_String8 desired_action_name = {}; Dqn_String8 desired_action_name = {};
switch (entity->direction) { switch (entity->direction) {
@ -664,7 +680,16 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
} break; } break;
case FP_EntityClingerState_Death: { case FP_EntityClingerState_Death: {
// TODO(doyle): Implement if (entering_new_state) {
TELY_AssetAnimatedSprite sprite = TELY_Asset_MakeAnimatedSprite(sheet, g_anim_names.clinger_death, 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 (action_has_finished) {
FP_Game_DeleteEntity(game, entity->handle);
}
} break; } break;
case FP_EntityClingerState_Run: { case FP_EntityClingerState_Run: {
@ -695,6 +720,10 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
} break; } break;
} }
if (entity->is_dying && *state != FP_EntityClingerState_Death) {
action->next_state = FP_EntityClingerState_Death;
}
if (*state == FP_EntityClingerState_Attack) { // NOTE: Position the attack box if (*state == FP_EntityClingerState_Attack) { // NOTE: Position the attack box
entity->attack_box_size = entity->local_hit_box_size; entity->attack_box_size = entity->local_hit_box_size;
switch (entity->direction) { switch (entity->direction) {
@ -895,25 +924,29 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
// NOTE: We had a waypoint to move to Terry because he has // NOTE: We had a waypoint to move to Terry because he has
// drawn our aggro and we've arrived at Terry // drawn our aggro and we've arrived at Terry
switch (entity->type) { bool can_attack = !entity->is_dying;
case FP_EntityType_Nil: /*FALLTHRU*/
case FP_EntityType_Merchant: break;
case FP_EntityType_Terry: { if (can_attack) {
// TODO(doyle): We should check if it's valid to enter this new state switch (entity->type) {
// from the entity's current state case FP_EntityType_Nil: /*FALLTHRU*/
entity->action.next_state = FP_EntityTerryState_AttackSide; case FP_EntityType_Merchant: break;
} break;
case FP_EntityType_Smoochie: { case FP_EntityType_Terry: {
// TODO(doyle): We should check if it's valid to enter this new state // TODO(doyle): We should check if it's valid to enter this new state
// from the entity's current state // from the entity's current state
entity->action.next_state = FP_EntitySmoochieState_AttackDown; entity->action.next_state = FP_EntityTerryState_AttackSide;
} break; } break;
case FP_EntityType_Clinger: { case FP_EntityType_Smoochie: {
entity->action.next_state = FP_EntityClingerState_Attack; // TODO(doyle): We should check if it's valid to enter this new state
} break; // from the entity's current state
entity->action.next_state = FP_EntitySmoochieState_AttackDown;
} break;
case FP_EntityType_Clinger: {
entity->action.next_state = FP_EntityClingerState_Attack;
} break;
}
} }
// NOTE: Aggro makes the entity attack Terry, we will // NOTE: Aggro makes the entity attack Terry, we will
@ -1030,7 +1063,6 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
if (target->handle == entity->handle) if (target->handle == entity->handle)
continue; continue;
// TODO(josh): This check should be updated based on an entity attackable flag
if (target->flags & FP_GameEntityFlag_Attackable) { if (target->flags & FP_GameEntityFlag_Attackable) {
Dqn_Rect target_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, target->handle); Dqn_Rect target_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, target->handle);
@ -1038,7 +1070,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
target->hp -= 1; target->hp -= 1;
if (target->hp <= 0) { if (target->hp <= 0) {
FP_Game_DeleteEntity(game, target->handle); target->is_dying = true;
} }
} }
} }

View File

@ -44,7 +44,8 @@ static FP_GameEntityHandle FP_Entity_CreateClinger(FP_Game *game, Dqn_V2 pos, DQ
va_end(args); va_end(args);
entity->type = FP_EntityType_Clinger; entity->type = FP_EntityType_Clinger;
entity->hp = 3; entity->hp = 1;
entity->is_dying = false;
entity->local_pos = pos; entity->local_pos = pos;
entity->sprite_height.meters = 1.6f; entity->sprite_height.meters = 1.6f;
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, 1.7f); entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, 1.7f);
@ -64,6 +65,7 @@ static FP_GameEntityHandle FP_Entity_CreateSmoochie(FP_Game *game, Dqn_V2 pos, D
entity->type = FP_EntityType_Smoochie; entity->type = FP_EntityType_Smoochie;
entity->hp = 1; entity->hp = 1;
entity->is_dying = false;
entity->local_pos = pos; entity->local_pos = pos;
entity->sprite_height.meters = 1.6f; entity->sprite_height.meters = 1.6f;
entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, 1.6f); entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.4f, 1.6f);

View File

@ -30,6 +30,7 @@ enum FP_EntitySmoochieState
FP_EntitySmoochieState_AttackDown, FP_EntitySmoochieState_AttackDown,
FP_EntitySmoochieState_HurtSide, FP_EntitySmoochieState_HurtSide,
FP_EntitySmoochieState_AttackHeart, FP_EntitySmoochieState_AttackHeart,
FP_EntitySmoochieState_Death,
FP_EntitySmoochieState_Run, FP_EntitySmoochieState_Run,
}; };

View File

@ -142,6 +142,7 @@ struct FP_GameEntity
Dqn_V2 attack_box_size; Dqn_V2 attack_box_size;
Dqn_V2 attack_box_offset; Dqn_V2 attack_box_offset;
bool attack_processed; bool attack_processed;
bool is_dying;
Dqn_FArray<Dqn_V2, 8> spawner_waypoints; Dqn_FArray<Dqn_V2, 8> spawner_waypoints;
FP_SentinelList<FP_GameEntityHandle> spawn_list; FP_SentinelList<FP_GameEntityHandle> spawn_list;