Page 1 of 1

How do I use request_path?

Posted: Thu Feb 09, 2023 5:50 am
by brainwave0
I'm trying to make a simple mod where the character walks to wherever I click on the map. I'm using request_path for pathfinding, but it keeps giving me paths through things that the character can't walk through, like in this screenshot:

Screenshot from 2023-02-08 21-36-36.png
Screenshot from 2023-02-08 21-36-36.png (870.21 KiB) Viewed 1539 times

Also, some of the paths seem to go the long way to reach the destinations, rather than taking more direct routes. What I'm getting hung up on is this field collision_mask that I'm supposed to pass to request_path. I'm not sure what values to use. None of the combinations I've tried have worked.

Here is my code:

Code: Select all

character_is_moving = false
path_index = 1
path = nil

-- Determines which direction the character should go.
local function get_direction(start_position, end_position)
    local angle = math.atan2(end_position.y - start_position.y, start_position.x - end_position.x)

    -- Given a circle representing the angles, it is divided into eight octants representing the cardinal directions.
    local octant = (angle + math.pi) / (2 * math.pi) * 8 + 0.5

    if octant < 1 then
        return defines.direction.east
    elseif octant < 2 then
        return defines.direction.northeast
    elseif octant < 3 then
        return defines.direction.north
    elseif octant < 4 then
        return defines.direction.northwest
    elseif octant < 5 then
        return defines.direction.west
    elseif octant < 6 then
        return defines.direction.southwest
    elseif octant < 7 then
        return defines.direction.south
    else
        return defines.direction.southeast
    end
end

function positions_approximately_equal(a, b)
    return math.abs(a.x - b.x) < 0.25 and math.abs(a.y - b.y) < 0.25
end

-- Requests a path when the map is clicked.
script.on_event("mouse-click", function (event)
    local surface = game.get_surface("nauvis")
    local character = game.get_player(1).character
    surface.request_path{
        bounding_box = character.bounding_box,
        collision_mask = character.prototype.collision_mask,
        start = character.position,
        goal = event.cursor_position,
        force = "player"
    }
end)

-- Initializes the movement process when the path is received.
script.on_event(defines.events.on_script_path_request_finished, function (event)
    character_is_moving = true
    path = event.path
    path_index = 1
end)

-- Moves the character.
script.on_event(defines.events.on_tick, function (event)
    local character = game.get_player(1).character
    if character_is_moving and path ~= nil then
        if positions_approximately_equal(character.position, path[path_index].position) then
            -- waypoint reached
            path_index = path_index + 1  -- select the next waypoint
        end

        if path_index == #path then
            character_is_moving = false
            path_index = 1
        else
            -- move the character for one tick
            game.get_player(1).walking_state = {
                walking = true,
                direction = get_direction(character.position, path[path_index].position)
            }
        end
    end
end)
Does anyone have an idea as to why this does not work?

Re: How do I use request_path?

Posted: Thu Feb 09, 2023 8:47 am
by boskid
Core of the problem is due to `bounding_box` you are giving to the request_path. LuaEntity::bounding_box gives you a bounding box which is centered around the entity's position and that is most likely far away from the {0,0} position. The request_path needs a bounding_box which is centered at {0,0} because it is moving this bounding box on its own to the check positions based on the pathfinder nodes. If this bounding box is already shifted, then the path you get is correct but the passable tiles are shifted in the opposite direction than the character's position relative to {0,0}.

Re: How do I use request_path?

Posted: Fri Feb 10, 2023 3:07 pm
by PFQNiet
I think character.prototype.bounding_box should work, right?

But aside from that your code is prone to issues in a multiplayer setting, since you've hardcoded "player 1" in there. You should instead do game.players[event.player_index], and you should also be checking that objects exist and are valid before you try to use them.

Re: How do I use request_path?

Posted: Wed May 31, 2023 1:01 am
by rawls
Did you ever figure this out? I'm having the same problem were request path generates a path over water for the player.

Re: How do I use request_path?

Posted: Wed May 31, 2023 4:52 am
by boskid
rawls wrote: Wed May 31, 2023 1:01 am Did you ever figure this out? I'm having the same problem were request path generates a path over water for the player.
Given the other topic (106448) you opened while cross posting here, i will give you an answer here for the part related to this topic: Your problem is related to usage of character's current bounding box which is shifted by the characters position while your expectation of having path with nodes not over water means you want to pass a bounding box which is centered at {0,0} position. Fix here is identical, use LuaEntityPrototype::collision_box from the entity's prototype since that one is not shifted.