diff --git a/feely_pona.cpp b/feely_pona.cpp index 7f93a43..fe788d8 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -1050,25 +1050,50 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input bool can_attack = !entity->is_dying; if (can_attack) { + + Dqn_V2 attack_dir_vectors[FP_GameDirection_Count] = {}; + attack_dir_vectors[FP_GameDirection_Up] = Dqn_V2_InitNx2(+0, -1); + attack_dir_vectors[FP_GameDirection_Down] = Dqn_V2_InitNx2(+0, +1); + attack_dir_vectors[FP_GameDirection_Left] = Dqn_V2_InitNx2(-1, +0); + attack_dir_vectors[FP_GameDirection_Right] = Dqn_V2_InitNx2(+1, +0); + + Dqn_V2 defender_entity_pos = FP_Game_CalcEntityWorldPos(game, waypoint_entity->handle); + Dqn_V2 entity_to_defender = defender_entity_pos - entity_pos; + Dqn_V2 entity_to_defender_norm = Dqn_V2_Normalise(entity_to_defender); + + FP_GameDirection best_attack_dir = FP_GameDirection_Up; + Dqn_f32 best_attack_dir_scalar_projection_onto_entity_to_waypoint_vector = -1.1f; + DQN_FOR_UINDEX (dir_index, FP_GameDirection_Count) { + Dqn_V2 attack_dir = attack_dir_vectors[dir_index]; + Dqn_f32 scalar_projection = Dqn_V2_Dot(attack_dir, entity_to_defender_norm); + if (scalar_projection > best_attack_dir_scalar_projection_onto_entity_to_waypoint_vector) { + best_attack_dir = DQN_CAST(FP_GameDirection)dir_index; + best_attack_dir_scalar_projection_onto_entity_to_waypoint_vector = scalar_projection; + } + } + switch (entity->type) { case FP_EntityType_Nil: /*FALLTHRU*/ case FP_EntityType_Merchant: break; - case FP_EntityType_Terry: { - // TODO(doyle): We should check if it's valid to enter this new state - // from the entity's current state - entity->action.next_state = FP_EntityTerryState_Attack; - } break; - - case FP_EntityType_Smoochie: { - // TODO(doyle): We should check if it's valid to enter this new state - // from the entity's current state - entity->action.next_state = FP_EntitySmoochieState_Attack; - } break; - + case FP_EntityType_Terry: /*FALLTHRU*/ + case FP_EntityType_Smoochie: /*FALLTHRU*/ case FP_EntityType_Clinger: { - entity->action.next_state = FP_EntityClingerState_Attack; + // TODO(doyle): We should check if it's valid to enter this new state + // from the entity's current state + if (entity->type == FP_EntityType_Terry) { + entity->action.next_state = FP_EntityTerryState_Attack; + } else if (entity->type == FP_EntityType_Smoochie) { + entity->action.next_state = FP_EntitySmoochieState_Attack; + } else { + DQN_ASSERT(entity->type == FP_EntityType_Clinger); + entity->action.next_state = FP_EntityClingerState_Attack; + } + + entity->direction = best_attack_dir; } break; + + case FP_EntityType_Count: DQN_INVALID_CODE_PATH; break; } } @@ -1181,6 +1206,27 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input if ((defender->flags & FP_GameEntityFlag_Attackable) == 0) continue; + bool permit_attack = true; + switch (attacker->type) { + case FP_EntityType_Smoochie: { + if (defender->type == FP_EntityType_Smoochie || defender->type == FP_EntityType_Clinger) + permit_attack = false; + } break; + + case FP_EntityType_Clinger: { + if (defender->type == FP_EntityType_Smoochie || defender->type == FP_EntityType_Clinger) + permit_attack = false; + } break; + + case FP_EntityType_Nil: /*FALLTRHU*/ + case FP_EntityType_Terry: /*FALLTRHU*/ + case FP_EntityType_Merchant: /*FALLTRHU*/ + case FP_EntityType_Count: break; + } + + if (!permit_attack) + continue; + Dqn_Rect defender_box = FP_Game_CalcEntityWorldHitBox(game, defender->handle); if (!Dqn_Rect_Intersects(attacker_box, defender_box)) continue;