diff --git a/feely_pona.cpp b/feely_pona.cpp index b6823b1..74f5b48 100644 --- a/feely_pona.cpp +++ b/feely_pona.cpp @@ -120,6 +120,102 @@ TELY_AssetSpriteSheet FP_LoadSpriteSheetFromSpec(TELY_Platform *platform, TELY_A return result; } +static void FP_Game_MoveEntity(FP_Game *game, FP_GameEntityHandle entity_handle, Dqn_V2 acceleration_meters_per_s) +{ + // f"(t) = a + // f'(t) = at + v + // f (t) = 0.5f*a(t^2) + vt + p + + FP_GameEntity *entity = FP_Game_GetEntity(game, entity_handle); + if (FP_Game_IsNilEntity(entity)) + return; + + Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(PHYSICS_STEP); + Dqn_f32 t_squared = DQN_SQUARED(t); + + Dqn_f32 velocity_falloff_coefficient = 0.82f; + Dqn_f32 acceleration_feel_good_factor = 15'000.f; + Dqn_V2 acceleration = FP_Game_MetersToPixelsV2(game, acceleration_meters_per_s) * acceleration_feel_good_factor; + entity->velocity = (acceleration * t) + entity->velocity * velocity_falloff_coefficient; + + // NOTE: Zero out velocity with epsilon + if (DQN_ABS(entity->velocity.x) < 5.f) + entity->velocity.x = 0.f; + if (DQN_ABS(entity->velocity.y) < 5.f) + entity->velocity.y = 0.f; + + Dqn_V2 delta_pos = (acceleration * 0.5f * t_squared) + (entity->velocity * t); + Dqn_Rect entity_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle); + Dqn_V2 entity_pos = Dqn_Rect_Center(entity_world_hit_box); + Dqn_V2 entity_new_pos = entity_pos + delta_pos; + + bool has_collision = false; + for (FP_GameEntityIterator collider_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &collider_it, game->root_entity); ) { + FP_GameEntity *collider = collider_it.entity; + if (collider->handle == entity->handle) + continue; + + // TODO(doyle): Calculate the list of collidables at the start of the frame + if ((collider->flags & FP_GameEntityFlag_NonTraversable) == 0) + continue; + + // NOTE: Sweep collider with half the radius of the source entity + Dqn_Rect collider_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, collider->handle); + Dqn_Rect swept_collider_world_hit_box = collider_world_hit_box; + swept_collider_world_hit_box.pos -= (entity_world_hit_box.size * .5f); + swept_collider_world_hit_box.size += entity_world_hit_box.size; + + if (!Dqn_Rect_ContainsPoint(swept_collider_world_hit_box, entity_new_pos)) + continue; + + Dqn_f32 collider_left_wall_x = swept_collider_world_hit_box.pos.x; + Dqn_f32 collider_right_wall_x = swept_collider_world_hit_box.pos.x + swept_collider_world_hit_box.size.w; + Dqn_f32 collider_top_wall_y = swept_collider_world_hit_box.pos.y; + Dqn_f32 collider_bottom_wall_y = swept_collider_world_hit_box.pos.y + swept_collider_world_hit_box.size.h; + + Dqn_V2 o = entity_pos; + Dqn_V2 d = delta_pos; + + // NOTE: Solve collision by determining the 't' value at which + // we hit one of the walls of the collider and move the entity + // at exactly that point. + // O + td = x + // td = x - O + // t = (x - O) / d + + Dqn_f32 const SENTINEL_T = 999.f; + Dqn_f32 earliest_t = SENTINEL_T; + if (d.x != 0.f) { + Dqn_f32 left_t = (collider_left_wall_x - o.x) / d.x; + Dqn_f32 right_t = (collider_right_wall_x - o.x) / d.x; + if (left_t >= 0.f && left_t <= 1.f) + earliest_t = DQN_MIN(earliest_t, left_t); + if (right_t >= 0.f && right_t <= 1.f) + earliest_t = DQN_MIN(earliest_t, right_t); + } + + if (d.y != 0.f) { + Dqn_f32 top_t = (collider_top_wall_y - o.y) / d.y; + Dqn_f32 bottom_t = (collider_bottom_wall_y - o.y) / d.y; + if (top_t >= 0.f && top_t <= 1.f) + earliest_t = DQN_MIN(earliest_t, top_t); + if (bottom_t >= 0.f && bottom_t <= 1.f) + earliest_t = DQN_MIN(earliest_t, bottom_t); + } + + if (earliest_t != SENTINEL_T) { + Dqn_V2 pos_just_before_collide = entity_pos + (d * earliest_t); + Dqn_V2 new_delta_p = pos_just_before_collide - entity_pos; + entity->local_pos += new_delta_p; + has_collision = true; + } + } + + if (!has_collision) { + entity->local_pos += delta_pos; + } +} + extern "C" __declspec(dllexport) void TELY_DLL_Reload(void *user_data) { @@ -394,7 +490,7 @@ void FP_EntityActionStateMachine(FP_Game *game, TELY_PlatformInput *input, FP_Ga } } - if (!entity_has_velocity /*&& !has_collision*/) { + if (!entity_has_velocity) { action->next_state = FP_EntityTerryState_Idle; } } break; @@ -884,7 +980,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input } } - if (closest_terry_dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 2.f))) { + if (closest_terry_dist < DQN_SQUARED(FP_Game_MetersToPixelsNx1(game, 4.f))) { FP_SentinelListLink *first_waypoint = FP_SentinelList_Front(&entity->waypoints); if (first_waypoint->data.entity != closest_terry->handle) { @@ -992,95 +1088,7 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input } // NOTE: Core equations of motion ========================================================== - bool has_collision = false; - { - // f"(t) = a - // f'(t) = at + v - // f (t) = 0.5f*a(t^2) + vt + p - Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(PHYSICS_STEP); - Dqn_f32 t_squared = DQN_SQUARED(t); - - Dqn_f32 velocity_falloff_coefficient = 0.82f; - Dqn_f32 acceleration_feel_good_factor = 15'000.f; - Dqn_V2 acceleration = FP_Game_MetersToPixelsV2(game, acceleration_meters_per_s) * acceleration_feel_good_factor; - entity->velocity = (acceleration * t) + entity->velocity * velocity_falloff_coefficient; - - // NOTE: Zero out velocity with epsilon - if (DQN_ABS(entity->velocity.x) < 5.f) - entity->velocity.x = 0.f; - if (DQN_ABS(entity->velocity.y) < 5.f) - entity->velocity.y = 0.f; - - Dqn_V2 delta_pos = (acceleration * 0.5f * t_squared) + (entity->velocity * t); - Dqn_Rect entity_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, entity->handle); - Dqn_V2 entity_pos = Dqn_Rect_Center(entity_world_hit_box); - Dqn_V2 entity_new_pos = entity_pos + delta_pos; - - for (FP_GameEntityIterator collider_it = {}; FP_Game_DFSPostOrderWalkEntityTree(game, &collider_it, game->root_entity); ) { - FP_GameEntity *collider = collider_it.entity; - if (collider->handle == entity->handle) - continue; - - // TODO(doyle): Calculate the list of collidables at the start of the frame - if ((collider->flags & FP_GameEntityFlag_NonTraversable) == 0) - continue; - - // NOTE: Sweep collider with half the radius of the source entity - Dqn_Rect collider_world_hit_box = FP_Game_CalcEntityWorldHitBox(game, collider->handle); - Dqn_Rect swept_collider_world_hit_box = collider_world_hit_box; - swept_collider_world_hit_box.pos -= (entity_world_hit_box.size * .5f); - swept_collider_world_hit_box.size += entity_world_hit_box.size; - - if (!Dqn_Rect_ContainsPoint(swept_collider_world_hit_box, entity_new_pos)) - continue; - - Dqn_f32 collider_left_wall_x = swept_collider_world_hit_box.pos.x; - Dqn_f32 collider_right_wall_x = swept_collider_world_hit_box.pos.x + swept_collider_world_hit_box.size.w; - Dqn_f32 collider_top_wall_y = swept_collider_world_hit_box.pos.y; - Dqn_f32 collider_bottom_wall_y = swept_collider_world_hit_box.pos.y + swept_collider_world_hit_box.size.h; - - Dqn_V2 o = entity_pos; - Dqn_V2 d = delta_pos; - - // NOTE: Solve collision by determining the 't' value at which - // we hit one of the walls of the collider and move the entity - // at exactly that point. - // O + td = x - // td = x - O - // t = (x - O) / d - - Dqn_f32 const SENTINEL_T = 999.f; - Dqn_f32 earliest_t = SENTINEL_T; - if (d.x != 0.f) { - Dqn_f32 left_t = (collider_left_wall_x - o.x) / d.x; - Dqn_f32 right_t = (collider_right_wall_x - o.x) / d.x; - if (left_t >= 0.f && left_t <= 1.f) - earliest_t = DQN_MIN(earliest_t, left_t); - if (right_t >= 0.f && right_t <= 1.f) - earliest_t = DQN_MIN(earliest_t, right_t); - } - - if (d.y != 0.f) { - Dqn_f32 top_t = (collider_top_wall_y - o.y) / d.y; - Dqn_f32 bottom_t = (collider_bottom_wall_y - o.y) / d.y; - if (top_t >= 0.f && top_t <= 1.f) - earliest_t = DQN_MIN(earliest_t, top_t); - if (bottom_t >= 0.f && bottom_t <= 1.f) - earliest_t = DQN_MIN(earliest_t, bottom_t); - } - - if (earliest_t != SENTINEL_T) { - Dqn_V2 pos_just_before_collide = entity_pos + (d * earliest_t); - Dqn_V2 new_delta_p = pos_just_before_collide - entity_pos; - entity->local_pos += new_delta_p; - has_collision = true; - } - } - - if (!has_collision) { - entity->local_pos += delta_pos; - } - } + FP_Game_MoveEntity(game, entity->handle, acceleration_meters_per_s); // NOTE: Tick the state machine FP_EntityActionStateMachine(game, input, entity, dir_vector); @@ -1189,12 +1197,8 @@ void FP_Update(TELY_Platform *platform, FP_Game *game, TELY_PlatformInput *input else attack_dir_vector.x = -1.f; - Dqn_V2 acceleration = attack_dir_vector * 500'000.f; - Dqn_f32 t = DQN_CAST(Dqn_f32)DQN_SQUARED(PHYSICS_STEP); - Dqn_f32 t_squared = DQN_SQUARED(t); - - Dqn_V2 delta_p = (acceleration * 0.5f * t_squared) + (defender->velocity * t); - defender->velocity = (acceleration * t) + defender->velocity * 2.0f; + Dqn_V2 attack_acceleration_meters_per_s = attack_dir_vector * 20.f; + FP_Game_MoveEntity(game, defender->handle, attack_acceleration_meters_per_s); } } } diff --git a/feely_pona_entity_create.cpp b/feely_pona_entity_create.cpp index f00231b..3fb78f7 100644 --- a/feely_pona_entity_create.cpp +++ b/feely_pona_entity_create.cpp @@ -134,6 +134,7 @@ static FP_GameEntityHandle FP_Entity_CreateTerry(FP_Game *game, Dqn_V2 pos, DQN_ entity->local_hit_box_size = FP_Game_MetersToPixelsNx2(game, 0.5f, entity->sprite_height.meters); FP_Entity_AddDebugEditorFlags(game, result); entity->flags |= FP_GameEntityFlag_NonTraversable; + entity->flags |= FP_GameEntityFlag_Attackable; return result; } diff --git a/feely_pona_game.h b/feely_pona_game.h index 97e5eb9..70db5be 100644 --- a/feely_pona_game.h +++ b/feely_pona_game.h @@ -15,7 +15,7 @@ enum FP_GameEntityFlag FP_GameEntityFlag_MobSpawner = 1 << 7, FP_GameEntityFlag_MobSpawnerWaypoint = 1 << 8, FP_GameEntityFlag_AggrosWhenNearTerry = 1 << 9, - FP_GameEntityFlag_Attackable = 1 << 9, + FP_GameEntityFlag_Attackable = 1 << 10, }; enum FP_GameShapeType