diff --git a/src/UIEntity.cpp b/src/UIEntity.cpp index 85c6d5f..88f1563 100644 --- a/src/UIEntity.cpp +++ b/src/UIEntity.cpp @@ -413,6 +413,92 @@ int UIEntity::set_float_member(PyUIEntityObject* self, PyObject* value, void* cl return 0; } +PyObject* UIEntity::get_grid(PyUIEntityObject* self, void* closure) +{ + if (!self->data || !self->data->grid) { + Py_RETURN_NONE; + } + + // Return a Python Grid object wrapping the C++ grid + PyTypeObject* grid_type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"); + if (!grid_type) return nullptr; + + auto pyGrid = (PyUIGridObject*)grid_type->tp_alloc(grid_type, 0); + Py_DECREF(grid_type); + + if (pyGrid) { + pyGrid->data = self->data->grid; + pyGrid->weakreflist = NULL; + } + return (PyObject*)pyGrid; +} + +int UIEntity::set_grid(PyUIEntityObject* self, PyObject* value, void* closure) +{ + if (!self->data) { + PyErr_SetString(PyExc_RuntimeError, "Invalid Entity object"); + return -1; + } + + // Handle None - remove from current grid + if (value == Py_None) { + if (self->data->grid) { + // Remove from current grid's entity list + auto& entities = self->data->grid->entities; + auto it = std::find_if(entities->begin(), entities->end(), + [self](const std::shared_ptr& e) { + return e.get() == self->data.get(); + }); + if (it != entities->end()) { + entities->erase(it); + } + self->data->grid.reset(); + } + return 0; + } + + // Value must be a Grid + PyTypeObject* grid_type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"); + bool is_grid = grid_type && PyObject_IsInstance(value, (PyObject*)grid_type); + Py_XDECREF(grid_type); + + if (!is_grid) { + PyErr_SetString(PyExc_TypeError, "grid must be a Grid or None"); + return -1; + } + + auto new_grid = ((PyUIGridObject*)value)->data; + + // Remove from old grid first (if any) + if (self->data->grid && self->data->grid != new_grid) { + auto& old_entities = self->data->grid->entities; + auto it = std::find_if(old_entities->begin(), old_entities->end(), + [self](const std::shared_ptr& e) { + return e.get() == self->data.get(); + }); + if (it != old_entities->end()) { + old_entities->erase(it); + } + } + + // Add to new grid + if (self->data->grid != new_grid) { + new_grid->entities->push_back(self->data); + self->data->grid = new_grid; + + // Initialize gridstate if needed + if (self->data->gridstate.size() == 0) { + self->data->gridstate.resize(new_grid->grid_x * new_grid->grid_y); + for (auto& state : self->data->gridstate) { + state.visible = false; + state.discovered = false; + } + } + } + + return 0; +} + PyObject* UIEntity::die(PyUIEntityObject* self, PyObject* Py_UNUSED(ignored)) { // Check if entity has a grid @@ -557,6 +643,10 @@ PyGetSetDef UIEntity::getsetters[] = { {"draw_pos", (getter)UIEntity::get_position, (setter)UIEntity::set_position, "Entity position (graphically)", (void*)0}, {"pos", (getter)UIEntity::get_position, (setter)UIEntity::set_position, "Entity position (integer grid coordinates)", (void*)1}, {"gridstate", (getter)UIEntity::get_gridstate, NULL, "Grid point states for the entity", NULL}, + {"grid", (getter)UIEntity::get_grid, (setter)UIEntity::set_grid, + "Grid this entity belongs to. " + "Get: Returns the Grid or None. " + "Set: Assign a Grid to move entity, or None to remove from grid.", NULL}, {"sprite_index", (getter)UIEntity::get_spritenumber, (setter)UIEntity::set_spritenumber, "Sprite index on the texture on the display", NULL}, {"sprite_number", (getter)UIEntity::get_spritenumber, (setter)UIEntity::set_spritenumber, "Sprite index (DEPRECATED: use sprite_index instead)", NULL}, {"x", (getter)UIEntity::get_float_member, (setter)UIEntity::set_float_member, "Entity x position", (void*)0}, diff --git a/src/UIEntity.h b/src/UIEntity.h index fa92330..6af7511 100644 --- a/src/UIEntity.h +++ b/src/UIEntity.h @@ -98,6 +98,8 @@ public: static int set_spritenumber(PyUIEntityObject* self, PyObject* value, void* closure); static PyObject* get_float_member(PyUIEntityObject* self, void* closure); static int set_float_member(PyUIEntityObject* self, PyObject* value, void* closure); + static PyObject* get_grid(PyUIEntityObject* self, void* closure); + static int set_grid(PyUIEntityObject* self, PyObject* value, void* closure); static PyMethodDef methods[]; static PyGetSetDef getsetters[]; static PyObject* repr(PyUIEntityObject* self); diff --git a/src/UIGrid.cpp b/src/UIGrid.cpp index 033b542..4811d54 100644 --- a/src/UIGrid.cpp +++ b/src/UIGrid.cpp @@ -1846,20 +1846,33 @@ PySequenceMethods UIEntityCollection::sqmethods = { PyObject* UIEntityCollection::append(PyUIEntityCollectionObject* self, PyObject* o) { - // if not UIDrawable subclass, reject it - // self->data->push_back( c++ object inside o ); - - // this would be a great use case for .tp_base - //if (!PyObject_IsInstance(o, (PyObject*)&PyUIEntityType)) + // Type check - must be Entity if (!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity"))) { PyErr_SetString(PyExc_TypeError, "Only Entity objects can be added to EntityCollection"); return NULL; } PyUIEntityObject* entity = (PyUIEntityObject*)o; - self->data->push_back(entity->data); - entity->data->grid = self->grid; - + + // Remove from old grid first (if different from target grid) + // This implements the documented "single grid only" behavior + if (entity->data->grid && entity->data->grid != self->grid) { + auto& old_entities = entity->data->grid->entities; + auto it = std::find_if(old_entities->begin(), old_entities->end(), + [entity](const std::shared_ptr& e) { + return e.get() == entity->data.get(); + }); + if (it != old_entities->end()) { + old_entities->erase(it); + } + } + + // Add to this grid (if not already in it) + if (entity->data->grid != self->grid) { + self->data->push_back(entity->data); + entity->data->grid = self->grid; + } + // Initialize gridstate if not already done if (entity->data->gridstate.size() == 0 && self->grid) { entity->data->gridstate.resize(self->grid->grid_x * self->grid->grid_y);