feat: Add GridPoint.entities and GridPointState.point properties
GridPoint.entities (#114): - Returns list of entities at this grid cell position - Enables convenient cell-based entity queries without manual iteration - Example: grid.at(5, 5).entities → [<Entity>, <Entity>] GridPointState.point (#16): - Returns GridPoint if entity has discovered this cell, None otherwise - Respects entity's perspective: undiscovered cells return None - Enables entity.at(x,y).point.walkable style access - Live reference: changes to GridPoint are immediately visible This provides a simpler solution for #16 without the complexity of caching stale GridPoint copies. The visible/discovered flags indicate whether the entity "should" trust the data; Python can implement memory systems if needed. closes #114, closes #16 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
a529e5eac3
commit
f33e79a123
5 changed files with 367 additions and 6 deletions
|
|
@ -120,9 +120,12 @@ PyObject* UIEntity::at(PyUIEntityObject* self, PyObject* o) {
|
|||
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState");
|
||||
auto obj = (PyUIGridPointStateObject*)type->tp_alloc(type, 0);
|
||||
Py_DECREF(type);
|
||||
obj->data = &(self->data->gridstate[y * self->data->grid->grid_x + x]);
|
||||
obj->grid = self->data->grid;
|
||||
obj->entity = self->data;
|
||||
obj->x = x; // #16 - Store position for .point property
|
||||
obj->y = y;
|
||||
return (PyObject*)obj;
|
||||
}
|
||||
|
||||
|
|
@ -312,23 +315,29 @@ sf::Vector2i PyObject_to_sfVector2i(PyObject* obj) {
|
|||
}
|
||||
|
||||
PyObject* UIGridPointState_to_PyObject(const UIGridPointState& state) {
|
||||
// Create a new GridPointState Python object
|
||||
// Create a new GridPointState Python object (detached - no grid/entity context)
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState");
|
||||
if (!type) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
auto obj = (PyUIGridPointStateObject*)type->tp_alloc(type, 0);
|
||||
if (!obj) {
|
||||
Py_DECREF(type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// Allocate new data and copy values
|
||||
obj->data = new UIGridPointState();
|
||||
obj->data->visible = state.visible;
|
||||
obj->data->discovered = state.discovered;
|
||||
|
||||
|
||||
// Initialize context fields (detached state has no grid/entity context)
|
||||
obj->grid = nullptr;
|
||||
obj->entity = nullptr;
|
||||
obj->x = -1;
|
||||
obj->y = -1;
|
||||
|
||||
Py_DECREF(type);
|
||||
return (PyObject*)obj;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "UIGridPoint.h"
|
||||
#include "UIGrid.h"
|
||||
#include "UIEntity.h" // #114 - for GridPoint.entities
|
||||
#include "GridLayers.h" // #150 - for GridLayerType, ColorLayer, TileLayer
|
||||
#include <cstring> // #150 - for strcmp
|
||||
|
||||
|
|
@ -90,9 +91,52 @@ int UIGridPoint::set_bool_member(PyUIGridPointObject* self, PyObject* value, voi
|
|||
|
||||
// #150 - Removed get_int_member/set_int_member - now handled by layers
|
||||
|
||||
// #114 - Get list of entities at this grid cell
|
||||
PyObject* UIGridPoint::get_entities(PyUIGridPointObject* self, void* closure) {
|
||||
if (!self->grid) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "GridPoint has no parent grid");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int target_x = self->data->grid_x;
|
||||
int target_y = self->data->grid_y;
|
||||
|
||||
PyObject* list = PyList_New(0);
|
||||
if (!list) return NULL;
|
||||
|
||||
// Iterate through grid's entities and find those at this position
|
||||
for (auto& entity : *(self->grid->entities)) {
|
||||
if (static_cast<int>(entity->position.x) == target_x &&
|
||||
static_cast<int>(entity->position.y) == target_y) {
|
||||
// Create Python Entity object for this entity
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity");
|
||||
if (!type) {
|
||||
Py_DECREF(list);
|
||||
return NULL;
|
||||
}
|
||||
auto obj = (PyUIEntityObject*)type->tp_alloc(type, 0);
|
||||
Py_DECREF(type);
|
||||
if (!obj) {
|
||||
Py_DECREF(list);
|
||||
return NULL;
|
||||
}
|
||||
obj->data = entity;
|
||||
if (PyList_Append(list, (PyObject*)obj) < 0) {
|
||||
Py_DECREF(obj);
|
||||
Py_DECREF(list);
|
||||
return NULL;
|
||||
}
|
||||
Py_DECREF(obj); // List now owns the reference
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
PyGetSetDef UIGridPoint::getsetters[] = {
|
||||
{"walkable", (getter)UIGridPoint::get_bool_member, (setter)UIGridPoint::set_bool_member, "Is the GridPoint walkable", (void*)0},
|
||||
{"transparent", (getter)UIGridPoint::get_bool_member, (setter)UIGridPoint::set_bool_member, "Is the GridPoint transparent", (void*)1},
|
||||
{"entities", (getter)UIGridPoint::get_entities, NULL, "List of entities at this grid cell (read-only)", NULL},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
|
|
@ -137,9 +181,43 @@ int UIGridPointState::set_bool_member(PyUIGridPointStateObject* self, PyObject*
|
|||
return 0;
|
||||
}
|
||||
|
||||
// #16 - Get GridPoint at this position (None if not discovered)
|
||||
PyObject* UIGridPointState::get_point(PyUIGridPointStateObject* self, void* closure) {
|
||||
// Return None if entity hasn't discovered this cell
|
||||
if (!self->data->discovered) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
if (!self->grid) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "GridPointState has no parent grid");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return the GridPoint at this position
|
||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPoint");
|
||||
if (!type) return NULL;
|
||||
|
||||
auto obj = (PyUIGridPointObject*)type->tp_alloc(type, 0);
|
||||
Py_DECREF(type);
|
||||
if (!obj) return NULL;
|
||||
|
||||
// Get the GridPoint from the grid
|
||||
int idx = self->y * self->grid->grid_x + self->x;
|
||||
if (idx < 0 || idx >= static_cast<int>(self->grid->points.size())) {
|
||||
Py_DECREF(obj);
|
||||
PyErr_SetString(PyExc_IndexError, "GridPointState position out of bounds");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj->data = &(self->grid->points[idx]);
|
||||
obj->grid = self->grid;
|
||||
return (PyObject*)obj;
|
||||
}
|
||||
|
||||
PyGetSetDef UIGridPointState::getsetters[] = {
|
||||
{"visible", (getter)UIGridPointState::get_bool_member, (setter)UIGridPointState::set_bool_member, "Is the GridPointState visible", (void*)0},
|
||||
{"discovered", (getter)UIGridPointState::get_bool_member, (setter)UIGridPointState::set_bool_member, "Has the GridPointState been discovered", (void*)1},
|
||||
{"point", (getter)UIGridPointState::get_point, NULL, "GridPoint at this position (None if not discovered)", NULL},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
|
|
@ -232,7 +310,7 @@ int UIGridPoint::setattro(PyUIGridPointObject* self, PyObject* name, PyObject* v
|
|||
sf::Color color = PyObject_to_sfColor(value);
|
||||
if (PyErr_Occurred()) return -1;
|
||||
color_layer->at(x, y) = color;
|
||||
color_layer->markDirty();
|
||||
color_layer->markDirty(x, y); // Mark only the affected chunk
|
||||
return 0;
|
||||
} else if (layer->type == GridLayerType::Tile) {
|
||||
auto tile_layer = std::static_pointer_cast<TileLayer>(layer);
|
||||
|
|
@ -241,7 +319,7 @@ int UIGridPoint::setattro(PyUIGridPointObject* self, PyObject* name, PyObject* v
|
|||
return -1;
|
||||
}
|
||||
tile_layer->at(x, y) = PyLong_AsLong(value);
|
||||
tile_layer->markDirty();
|
||||
tile_layer->markDirty(x, y); // Mark only the affected chunk
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ typedef struct {
|
|||
UIGridPointState* data;
|
||||
std::shared_ptr<UIGrid> grid;
|
||||
std::shared_ptr<UIEntity> entity;
|
||||
int x, y; // Position in grid (needed for .point property)
|
||||
} PyUIGridPointStateObject;
|
||||
|
||||
// UIGridPoint - grid cell data for pathfinding and layer access
|
||||
|
|
@ -49,6 +50,9 @@ public:
|
|||
static PyObject* get_bool_member(PyUIGridPointObject* self, void* closure);
|
||||
static PyObject* repr(PyUIGridPointObject* self);
|
||||
|
||||
// #114 - entities property: list of entities at this cell
|
||||
static PyObject* get_entities(PyUIGridPointObject* self, void* closure);
|
||||
|
||||
// #150 - Dynamic property access for named layers
|
||||
static PyObject* getattro(PyUIGridPointObject* self, PyObject* name);
|
||||
static int setattro(PyUIGridPointObject* self, PyObject* name, PyObject* value);
|
||||
|
|
@ -64,6 +68,9 @@ public:
|
|||
static int set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure);
|
||||
static PyGetSetDef getsetters[];
|
||||
static PyObject* repr(PyUIGridPointStateObject* self);
|
||||
|
||||
// #16 - point property: access to GridPoint (None if not discovered)
|
||||
static PyObject* get_point(PyUIGridPointStateObject* self, void* closure);
|
||||
};
|
||||
|
||||
namespace mcrfpydef {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue