-- Regression test for a deadlock in PathMovement/PathFindingMovement.
--
-- A "path_finding" movement snaps its entity to the map's 8x8 grid before
-- moving it (see PathMovement::snap()). Snapping tries the grid
-- intersection closest to the entity and, if that one is blocked by an
-- obstacle, the opposite intersection.
--
-- Entity "e" starts in a perfectly valid (non-overlapping) position, but
-- misaligned to the grid such that the closest grid intersection is
-- inside the wall below it, while the opposite intersection (away from
-- the wall) is free. This alone should never be a problem: the movement
-- is expected to try the closest intersection, notice it is unreachable,
-- and fall back to the opposite one.
--
-- Before the fix, PathFindingMovement's own retry logic (creating a new
-- random path whenever no path is found) called PathMovement::restart(),
-- which unconditionally reset the snapping state. Since the entity never
-- moves while it fails to align, this restart cycle repeated indefinitely
-- and PathMovement::snap() kept retrying only the closest (unreachable)
-- intersection, never reaching the opposite one: the entity stayed
-- completely frozen forever, even though nothing actually blocked it from
-- moving away.

local map = ...

function map:on_opening_transition_finished()

  local e = map:get_entity("e")
  local target = map:get_entity("target")

  -- A non-default, non-8-aligned size/origin (as used by real quests to
  -- give an enemy a smaller hitbox) is what usually creates this kind of
  -- grid misalignment in the first place.
  e:set_size(12, 12)
  e:set_origin(6, 9)

  local start_x, start_y = e:get_position()
  local start_distance = e:get_distance(target)
  local min_distance = start_distance

  local movement = sol.movement.create("path_finding")
  movement:set_speed(88)
  movement:set_target(target)
  movement:start(e)

  -- The entity must move away from its stuck position quickly: snapping
  -- to the closest grid intersection should fail almost immediately
  -- (it is blocked), and the fallback to the opposite intersection
  -- happens well within 2 seconds.
  sol.timer.start(map, 2000, function()
    local x, y = e:get_position()
    assert(x ~= start_x or y ~= start_y,
        "The entity is still stuck at its starting position: " ..
        "path_finding movement deadlocked")
  end)

  -- Once unstuck, the entity should be able to resume normal path
  -- finding and get significantly closer to its target at some point.
  -- Path finding towards a small target keeps recomputing and can wander
  -- a bit, so track the minimum distance reached instead of checking a
  -- single point in time.
  sol.timer.start(map, 200, function()
    local distance = e:get_distance(target)
    if distance < min_distance then
      min_distance = distance
    end
    return true
  end)

  sol.timer.start(map, 12000, function()
    assert(min_distance < start_distance / 2,
        "The entity did not make real progress towards its target " ..
        "(min_distance=" .. min_distance .. ", start_distance=" .. start_distance .. ")")
    sol.main.exit()
  end)
end
