pathfinding on heightmap
This commit is contained in:
parent
e572269eac
commit
63008bdefd
6 changed files with 1350 additions and 0 deletions
225
src/3d/VoxelPoint.cpp
Normal file
225
src/3d/VoxelPoint.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
// VoxelPoint.cpp - Navigation grid cell implementation
|
||||
|
||||
#include "VoxelPoint.h"
|
||||
#include "Viewport3D.h"
|
||||
#include <cstdio>
|
||||
|
||||
// Default constructor
|
||||
VoxelPoint::VoxelPoint()
|
||||
: walkable(true)
|
||||
, transparent(true)
|
||||
, height(0.0f)
|
||||
, cost(1.0f)
|
||||
, grid_x(0)
|
||||
, grid_z(0)
|
||||
, parent_viewport(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor with position
|
||||
VoxelPoint::VoxelPoint(int x, int z, mcrf::Viewport3D* parent)
|
||||
: walkable(true)
|
||||
, transparent(true)
|
||||
, height(0.0f)
|
||||
, cost(1.0f)
|
||||
, grid_x(x)
|
||||
, grid_z(z)
|
||||
, parent_viewport(parent)
|
||||
{
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Python Property Accessors
|
||||
// =============================================================================
|
||||
|
||||
// Member offsets for bool properties
|
||||
enum VoxelPointBoolMember {
|
||||
VOXEL_WALKABLE = 0,
|
||||
VOXEL_TRANSPARENT = 1
|
||||
};
|
||||
|
||||
// Member offsets for float properties
|
||||
enum VoxelPointFloatMember {
|
||||
VOXEL_HEIGHT = 0,
|
||||
VOXEL_COST = 1
|
||||
};
|
||||
|
||||
PyObject* VoxelPoint::get_bool_member(PyVoxelPointObject* self, void* closure)
|
||||
{
|
||||
if (!self->data) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "VoxelPoint data is null");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
long member = reinterpret_cast<long>(closure);
|
||||
bool value = false;
|
||||
|
||||
switch (member) {
|
||||
case VOXEL_WALKABLE:
|
||||
value = self->data->walkable;
|
||||
break;
|
||||
case VOXEL_TRANSPARENT:
|
||||
value = self->data->transparent;
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_AttributeError, "Invalid bool member");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return PyBool_FromLong(value ? 1 : 0);
|
||||
}
|
||||
|
||||
int VoxelPoint::set_bool_member(PyVoxelPointObject* self, PyObject* value, void* closure)
|
||||
{
|
||||
if (!self->data) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "VoxelPoint data is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!PyBool_Check(value) && !PyLong_Check(value)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Value must be a boolean");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool newValue = PyObject_IsTrue(value);
|
||||
long member = reinterpret_cast<long>(closure);
|
||||
|
||||
switch (member) {
|
||||
case VOXEL_WALKABLE:
|
||||
self->data->walkable = newValue;
|
||||
break;
|
||||
case VOXEL_TRANSPARENT:
|
||||
self->data->transparent = newValue;
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_AttributeError, "Invalid bool member");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Trigger TCOD synchronization
|
||||
if (self->data->parent_viewport) {
|
||||
self->data->parent_viewport->syncTCODCell(self->data->grid_x, self->data->grid_z);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* VoxelPoint::get_float_member(PyVoxelPointObject* self, void* closure)
|
||||
{
|
||||
if (!self->data) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "VoxelPoint data is null");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
long member = reinterpret_cast<long>(closure);
|
||||
float value = 0.0f;
|
||||
|
||||
switch (member) {
|
||||
case VOXEL_HEIGHT:
|
||||
value = self->data->height;
|
||||
break;
|
||||
case VOXEL_COST:
|
||||
value = self->data->cost;
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_AttributeError, "Invalid float member");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return PyFloat_FromDouble(value);
|
||||
}
|
||||
|
||||
int VoxelPoint::set_float_member(PyVoxelPointObject* self, PyObject* value, void* closure)
|
||||
{
|
||||
if (!self->data) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "VoxelPoint data is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
double newValue;
|
||||
if (PyFloat_Check(value)) {
|
||||
newValue = PyFloat_AsDouble(value);
|
||||
} else if (PyLong_Check(value)) {
|
||||
newValue = static_cast<double>(PyLong_AsLong(value));
|
||||
} else {
|
||||
PyErr_SetString(PyExc_TypeError, "Value must be a number");
|
||||
return -1;
|
||||
}
|
||||
|
||||
long member = reinterpret_cast<long>(closure);
|
||||
|
||||
switch (member) {
|
||||
case VOXEL_HEIGHT:
|
||||
self->data->height = static_cast<float>(newValue);
|
||||
break;
|
||||
case VOXEL_COST:
|
||||
if (newValue < 0.0) {
|
||||
PyErr_SetString(PyExc_ValueError, "Cost must be non-negative");
|
||||
return -1;
|
||||
}
|
||||
self->data->cost = static_cast<float>(newValue);
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_AttributeError, "Invalid float member");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject* VoxelPoint::get_grid_pos(PyVoxelPointObject* self, void* closure)
|
||||
{
|
||||
if (!self->data) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "VoxelPoint data is null");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return Py_BuildValue("(ii)", self->data->grid_x, self->data->grid_z);
|
||||
}
|
||||
|
||||
PyObject* VoxelPoint::get_entities(PyVoxelPointObject* self, void* closure)
|
||||
{
|
||||
// TODO: Implement when Entity3D is created
|
||||
// For now, return an empty list
|
||||
return PyList_New(0);
|
||||
}
|
||||
|
||||
PyObject* VoxelPoint::repr(PyVoxelPointObject* self)
|
||||
{
|
||||
if (!self->data) {
|
||||
return PyUnicode_FromString("<VoxelPoint (null)>");
|
||||
}
|
||||
|
||||
// Use snprintf then PyUnicode_FromString because PyUnicode_FromFormat doesn't support %.2f
|
||||
char buffer[128];
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"<VoxelPoint (%d, %d) walkable=%s transparent=%s height=%.2f cost=%.2f>",
|
||||
self->data->grid_x,
|
||||
self->data->grid_z,
|
||||
self->data->walkable ? "True" : "False",
|
||||
self->data->transparent ? "True" : "False",
|
||||
self->data->height,
|
||||
self->data->cost
|
||||
);
|
||||
return PyUnicode_FromString(buffer);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Python GetSetDef Table
|
||||
// =============================================================================
|
||||
|
||||
PyGetSetDef VoxelPoint::getsetters[] = {
|
||||
{"walkable", (getter)VoxelPoint::get_bool_member, (setter)VoxelPoint::set_bool_member,
|
||||
"Whether entities can walk through this cell.", (void*)VOXEL_WALKABLE},
|
||||
{"transparent", (getter)VoxelPoint::get_bool_member, (setter)VoxelPoint::set_bool_member,
|
||||
"Whether FOV can see through this cell.", (void*)VOXEL_TRANSPARENT},
|
||||
{"height", (getter)VoxelPoint::get_float_member, (setter)VoxelPoint::set_float_member,
|
||||
"World-space Y coordinate from terrain.", (void*)VOXEL_HEIGHT},
|
||||
{"cost", (getter)VoxelPoint::get_float_member, (setter)VoxelPoint::set_float_member,
|
||||
"Movement cost multiplier (1.0 = normal).", (void*)VOXEL_COST},
|
||||
{"grid_pos", (getter)VoxelPoint::get_grid_pos, NULL,
|
||||
"Grid coordinates as (x, z) tuple (read-only).", NULL},
|
||||
{"entities", (getter)VoxelPoint::get_entities, NULL,
|
||||
"List of Entity3D objects at this cell (read-only).", NULL},
|
||||
{NULL} // Sentinel
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue