feat(entity): implement path_to() method for entity pathfinding
- Add path_to(target_x, target_y) method to UIEntity class - Uses existing Dijkstra pathfinding implementation from UIGrid - Returns list of (x, y) coordinate tuples for complete path - Supports both positional and keyword argument formats - Proper error handling for out-of-bounds and no-grid scenarios - Comprehensive test suite covering normal and edge cases Part of TCOD integration sprint - gives entities immediate pathfinding capabilities. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
1a3e308c77
commit
7ee0a08662
11 changed files with 1163 additions and 30 deletions
343
src/UIGrid.cpp
343
src/UIGrid.cpp
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
UIGrid::UIGrid()
|
||||
: grid_x(0), grid_y(0), zoom(1.0f), center_x(0.0f), center_y(0.0f), ptex(nullptr),
|
||||
fill_color(8, 8, 8, 255) // Default dark gray background
|
||||
fill_color(8, 8, 8, 255), tcod_map(nullptr), tcod_dijkstra(nullptr) // Default dark gray background
|
||||
{
|
||||
// Initialize entities list
|
||||
entities = std::make_shared<std::list<std::shared_ptr<UIEntity>>>();
|
||||
|
|
@ -27,13 +27,14 @@ UIGrid::UIGrid()
|
|||
output.setTexture(renderTexture.getTexture());
|
||||
|
||||
// Points vector starts empty (grid_x * grid_y = 0)
|
||||
// TCOD map will be created when grid is resized
|
||||
}
|
||||
|
||||
UIGrid::UIGrid(int gx, int gy, std::shared_ptr<PyTexture> _ptex, sf::Vector2f _xy, sf::Vector2f _wh)
|
||||
: grid_x(gx), grid_y(gy),
|
||||
zoom(1.0f),
|
||||
ptex(_ptex), points(gx * gy),
|
||||
fill_color(8, 8, 8, 255) // Default dark gray background
|
||||
fill_color(8, 8, 8, 255), tcod_map(nullptr), tcod_dijkstra(nullptr) // Default dark gray background
|
||||
{
|
||||
// Use texture dimensions if available, otherwise use defaults
|
||||
int cell_width = _ptex ? _ptex->sprite_width : DEFAULT_CELL_WIDTH;
|
||||
|
|
@ -63,6 +64,24 @@ UIGrid::UIGrid(int gx, int gy, std::shared_ptr<PyTexture> _ptex, sf::Vector2f _x
|
|||
// textures are upside-down inside renderTexture
|
||||
output.setTexture(renderTexture.getTexture());
|
||||
|
||||
// Create TCOD map
|
||||
tcod_map = new TCODMap(gx, gy);
|
||||
|
||||
// Create TCOD dijkstra pathfinder
|
||||
tcod_dijkstra = new TCODDijkstra(tcod_map);
|
||||
|
||||
// Initialize grid points with parent reference
|
||||
for (int y = 0; y < gy; y++) {
|
||||
for (int x = 0; x < gx; x++) {
|
||||
int idx = y * gx + x;
|
||||
points[idx].grid_x = x;
|
||||
points[idx].grid_y = y;
|
||||
points[idx].parent_grid = this;
|
||||
}
|
||||
}
|
||||
|
||||
// Initial sync of TCOD map
|
||||
syncTCODMap();
|
||||
}
|
||||
|
||||
void UIGrid::update() {}
|
||||
|
|
@ -234,11 +253,116 @@ UIGridPoint& UIGrid::at(int x, int y)
|
|||
return points[y * grid_x + x];
|
||||
}
|
||||
|
||||
UIGrid::~UIGrid()
|
||||
{
|
||||
if (tcod_dijkstra) {
|
||||
delete tcod_dijkstra;
|
||||
tcod_dijkstra = nullptr;
|
||||
}
|
||||
if (tcod_map) {
|
||||
delete tcod_map;
|
||||
tcod_map = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
PyObjectsEnum UIGrid::derived_type()
|
||||
{
|
||||
return PyObjectsEnum::UIGRID;
|
||||
}
|
||||
|
||||
// TCOD integration methods
|
||||
void UIGrid::syncTCODMap()
|
||||
{
|
||||
if (!tcod_map) return;
|
||||
|
||||
for (int y = 0; y < grid_y; y++) {
|
||||
for (int x = 0; x < grid_x; x++) {
|
||||
const UIGridPoint& point = at(x, y);
|
||||
tcod_map->setProperties(x, y, point.transparent, point.walkable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UIGrid::syncTCODMapCell(int x, int y)
|
||||
{
|
||||
if (!tcod_map || x < 0 || x >= grid_x || y < 0 || y >= grid_y) return;
|
||||
|
||||
const UIGridPoint& point = at(x, y);
|
||||
tcod_map->setProperties(x, y, point.transparent, point.walkable);
|
||||
}
|
||||
|
||||
void UIGrid::computeFOV(int x, int y, int radius, bool light_walls, TCOD_fov_algorithm_t algo)
|
||||
{
|
||||
if (!tcod_map || x < 0 || x >= grid_x || y < 0 || y >= grid_y) return;
|
||||
|
||||
tcod_map->computeFov(x, y, radius, light_walls, algo);
|
||||
}
|
||||
|
||||
bool UIGrid::isInFOV(int x, int y) const
|
||||
{
|
||||
if (!tcod_map || x < 0 || x >= grid_x || y < 0 || y >= grid_y) return false;
|
||||
|
||||
return tcod_map->isInFov(x, y);
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, int>> UIGrid::findPath(int x1, int y1, int x2, int y2, float diagonalCost)
|
||||
{
|
||||
std::vector<std::pair<int, int>> path;
|
||||
|
||||
if (!tcod_map || x1 < 0 || x1 >= grid_x || y1 < 0 || y1 >= grid_y ||
|
||||
x2 < 0 || x2 >= grid_x || y2 < 0 || y2 >= grid_y) {
|
||||
return path;
|
||||
}
|
||||
|
||||
TCODPath tcod_path(tcod_map, diagonalCost);
|
||||
if (tcod_path.compute(x1, y1, x2, y2)) {
|
||||
for (int i = 0; i < tcod_path.size(); i++) {
|
||||
int x, y;
|
||||
tcod_path.get(i, &x, &y);
|
||||
path.push_back(std::make_pair(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
void UIGrid::computeDijkstra(int rootX, int rootY, float diagonalCost)
|
||||
{
|
||||
if (!tcod_map || !tcod_dijkstra || rootX < 0 || rootX >= grid_x || rootY < 0 || rootY >= grid_y) return;
|
||||
|
||||
// Compute the Dijkstra map from the root position
|
||||
tcod_dijkstra->compute(rootX, rootY);
|
||||
}
|
||||
|
||||
float UIGrid::getDijkstraDistance(int x, int y) const
|
||||
{
|
||||
if (!tcod_dijkstra || x < 0 || x >= grid_x || y < 0 || y >= grid_y) {
|
||||
return -1.0f; // Invalid position
|
||||
}
|
||||
|
||||
return tcod_dijkstra->getDistance(x, y);
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, int>> UIGrid::getDijkstraPath(int x, int y) const
|
||||
{
|
||||
std::vector<std::pair<int, int>> path;
|
||||
|
||||
if (!tcod_dijkstra || x < 0 || x >= grid_x || y < 0 || y >= grid_y) {
|
||||
return path; // Empty path for invalid position
|
||||
}
|
||||
|
||||
// Set the destination
|
||||
if (tcod_dijkstra->setPath(x, y)) {
|
||||
// Walk the path and collect points
|
||||
int px, py;
|
||||
while (tcod_dijkstra->walk(&px, &py)) {
|
||||
path.push_back(std::make_pair(px, py));
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
// Phase 1 implementations
|
||||
sf::FloatRect UIGrid::get_bounds() const
|
||||
{
|
||||
|
|
@ -338,35 +462,53 @@ UIDrawable* UIGrid::click_at(sf::Vector2f point)
|
|||
|
||||
|
||||
int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
||||
// Try parsing with PyArgHelpers
|
||||
int arg_idx = 0;
|
||||
auto grid_size_result = PyArgHelpers::parseGridSize(args, kwds, &arg_idx);
|
||||
auto pos_result = PyArgHelpers::parsePosition(args, kwds, &arg_idx);
|
||||
auto size_result = PyArgHelpers::parseSize(args, kwds, &arg_idx);
|
||||
|
||||
// Default values
|
||||
int grid_x = 0, grid_y = 0;
|
||||
float x = 0.0f, y = 0.0f, w = 0.0f, h = 0.0f;
|
||||
PyObject* textureObj = nullptr;
|
||||
|
||||
// Case 1: Got grid size and position from helpers (tuple format)
|
||||
if (grid_size_result.valid) {
|
||||
// Check if first argument is a tuple (for tuple-based initialization)
|
||||
bool has_tuple_first_arg = false;
|
||||
if (args && PyTuple_Size(args) > 0) {
|
||||
PyObject* first_arg = PyTuple_GetItem(args, 0);
|
||||
if (PyTuple_Check(first_arg)) {
|
||||
has_tuple_first_arg = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try tuple-based parsing if we have a tuple as first argument
|
||||
if (has_tuple_first_arg) {
|
||||
int arg_idx = 0;
|
||||
auto grid_size_result = PyArgHelpers::parseGridSize(args, kwds, &arg_idx);
|
||||
|
||||
// If grid size parsing failed with an error, report it
|
||||
if (!grid_size_result.valid) {
|
||||
if (grid_size_result.error) {
|
||||
PyErr_SetString(PyExc_TypeError, grid_size_result.error);
|
||||
} else {
|
||||
PyErr_SetString(PyExc_TypeError, "Invalid grid size tuple");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We got a valid grid size
|
||||
grid_x = grid_size_result.grid_w;
|
||||
grid_y = grid_size_result.grid_h;
|
||||
|
||||
// Set position if we got it
|
||||
// Try to parse position and size
|
||||
auto pos_result = PyArgHelpers::parsePosition(args, kwds, &arg_idx);
|
||||
if (pos_result.valid) {
|
||||
x = pos_result.x;
|
||||
y = pos_result.y;
|
||||
}
|
||||
|
||||
// Set size if we got it, otherwise calculate default
|
||||
auto size_result = PyArgHelpers::parseSize(args, kwds, &arg_idx);
|
||||
if (size_result.valid) {
|
||||
w = size_result.w;
|
||||
h = size_result.h;
|
||||
} else {
|
||||
// Default size based on grid dimensions and texture
|
||||
w = grid_x * 16.0f; // Will be recalculated if texture provided
|
||||
// Default size based on grid dimensions
|
||||
w = grid_x * 16.0f;
|
||||
h = grid_y * 16.0f;
|
||||
}
|
||||
|
||||
|
|
@ -380,10 +522,8 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
|||
&textureObj);
|
||||
Py_DECREF(remaining_args);
|
||||
}
|
||||
// Case 2: Traditional format
|
||||
// Traditional format parsing
|
||||
else {
|
||||
PyErr_Clear(); // Clear any errors from helpers
|
||||
|
||||
static const char* keywords[] = {
|
||||
"grid_x", "grid_y", "texture", "pos", "size", "grid_size", nullptr
|
||||
};
|
||||
|
|
@ -406,7 +546,13 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
|||
if (PyLong_Check(x_obj) && PyLong_Check(y_obj)) {
|
||||
grid_x = PyLong_AsLong(x_obj);
|
||||
grid_y = PyLong_AsLong(y_obj);
|
||||
} else {
|
||||
PyErr_SetString(PyExc_TypeError, "grid_size must contain integers");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
PyErr_SetString(PyExc_TypeError, "grid_size must be a tuple of two integers");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -419,7 +565,13 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
|||
(PyFloat_Check(y_val) || PyLong_Check(y_val))) {
|
||||
x = PyFloat_Check(x_val) ? PyFloat_AsDouble(x_val) : PyLong_AsLong(x_val);
|
||||
y = PyFloat_Check(y_val) ? PyFloat_AsDouble(y_val) : PyLong_AsLong(y_val);
|
||||
} else {
|
||||
PyErr_SetString(PyExc_TypeError, "pos must contain numbers");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
PyErr_SetString(PyExc_TypeError, "pos must be a tuple of two numbers");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -432,7 +584,13 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
|||
(PyFloat_Check(h_val) || PyLong_Check(h_val))) {
|
||||
w = PyFloat_Check(w_val) ? PyFloat_AsDouble(w_val) : PyLong_AsLong(w_val);
|
||||
h = PyFloat_Check(h_val) ? PyFloat_AsDouble(h_val) : PyLong_AsLong(h_val);
|
||||
} else {
|
||||
PyErr_SetString(PyExc_TypeError, "size must contain numbers");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
PyErr_SetString(PyExc_TypeError, "size must be a tuple of two numbers");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// Default size based on grid
|
||||
|
|
@ -440,17 +598,20 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
|||
h = grid_y * 16.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate grid dimensions
|
||||
if (grid_x <= 0 || grid_y <= 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "Grid dimensions must be positive integers");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// At this point we have x, y, w, h values from either parsing method
|
||||
|
||||
// Convert PyObject texture to IndexTexture*
|
||||
// This requires the texture object to have been initialized similar to UISprite's texture handling
|
||||
|
||||
// Convert PyObject texture to shared_ptr<PyTexture>
|
||||
std::shared_ptr<PyTexture> texture_ptr = nullptr;
|
||||
|
||||
// Allow None for texture - use default texture in that case
|
||||
if (textureObj != Py_None) {
|
||||
//if (!PyObject_IsInstance(textureObj, (PyObject*)&PyTextureType)) {
|
||||
// Allow None or NULL for texture - use default texture in that case
|
||||
if (textureObj && textureObj != Py_None) {
|
||||
if (!PyObject_IsInstance(textureObj, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))) {
|
||||
PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance or None");
|
||||
return -1;
|
||||
|
|
@ -458,16 +619,12 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
|||
PyTextureObject* pyTexture = reinterpret_cast<PyTextureObject*>(textureObj);
|
||||
texture_ptr = pyTexture->data;
|
||||
} else {
|
||||
// Use default texture when None is provided
|
||||
// Use default texture when None is provided or texture not specified
|
||||
texture_ptr = McRFPy_API::default_texture;
|
||||
}
|
||||
|
||||
// Initialize UIGrid - texture_ptr will be nullptr if texture was None
|
||||
//self->data = new UIGrid(grid_x, grid_y, texture, sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h));
|
||||
//self->data = std::make_shared<UIGrid>(grid_x, grid_y, pyTexture->data,
|
||||
// sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h));
|
||||
// Adjust size based on texture if available and size not explicitly set
|
||||
if (!size_result.valid && texture_ptr) {
|
||||
if (texture_ptr && w == grid_x * 16.0f && h == grid_y * 16.0f) {
|
||||
w = grid_x * texture_ptr->sprite_width;
|
||||
h = grid_y * texture_ptr->sprite_height;
|
||||
}
|
||||
|
|
@ -719,8 +876,124 @@ int UIGrid::set_fill_color(PyUIGridObject* self, PyObject* value, void* closure)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Python API implementations for TCOD functionality
|
||||
PyObject* UIGrid::py_compute_fov(PyUIGridObject* self, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
static char* kwlist[] = {"x", "y", "radius", "light_walls", "algorithm", NULL};
|
||||
int x, y, radius = 0;
|
||||
int light_walls = 1;
|
||||
int algorithm = FOV_BASIC;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii|ipi", kwlist,
|
||||
&x, &y, &radius, &light_walls, &algorithm)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->data->computeFOV(x, y, radius, light_walls, (TCOD_fov_algorithm_t)algorithm);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject* UIGrid::py_is_in_fov(PyUIGridObject* self, PyObject* args)
|
||||
{
|
||||
int x, y;
|
||||
if (!PyArg_ParseTuple(args, "ii", &x, &y)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool in_fov = self->data->isInFOV(x, y);
|
||||
return PyBool_FromLong(in_fov);
|
||||
}
|
||||
|
||||
PyObject* UIGrid::py_find_path(PyUIGridObject* self, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
static char* kwlist[] = {"x1", "y1", "x2", "y2", "diagonal_cost", NULL};
|
||||
int x1, y1, x2, y2;
|
||||
float diagonal_cost = 1.41f;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiii|f", kwlist,
|
||||
&x1, &y1, &x2, &y2, &diagonal_cost)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, int>> path = self->data->findPath(x1, y1, x2, y2, diagonal_cost);
|
||||
|
||||
PyObject* path_list = PyList_New(path.size());
|
||||
if (!path_list) return NULL;
|
||||
|
||||
for (size_t i = 0; i < path.size(); i++) {
|
||||
PyObject* coord = Py_BuildValue("(ii)", path[i].first, path[i].second);
|
||||
if (!coord) {
|
||||
Py_DECREF(path_list);
|
||||
return NULL;
|
||||
}
|
||||
PyList_SET_ITEM(path_list, i, coord);
|
||||
}
|
||||
|
||||
return path_list;
|
||||
}
|
||||
|
||||
PyObject* UIGrid::py_compute_dijkstra(PyUIGridObject* self, PyObject* args, PyObject* kwds)
|
||||
{
|
||||
static char* kwlist[] = {"root_x", "root_y", "diagonal_cost", NULL};
|
||||
int root_x, root_y;
|
||||
float diagonal_cost = 1.41f;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii|f", kwlist,
|
||||
&root_x, &root_y, &diagonal_cost)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->data->computeDijkstra(root_x, root_y, diagonal_cost);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject* UIGrid::py_get_dijkstra_distance(PyUIGridObject* self, PyObject* args)
|
||||
{
|
||||
int x, y;
|
||||
if (!PyArg_ParseTuple(args, "ii", &x, &y)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
float distance = self->data->getDijkstraDistance(x, y);
|
||||
if (distance < 0) {
|
||||
Py_RETURN_NONE; // Invalid position
|
||||
}
|
||||
|
||||
return PyFloat_FromDouble(distance);
|
||||
}
|
||||
|
||||
PyObject* UIGrid::py_get_dijkstra_path(PyUIGridObject* self, PyObject* args)
|
||||
{
|
||||
int x, y;
|
||||
if (!PyArg_ParseTuple(args, "ii", &x, &y)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, int>> path = self->data->getDijkstraPath(x, y);
|
||||
|
||||
PyObject* path_list = PyList_New(path.size());
|
||||
for (size_t i = 0; i < path.size(); i++) {
|
||||
PyObject* pos = Py_BuildValue("(ii)", path[i].first, path[i].second);
|
||||
PyList_SetItem(path_list, i, pos); // Steals reference
|
||||
}
|
||||
|
||||
return path_list;
|
||||
}
|
||||
|
||||
PyMethodDef UIGrid::methods[] = {
|
||||
{"at", (PyCFunction)UIGrid::py_at, METH_VARARGS | METH_KEYWORDS},
|
||||
{"compute_fov", (PyCFunction)UIGrid::py_compute_fov, METH_VARARGS | METH_KEYWORDS,
|
||||
"Compute field of view from a position. Args: x, y, radius=0, light_walls=True, algorithm=FOV_BASIC"},
|
||||
{"is_in_fov", (PyCFunction)UIGrid::py_is_in_fov, METH_VARARGS,
|
||||
"Check if a cell is in the field of view. Args: x, y"},
|
||||
{"find_path", (PyCFunction)UIGrid::py_find_path, METH_VARARGS | METH_KEYWORDS,
|
||||
"Find A* path between two points. Args: x1, y1, x2, y2, diagonal_cost=1.41"},
|
||||
{"compute_dijkstra", (PyCFunction)UIGrid::py_compute_dijkstra, METH_VARARGS | METH_KEYWORDS,
|
||||
"Compute Dijkstra map from root position. Args: root_x, root_y, diagonal_cost=1.41"},
|
||||
{"get_dijkstra_distance", (PyCFunction)UIGrid::py_get_dijkstra_distance, METH_VARARGS,
|
||||
"Get distance from Dijkstra root to position. Args: x, y. Returns float or None if invalid."},
|
||||
{"get_dijkstra_path", (PyCFunction)UIGrid::py_get_dijkstra_path, METH_VARARGS,
|
||||
"Get path from position to Dijkstra root. Args: x, y. Returns list of (x,y) tuples."},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
|
|
@ -731,6 +1004,18 @@ typedef PyUIGridObject PyObjectType;
|
|||
PyMethodDef UIGrid_all_methods[] = {
|
||||
UIDRAWABLE_METHODS,
|
||||
{"at", (PyCFunction)UIGrid::py_at, METH_VARARGS | METH_KEYWORDS},
|
||||
{"compute_fov", (PyCFunction)UIGrid::py_compute_fov, METH_VARARGS | METH_KEYWORDS,
|
||||
"Compute field of view from a position. Args: x, y, radius=0, light_walls=True, algorithm=FOV_BASIC"},
|
||||
{"is_in_fov", (PyCFunction)UIGrid::py_is_in_fov, METH_VARARGS,
|
||||
"Check if a cell is in the field of view. Args: x, y"},
|
||||
{"find_path", (PyCFunction)UIGrid::py_find_path, METH_VARARGS | METH_KEYWORDS,
|
||||
"Find A* path between two points. Args: x1, y1, x2, y2, diagonal_cost=1.41"},
|
||||
{"compute_dijkstra", (PyCFunction)UIGrid::py_compute_dijkstra, METH_VARARGS | METH_KEYWORDS,
|
||||
"Compute Dijkstra map from root position. Args: root_x, root_y, diagonal_cost=1.41"},
|
||||
{"get_dijkstra_distance", (PyCFunction)UIGrid::py_get_dijkstra_distance, METH_VARARGS,
|
||||
"Get distance from Dijkstra root to position. Args: x, y. Returns float or None if invalid."},
|
||||
{"get_dijkstra_path", (PyCFunction)UIGrid::py_get_dijkstra_path, METH_VARARGS,
|
||||
"Get path from position to Dijkstra root. Args: x, y. Returns list of (x,y) tuples."},
|
||||
{NULL} // Sentinel
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue