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 ||
*state == FP_EntityTerryState_AttackDown ||
*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;
DQN_ASSERT(duration_ms >= PHYSICS_STEP);
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
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;
// NOTE: Position the attack box
@ -564,6 +562,19 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
case FP_EntitySmoochieState_AttackHeart: {
} 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: {
Dqn_String8 desired_action_name = {};
switch (entity->direction) {
@ -592,6 +603,10 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
} break;
}
if (entity->is_dying && *state != FP_EntitySmoochieState_Death) {
action->next_state = FP_EntitySmoochieState_Death;
}
if (*state == FP_EntitySmoochieState_AttackDown) {
entity->attack_box_size = entity->local_hit_box_size;
// NOTE: Position the attack box
@ -644,6 +659,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
} break;
case FP_EntityClingerState_Attack: {
uint32_t asset_flip = {};
Dqn_String8 desired_action_name = {};
switch (entity->direction) {
@ -664,7 +680,16 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
} break;
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;
case FP_EntityClingerState_Run: {
@ -695,6 +720,10 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga
} 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
entity->attack_box_size = entity->local_hit_box_size;
switch (entity->direction) {
@ -895,6 +924,9 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
// NOTE: We had a waypoint to move to Terry because he has
// drawn our aggro and we've arrived at Terry
bool can_attack = !entity->is_dying;
if (can_attack) {
switch (entity->type) {
case FP_EntityType_Nil: /*FALLTHRU*/
case FP_EntityType_Merchant: break;
@ -915,6 +947,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
entity->action.next_state = FP_EntityClingerState_Attack;
} break;
}
}
// NOTE: Aggro makes the entity attack Terry, we will
// exit here preserving the waypoint in the entity.
@ -1030,7 +1063,6 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input
if (target->handle == entity->handle)
continue;
// TODO(josh): This check should be updated based on an entity attackable flag
if (target->flags & FP_GameEntityFlag_Attackable) {
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;
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);
entity->type = FP_EntityType_Clinger;
entity->hp = 3;
entity->hp = 1;
entity->is_dying = false;
entity->local_pos = pos;
entity->sprite_height.meters = 1.6f;
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->hp = 1;
entity->is_dying = false;
entity->local_pos = pos;
entity->sprite_height.meters = 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_HurtSide,
FP_EntitySmoochieState_AttackHeart,
FP_EntitySmoochieState_Death,
FP_EntitySmoochieState_Run,
};

View File

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