bugfixes for .parent property - partial #183 solution
This commit is contained in:
parent
a4c2c04343
commit
7e47050d6f
9 changed files with 357 additions and 149 deletions
|
|
@ -42,8 +42,8 @@ static PyObject* McRFPy_Libtcod::compute_fov(PyObject* self, PyObject* args) {
|
||||||
|
|
||||||
// Return list of visible cells
|
// Return list of visible cells
|
||||||
PyObject* visible_list = PyList_New(0);
|
PyObject* visible_list = PyList_New(0);
|
||||||
for (int gy = 0; gy < grid->grid_y; gy++) {
|
for (int gy = 0; gy < grid->grid_h; gy++) {
|
||||||
for (int gx = 0; gx < grid->grid_x; gx++) {
|
for (int gx = 0; gx < grid->grid_w; gx++) {
|
||||||
if (grid->isInFOV(gx, gy)) {
|
if (grid->isInFOV(gx, gy)) {
|
||||||
PyObject* pos = Py_BuildValue("(ii)", gx, gy);
|
PyObject* pos = Py_BuildValue("(ii)", gx, gy);
|
||||||
PyList_Append(visible_list, pos);
|
PyList_Append(visible_list, pos);
|
||||||
|
|
|
||||||
|
|
@ -1128,25 +1128,23 @@ PyObject* UICollection::find(PyUICollectionObject* self, PyObject* args, PyObjec
|
||||||
if (recursive && drawable->derived_type() == PyObjectsEnum::UIFRAME) {
|
if (recursive && drawable->derived_type() == PyObjectsEnum::UIFRAME) {
|
||||||
auto frame = std::static_pointer_cast<UIFrame>(drawable);
|
auto frame = std::static_pointer_cast<UIFrame>(drawable);
|
||||||
// Create temporary collection object for recursive call
|
// Create temporary collection object for recursive call
|
||||||
PyTypeObject* collType = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "UICollection");
|
// Use the type directly from namespace (#189 - type not exported to module)
|
||||||
if (collType) {
|
PyTypeObject* collType = &PyUICollectionType;
|
||||||
PyUICollectionObject* child_coll = (PyUICollectionObject*)collType->tp_alloc(collType, 0);
|
PyUICollectionObject* child_coll = (PyUICollectionObject*)collType->tp_alloc(collType, 0);
|
||||||
if (child_coll) {
|
if (child_coll) {
|
||||||
child_coll->data = frame->children;
|
child_coll->data = frame->children;
|
||||||
PyObject* child_results = find(child_coll, args, kwds);
|
PyObject* child_results = find(child_coll, args, kwds);
|
||||||
if (child_results && PyList_Check(child_results)) {
|
if (child_results && PyList_Check(child_results)) {
|
||||||
// Extend results with child results
|
// Extend results with child results
|
||||||
for (Py_ssize_t i = 0; i < PyList_Size(child_results); i++) {
|
for (Py_ssize_t i = 0; i < PyList_Size(child_results); i++) {
|
||||||
PyObject* item = PyList_GetItem(child_results, i);
|
PyObject* item = PyList_GetItem(child_results, i);
|
||||||
Py_INCREF(item);
|
Py_INCREF(item);
|
||||||
PyList_Append(results, item);
|
PyList_Append(results, item);
|
||||||
Py_DECREF(item);
|
Py_DECREF(item);
|
||||||
}
|
|
||||||
Py_DECREF(child_results);
|
|
||||||
}
|
}
|
||||||
Py_DECREF(child_coll);
|
Py_DECREF(child_results);
|
||||||
}
|
}
|
||||||
Py_DECREF(collType);
|
Py_DECREF(child_coll);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1162,21 +1160,17 @@ PyObject* UICollection::find(PyUICollectionObject* self, PyObject* args, PyObjec
|
||||||
// Recursive search into Frame children
|
// Recursive search into Frame children
|
||||||
if (recursive && drawable->derived_type() == PyObjectsEnum::UIFRAME) {
|
if (recursive && drawable->derived_type() == PyObjectsEnum::UIFRAME) {
|
||||||
auto frame = std::static_pointer_cast<UIFrame>(drawable);
|
auto frame = std::static_pointer_cast<UIFrame>(drawable);
|
||||||
PyTypeObject* collType = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "UICollection");
|
// Use the type directly from namespace (#189 - type not exported to module)
|
||||||
if (collType) {
|
PyTypeObject* collType = &PyUICollectionType;
|
||||||
PyUICollectionObject* child_coll = (PyUICollectionObject*)collType->tp_alloc(collType, 0);
|
PyUICollectionObject* child_coll = (PyUICollectionObject*)collType->tp_alloc(collType, 0);
|
||||||
if (child_coll) {
|
if (child_coll) {
|
||||||
child_coll->data = frame->children;
|
child_coll->data = frame->children;
|
||||||
PyObject* result = find(child_coll, args, kwds);
|
PyObject* result = find(child_coll, args, kwds);
|
||||||
Py_DECREF(child_coll);
|
Py_DECREF(child_coll);
|
||||||
Py_DECREF(collType);
|
if (result && result != Py_None) {
|
||||||
if (result && result != Py_None) {
|
return result;
|
||||||
return result;
|
|
||||||
}
|
|
||||||
Py_XDECREF(result);
|
|
||||||
} else {
|
|
||||||
Py_DECREF(collType);
|
|
||||||
}
|
}
|
||||||
|
Py_XDECREF(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -774,7 +774,7 @@ void UIDrawable::removeFromParent() {
|
||||||
auto p = parent.lock();
|
auto p = parent.lock();
|
||||||
if (!p) return;
|
if (!p) return;
|
||||||
|
|
||||||
// Check if parent is a UIFrame (has children vector)
|
// Check if parent is a UIFrame or UIGrid (both have children vector)
|
||||||
if (p->derived_type() == PyObjectsEnum::UIFRAME) {
|
if (p->derived_type() == PyObjectsEnum::UIFRAME) {
|
||||||
auto frame = std::static_pointer_cast<UIFrame>(p);
|
auto frame = std::static_pointer_cast<UIFrame>(p);
|
||||||
auto& children = *frame->children;
|
auto& children = *frame->children;
|
||||||
|
|
@ -790,7 +790,18 @@ void UIDrawable::removeFromParent() {
|
||||||
}
|
}
|
||||||
frame->children_need_sort = true;
|
frame->children_need_sort = true;
|
||||||
}
|
}
|
||||||
// TODO: Handle UIGrid children when needed
|
else if (p->derived_type() == PyObjectsEnum::UIGRID) {
|
||||||
|
auto grid = std::static_pointer_cast<UIGrid>(p);
|
||||||
|
auto& children = *grid->children;
|
||||||
|
|
||||||
|
for (auto it = children.begin(); it != children.end(); ++it) {
|
||||||
|
if (it->get() == this) {
|
||||||
|
children.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
grid->children_need_sort = true;
|
||||||
|
}
|
||||||
|
|
||||||
parent.reset();
|
parent.reset();
|
||||||
}
|
}
|
||||||
|
|
@ -1031,9 +1042,20 @@ int UIDrawable::set_parent(PyObject* self, PyObject* value, void* closure) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (children_ptr && *children_ptr) {
|
if (children_ptr && *children_ptr) {
|
||||||
// Add to new parent's children
|
// Check if already in this parent's collection (prevent duplicates)
|
||||||
(*children_ptr)->push_back(drawable);
|
bool already_present = false;
|
||||||
drawable->setParent(new_parent);
|
for (const auto& child : **children_ptr) {
|
||||||
|
if (child.get() == drawable.get()) {
|
||||||
|
already_present = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!already_present) {
|
||||||
|
// Add to new parent's children
|
||||||
|
(*children_ptr)->push_back(drawable);
|
||||||
|
drawable->setParent(new_parent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
225
src/UIEntity.cpp
225
src/UIEntity.cpp
|
|
@ -37,7 +37,7 @@ void UIEntity::updateVisibility()
|
||||||
|
|
||||||
// Lazy initialize gridstate if needed
|
// Lazy initialize gridstate if needed
|
||||||
if (gridstate.size() == 0) {
|
if (gridstate.size() == 0) {
|
||||||
gridstate.resize(grid->grid_x * grid->grid_y);
|
gridstate.resize(grid->grid_w * grid->grid_h);
|
||||||
// Initialize all cells as not visible/discovered
|
// Initialize all cells as not visible/discovered
|
||||||
for (auto& state : gridstate) {
|
for (auto& state : gridstate) {
|
||||||
state.visible = false;
|
state.visible = false;
|
||||||
|
|
@ -58,9 +58,9 @@ void UIEntity::updateVisibility()
|
||||||
grid->computeFOV(x, y, grid->fov_radius, true, grid->fov_algorithm);
|
grid->computeFOV(x, y, grid->fov_radius, true, grid->fov_algorithm);
|
||||||
|
|
||||||
// Update visible cells based on FOV computation
|
// Update visible cells based on FOV computation
|
||||||
for (int gy = 0; gy < grid->grid_y; gy++) {
|
for (int gy = 0; gy < grid->grid_h; gy++) {
|
||||||
for (int gx = 0; gx < grid->grid_x; gx++) {
|
for (int gx = 0; gx < grid->grid_w; gx++) {
|
||||||
int idx = gy * grid->grid_x + gx;
|
int idx = gy * grid->grid_w + gx;
|
||||||
if (grid->isInFOV(gx, gy)) {
|
if (grid->isInFOV(gx, gy)) {
|
||||||
gridstate[idx].visible = true;
|
gridstate[idx].visible = true;
|
||||||
gridstate[idx].discovered = true; // Once seen, always discovered
|
gridstate[idx].discovered = true; // Once seen, always discovered
|
||||||
|
|
@ -108,7 +108,7 @@ PyObject* UIEntity::at(PyUIEntityObject* self, PyObject* args, PyObject* kwds) {
|
||||||
|
|
||||||
// Lazy initialize gridstate if needed
|
// Lazy initialize gridstate if needed
|
||||||
if (self->data->gridstate.size() == 0) {
|
if (self->data->gridstate.size() == 0) {
|
||||||
self->data->gridstate.resize(self->data->grid->grid_x * self->data->grid->grid_y);
|
self->data->gridstate.resize(self->data->grid->grid_w * self->data->grid->grid_h);
|
||||||
// Initialize all cells as not visible/discovered
|
// Initialize all cells as not visible/discovered
|
||||||
for (auto& state : self->data->gridstate) {
|
for (auto& state : self->data->gridstate) {
|
||||||
state.visible = false;
|
state.visible = false;
|
||||||
|
|
@ -117,7 +117,7 @@ PyObject* UIEntity::at(PyUIEntityObject* self, PyObject* args, PyObject* kwds) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bounds check
|
// Bounds check
|
||||||
if (x < 0 || x >= self->data->grid->grid_x || y < 0 || y >= self->data->grid->grid_y) {
|
if (x < 0 || x >= self->data->grid->grid_w || y < 0 || y >= self->data->grid->grid_h) {
|
||||||
PyErr_Format(PyExc_IndexError, "Grid coordinates (%d, %d) out of bounds", x, y);
|
PyErr_Format(PyExc_IndexError, "Grid coordinates (%d, %d) out of bounds", x, y);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -125,7 +125,7 @@ PyObject* UIEntity::at(PyUIEntityObject* self, PyObject* args, PyObject* kwds) {
|
||||||
// Use type directly since GridPointState is internal-only (not exported to module)
|
// Use type directly since GridPointState is internal-only (not exported to module)
|
||||||
auto type = &mcrfpydef::PyUIGridPointStateType;
|
auto type = &mcrfpydef::PyUIGridPointStateType;
|
||||||
auto obj = (PyUIGridPointStateObject*)type->tp_alloc(type, 0);
|
auto obj = (PyUIGridPointStateObject*)type->tp_alloc(type, 0);
|
||||||
obj->data = &(self->data->gridstate[y * self->data->grid->grid_x + x]);
|
obj->data = &(self->data->gridstate[y * self->data->grid->grid_w + x]);
|
||||||
obj->grid = self->data->grid;
|
obj->grid = self->data->grid;
|
||||||
obj->entity = self->data;
|
obj->entity = self->data;
|
||||||
obj->x = x; // #16 - Store position for .point property
|
obj->x = x; // #16 - Store position for .point property
|
||||||
|
|
@ -469,6 +469,157 @@ int UIEntity::set_float_member(PyUIEntityObject* self, PyObject* value, void* cl
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #176 - Helper to get cell dimensions from grid
|
||||||
|
static void get_cell_dimensions(UIEntity* entity, float& cell_width, float& cell_height) {
|
||||||
|
// Default cell dimensions when no texture
|
||||||
|
constexpr float DEFAULT_CELL_WIDTH = 16.0f;
|
||||||
|
constexpr float DEFAULT_CELL_HEIGHT = 16.0f;
|
||||||
|
|
||||||
|
if (entity->grid) {
|
||||||
|
auto ptex = entity->grid->getTexture();
|
||||||
|
cell_width = ptex ? static_cast<float>(ptex->sprite_width) : DEFAULT_CELL_WIDTH;
|
||||||
|
cell_height = ptex ? static_cast<float>(ptex->sprite_height) : DEFAULT_CELL_HEIGHT;
|
||||||
|
} else {
|
||||||
|
cell_width = DEFAULT_CELL_WIDTH;
|
||||||
|
cell_height = DEFAULT_CELL_HEIGHT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #176 - Pixel position: pos = draw_pos * tile_size
|
||||||
|
PyObject* UIEntity::get_pixel_pos(PyUIEntityObject* self, void* closure) {
|
||||||
|
if (!self->data->grid) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "entity is not attached to a Grid");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
float cell_width, cell_height;
|
||||||
|
get_cell_dimensions(self->data.get(), cell_width, cell_height);
|
||||||
|
|
||||||
|
sf::Vector2f pixel_pos(
|
||||||
|
self->data->position.x * cell_width,
|
||||||
|
self->data->position.y * cell_height
|
||||||
|
);
|
||||||
|
return sfVector2f_to_PyObject(pixel_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
int UIEntity::set_pixel_pos(PyUIEntityObject* self, PyObject* value, void* closure) {
|
||||||
|
if (!self->data->grid) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "entity is not attached to a Grid");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sf::Vector2f pixel_vec = PyObject_to_sfVector2f(value);
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
float cell_width, cell_height;
|
||||||
|
get_cell_dimensions(self->data.get(), cell_width, cell_height);
|
||||||
|
|
||||||
|
// Save old position for spatial hash update
|
||||||
|
float old_x = self->data->position.x;
|
||||||
|
float old_y = self->data->position.y;
|
||||||
|
|
||||||
|
// Convert pixels to tile coordinates
|
||||||
|
self->data->position.x = pixel_vec.x / cell_width;
|
||||||
|
self->data->position.y = pixel_vec.y / cell_height;
|
||||||
|
|
||||||
|
// Update spatial hash
|
||||||
|
self->data->grid->spatial_hash.update(self->data, old_x, old_y);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// #176 - Individual pixel coordinates (x, y)
|
||||||
|
PyObject* UIEntity::get_pixel_member(PyUIEntityObject* self, void* closure) {
|
||||||
|
if (!self->data->grid) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "entity is not attached to a Grid");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
float cell_width, cell_height;
|
||||||
|
get_cell_dimensions(self->data.get(), cell_width, cell_height);
|
||||||
|
|
||||||
|
auto member_ptr = reinterpret_cast<long>(closure);
|
||||||
|
if (member_ptr == 0) // x
|
||||||
|
return PyFloat_FromDouble(self->data->position.x * cell_width);
|
||||||
|
else // y
|
||||||
|
return PyFloat_FromDouble(self->data->position.y * cell_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
int UIEntity::set_pixel_member(PyUIEntityObject* self, PyObject* value, void* closure) {
|
||||||
|
if (!self->data->grid) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, "entity is not attached to a Grid");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
float val;
|
||||||
|
if (PyFloat_Check(value)) {
|
||||||
|
val = PyFloat_AsDouble(value);
|
||||||
|
} else if (PyLong_Check(value)) {
|
||||||
|
val = PyLong_AsLong(value);
|
||||||
|
} else {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "Position must be a number (int or float)");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
float cell_width, cell_height;
|
||||||
|
get_cell_dimensions(self->data.get(), cell_width, cell_height);
|
||||||
|
|
||||||
|
// Save old position for spatial hash update
|
||||||
|
float old_x = self->data->position.x;
|
||||||
|
float old_y = self->data->position.y;
|
||||||
|
|
||||||
|
auto member_ptr = reinterpret_cast<long>(closure);
|
||||||
|
if (member_ptr == 0) // x
|
||||||
|
self->data->position.x = val / cell_width;
|
||||||
|
else // y
|
||||||
|
self->data->position.y = val / cell_height;
|
||||||
|
|
||||||
|
// Update spatial hash
|
||||||
|
self->data->grid->spatial_hash.update(self->data, old_x, old_y);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// #176 - Integer grid position (grid_x, grid_y)
|
||||||
|
PyObject* UIEntity::get_grid_int_member(PyUIEntityObject* self, void* closure) {
|
||||||
|
auto member_ptr = reinterpret_cast<long>(closure);
|
||||||
|
if (member_ptr == 0) // grid_x
|
||||||
|
return PyLong_FromLong(static_cast<int>(self->data->position.x));
|
||||||
|
else // grid_y
|
||||||
|
return PyLong_FromLong(static_cast<int>(self->data->position.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
int UIEntity::set_grid_int_member(PyUIEntityObject* self, PyObject* value, void* closure) {
|
||||||
|
int val;
|
||||||
|
if (PyLong_Check(value)) {
|
||||||
|
val = PyLong_AsLong(value);
|
||||||
|
} else if (PyFloat_Check(value)) {
|
||||||
|
val = static_cast<int>(PyFloat_AsDouble(value));
|
||||||
|
} else {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "Grid position must be an integer");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save old position for spatial hash update
|
||||||
|
float old_x = self->data->position.x;
|
||||||
|
float old_y = self->data->position.y;
|
||||||
|
|
||||||
|
auto member_ptr = reinterpret_cast<long>(closure);
|
||||||
|
if (member_ptr == 0) // grid_x
|
||||||
|
self->data->position.x = static_cast<float>(val);
|
||||||
|
else // grid_y
|
||||||
|
self->data->position.y = static_cast<float>(val);
|
||||||
|
|
||||||
|
// Update spatial hash if grid exists
|
||||||
|
if (self->data->grid) {
|
||||||
|
self->data->grid->spatial_hash.update(self->data, old_x, old_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
PyObject* UIEntity::get_grid(PyUIEntityObject* self, void* closure)
|
PyObject* UIEntity::get_grid(PyUIEntityObject* self, void* closure)
|
||||||
{
|
{
|
||||||
if (!self->data || !self->data->grid) {
|
if (!self->data || !self->data->grid) {
|
||||||
|
|
@ -544,7 +695,7 @@ int UIEntity::set_grid(PyUIEntityObject* self, PyObject* value, void* closure)
|
||||||
|
|
||||||
// Initialize gridstate if needed
|
// Initialize gridstate if needed
|
||||||
if (self->data->gridstate.size() == 0) {
|
if (self->data->gridstate.size() == 0) {
|
||||||
self->data->gridstate.resize(new_grid->grid_x * new_grid->grid_y);
|
self->data->gridstate.resize(new_grid->grid_w * new_grid->grid_h);
|
||||||
for (auto& state : self->data->gridstate) {
|
for (auto& state : self->data->gridstate) {
|
||||||
state.visible = false;
|
state.visible = false;
|
||||||
state.discovered = false;
|
state.discovered = false;
|
||||||
|
|
@ -605,9 +756,9 @@ PyObject* UIEntity::path_to(PyUIEntityObject* self, PyObject* args, PyObject* kw
|
||||||
|
|
||||||
// Validate target position
|
// Validate target position
|
||||||
auto grid = self->data->grid;
|
auto grid = self->data->grid;
|
||||||
if (target_x < 0 || target_x >= grid->grid_x || target_y < 0 || target_y >= grid->grid_y) {
|
if (target_x < 0 || target_x >= grid->grid_w || target_y < 0 || target_y >= grid->grid_h) {
|
||||||
PyErr_Format(PyExc_ValueError, "Target position (%d, %d) is out of grid bounds (0-%d, 0-%d)",
|
PyErr_Format(PyExc_ValueError, "Target position (%d, %d) is out of grid bounds (0-%d, 0-%d)",
|
||||||
target_x, target_y, grid->grid_x - 1, grid->grid_y - 1);
|
target_x, target_y, grid->grid_w - 1, grid->grid_h - 1);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -789,7 +940,7 @@ PyMethodDef UIEntity_all_methods[] = {
|
||||||
MCRF_SIG("(property: str, target: Any, duration: float, easing=None, delta=False, callback=None, conflict_mode='replace')", "Animation"),
|
MCRF_SIG("(property: str, target: Any, duration: float, easing=None, delta=False, callback=None, conflict_mode='replace')", "Animation"),
|
||||||
MCRF_DESC("Create and start an animation on this entity's property."),
|
MCRF_DESC("Create and start an animation on this entity's property."),
|
||||||
MCRF_ARGS_START
|
MCRF_ARGS_START
|
||||||
MCRF_ARG("property", "Name of the property to animate (e.g., 'x', 'y', 'sprite_index')")
|
MCRF_ARG("property", "Name of the property to animate: 'draw_x', 'draw_y' (tile coords), 'sprite_scale', 'sprite_index'")
|
||||||
MCRF_ARG("target", "Target value - float or int depending on property")
|
MCRF_ARG("target", "Target value - float or int depending on property")
|
||||||
MCRF_ARG("duration", "Animation duration in seconds")
|
MCRF_ARG("duration", "Animation duration in seconds")
|
||||||
MCRF_ARG("easing", "Easing function: Easing enum value, string name, or None for linear")
|
MCRF_ARG("easing", "Easing function: Easing enum value, string name, or None for linear")
|
||||||
|
|
@ -797,8 +948,8 @@ PyMethodDef UIEntity_all_methods[] = {
|
||||||
MCRF_ARG("callback", "Optional callable invoked when animation completes")
|
MCRF_ARG("callback", "Optional callable invoked when animation completes")
|
||||||
MCRF_ARG("conflict_mode", "'replace' (default), 'queue', or 'error' if property already animating")
|
MCRF_ARG("conflict_mode", "'replace' (default), 'queue', or 'error' if property already animating")
|
||||||
MCRF_RETURNS("Animation object for monitoring progress")
|
MCRF_RETURNS("Animation object for monitoring progress")
|
||||||
MCRF_RAISES("ValueError", "If property name is not valid for Entity (x, y, sprite_scale, sprite_index)")
|
MCRF_RAISES("ValueError", "If property name is not valid for Entity (draw_x, draw_y, sprite_scale, sprite_index)")
|
||||||
MCRF_NOTE("Entity animations use grid coordinates for x/y, not pixel coordinates.")
|
MCRF_NOTE("Use 'draw_x'/'draw_y' to animate tile coordinates for smooth movement between grid cells.")
|
||||||
)},
|
)},
|
||||||
{"at", (PyCFunction)UIEntity::at, METH_VARARGS | METH_KEYWORDS,
|
{"at", (PyCFunction)UIEntity::at, METH_VARARGS | METH_KEYWORDS,
|
||||||
"at(x, y) or at(pos) -> GridPointState\n\n"
|
"at(x, y) or at(pos) -> GridPointState\n\n"
|
||||||
|
|
@ -846,8 +997,27 @@ PyMethodDef UIEntity_all_methods[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
PyGetSetDef UIEntity::getsetters[] = {
|
PyGetSetDef UIEntity::getsetters[] = {
|
||||||
{"draw_pos", (getter)UIEntity::get_position, (setter)UIEntity::set_position, "Entity position (graphically)", (void*)0},
|
// #176 - Pixel coordinates (relative to grid, like UIDrawable.pos)
|
||||||
{"pos", (getter)UIEntity::get_position, (setter)UIEntity::set_position, "Entity position (integer grid coordinates)", (void*)1},
|
{"pos", (getter)UIEntity::get_pixel_pos, (setter)UIEntity::set_pixel_pos,
|
||||||
|
"Pixel position relative to grid (Vector). Computed as draw_pos * tile_size. "
|
||||||
|
"Requires entity to be attached to a grid.", NULL},
|
||||||
|
{"x", (getter)UIEntity::get_pixel_member, (setter)UIEntity::set_pixel_member,
|
||||||
|
"Pixel X position relative to grid. Requires entity to be attached to a grid.", (void*)0},
|
||||||
|
{"y", (getter)UIEntity::get_pixel_member, (setter)UIEntity::set_pixel_member,
|
||||||
|
"Pixel Y position relative to grid. Requires entity to be attached to a grid.", (void*)1},
|
||||||
|
|
||||||
|
// #176 - Integer tile coordinates (logical game position)
|
||||||
|
{"grid_pos", (getter)UIEntity::get_position, (setter)UIEntity::set_position,
|
||||||
|
"Grid position as integer tile coordinates (Vector). The logical cell this entity occupies.", (void*)1},
|
||||||
|
{"grid_x", (getter)UIEntity::get_grid_int_member, (setter)UIEntity::set_grid_int_member,
|
||||||
|
"Grid X position as integer tile coordinate.", (void*)0},
|
||||||
|
{"grid_y", (getter)UIEntity::get_grid_int_member, (setter)UIEntity::set_grid_int_member,
|
||||||
|
"Grid Y position as integer tile coordinate.", (void*)1},
|
||||||
|
|
||||||
|
// Float tile coordinates (for smooth animation between tiles)
|
||||||
|
{"draw_pos", (getter)UIEntity::get_position, (setter)UIEntity::set_position,
|
||||||
|
"Fractional tile position for rendering (Vector). Use for smooth animation between grid cells.", (void*)0},
|
||||||
|
|
||||||
{"gridstate", (getter)UIEntity::get_gridstate, NULL, "Grid point states for the entity", NULL},
|
{"gridstate", (getter)UIEntity::get_gridstate, NULL, "Grid point states for the entity", NULL},
|
||||||
{"grid", (getter)UIEntity::get_grid, (setter)UIEntity::set_grid,
|
{"grid", (getter)UIEntity::get_grid, (setter)UIEntity::set_grid,
|
||||||
"Grid this entity belongs to. "
|
"Grid this entity belongs to. "
|
||||||
|
|
@ -855,8 +1025,6 @@ PyGetSetDef UIEntity::getsetters[] = {
|
||||||
"Set: Assign a Grid to move entity, or None to remove from grid.", NULL},
|
"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_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},
|
{"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},
|
|
||||||
{"y", (getter)UIEntity::get_float_member, (setter)UIEntity::set_float_member, "Entity y position", (void*)1},
|
|
||||||
{"visible", (getter)UIEntity_get_visible, (setter)UIEntity_set_visible, "Visibility flag", NULL},
|
{"visible", (getter)UIEntity_get_visible, (setter)UIEntity_set_visible, "Visibility flag", NULL},
|
||||||
{"opacity", (getter)UIEntity_get_opacity, (setter)UIEntity_set_opacity, "Opacity (0.0 = transparent, 1.0 = opaque)", NULL},
|
{"opacity", (getter)UIEntity_get_opacity, (setter)UIEntity_set_opacity, "Opacity (0.0 = transparent, 1.0 = opaque)", NULL},
|
||||||
{"name", (getter)UIEntity_get_name, (setter)UIEntity_set_name, "Name for finding elements", NULL},
|
{"name", (getter)UIEntity_get_name, (setter)UIEntity_set_name, "Name for finding elements", NULL},
|
||||||
|
|
@ -867,23 +1035,26 @@ PyObject* UIEntity::repr(PyUIEntityObject* self) {
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
if (!self->data) ss << "<Entity (invalid internal object)>";
|
if (!self->data) ss << "<Entity (invalid internal object)>";
|
||||||
else {
|
else {
|
||||||
auto ent = self->data;
|
// #176 - Use grid_x/grid_y naming to reflect tile coordinates
|
||||||
ss << "<Entity (x=" << self->data->position.x << ", y=" << self->data->position.y << ", sprite_index=" << self->data->sprite.getSpriteIndex() <<
|
ss << "<Entity (grid_x=" << static_cast<int>(self->data->position.x)
|
||||||
")>";
|
<< ", grid_y=" << static_cast<int>(self->data->position.y)
|
||||||
|
<< ", sprite_index=" << self->data->sprite.getSpriteIndex() << ")>";
|
||||||
}
|
}
|
||||||
std::string repr_str = ss.str();
|
std::string repr_str = ss.str();
|
||||||
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Property system implementation for animations
|
// Property system implementation for animations
|
||||||
|
// #176 - Animation properties use tile coordinates (draw_x, draw_y)
|
||||||
|
// "x" and "y" are kept as aliases for backwards compatibility
|
||||||
bool UIEntity::setProperty(const std::string& name, float value) {
|
bool UIEntity::setProperty(const std::string& name, float value) {
|
||||||
if (name == "x") {
|
if (name == "draw_x" || name == "x") { // #176 - draw_x is preferred, x is alias
|
||||||
position.x = value;
|
position.x = value;
|
||||||
// Don't update sprite position here - UIGrid::render() handles the pixel positioning
|
// Don't update sprite position here - UIGrid::render() handles the pixel positioning
|
||||||
if (grid) grid->markDirty(); // #144 - Propagate to parent grid for texture caching
|
if (grid) grid->markDirty(); // #144 - Propagate to parent grid for texture caching
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (name == "y") {
|
else if (name == "draw_y" || name == "y") { // #176 - draw_y is preferred, y is alias
|
||||||
position.y = value;
|
position.y = value;
|
||||||
// Don't update sprite position here - UIGrid::render() handles the pixel positioning
|
// Don't update sprite position here - UIGrid::render() handles the pixel positioning
|
||||||
if (grid) grid->markDirty(); // #144 - Propagate to parent grid for texture caching
|
if (grid) grid->markDirty(); // #144 - Propagate to parent grid for texture caching
|
||||||
|
|
@ -907,11 +1078,11 @@ bool UIEntity::setProperty(const std::string& name, int value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UIEntity::getProperty(const std::string& name, float& value) const {
|
bool UIEntity::getProperty(const std::string& name, float& value) const {
|
||||||
if (name == "x") {
|
if (name == "draw_x" || name == "x") { // #176
|
||||||
value = position.x;
|
value = position.x;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (name == "y") {
|
else if (name == "draw_y" || name == "y") { // #176
|
||||||
value = position.y;
|
value = position.y;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -923,8 +1094,8 @@ bool UIEntity::getProperty(const std::string& name, float& value) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UIEntity::hasProperty(const std::string& name) const {
|
bool UIEntity::hasProperty(const std::string& name) const {
|
||||||
// Float properties
|
// #176 - Float properties (draw_x/draw_y preferred, x/y are aliases)
|
||||||
if (name == "x" || name == "y" || name == "sprite_scale") {
|
if (name == "draw_x" || name == "draw_y" || name == "x" || name == "y" || name == "sprite_scale") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Int properties
|
// Int properties
|
||||||
|
|
@ -956,7 +1127,7 @@ PyObject* UIEntity::animate(PyUIEntityObject* self, PyObject* args, PyObject* kw
|
||||||
if (!self->data->hasProperty(property_name)) {
|
if (!self->data->hasProperty(property_name)) {
|
||||||
PyErr_Format(PyExc_ValueError,
|
PyErr_Format(PyExc_ValueError,
|
||||||
"Property '%s' is not valid for animation on Entity. "
|
"Property '%s' is not valid for animation on Entity. "
|
||||||
"Valid properties: x, y, sprite_scale, sprite_index, sprite_number",
|
"Valid properties: draw_x, draw_y (tile coords), sprite_scale, sprite_index",
|
||||||
property_name);
|
property_name);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,15 @@ public:
|
||||||
static int set_spritenumber(PyUIEntityObject* self, PyObject* value, void* closure);
|
static int set_spritenumber(PyUIEntityObject* self, PyObject* value, void* closure);
|
||||||
static PyObject* get_float_member(PyUIEntityObject* self, void* closure);
|
static PyObject* get_float_member(PyUIEntityObject* self, void* closure);
|
||||||
static int set_float_member(PyUIEntityObject* self, PyObject* value, void* closure);
|
static int set_float_member(PyUIEntityObject* self, PyObject* value, void* closure);
|
||||||
|
|
||||||
|
// #176 - Pixel position (pos, x, y) computed from draw_pos * tile_size
|
||||||
|
static PyObject* get_pixel_pos(PyUIEntityObject* self, void* closure);
|
||||||
|
static int set_pixel_pos(PyUIEntityObject* self, PyObject* value, void* closure);
|
||||||
|
static PyObject* get_pixel_member(PyUIEntityObject* self, void* closure);
|
||||||
|
static int set_pixel_member(PyUIEntityObject* self, PyObject* value, void* closure);
|
||||||
|
// #176 - Integer grid position (grid_x, grid_y)
|
||||||
|
static PyObject* get_grid_int_member(PyUIEntityObject* self, void* closure);
|
||||||
|
static int set_grid_int_member(PyUIEntityObject* self, PyObject* value, void* closure);
|
||||||
static PyObject* get_grid(PyUIEntityObject* self, void* closure);
|
static PyObject* get_grid(PyUIEntityObject* self, void* closure);
|
||||||
static int set_grid(PyUIEntityObject* self, PyObject* value, void* closure);
|
static int set_grid(PyUIEntityObject* self, PyObject* value, void* closure);
|
||||||
static PyMethodDef methods[];
|
static PyMethodDef methods[];
|
||||||
|
|
@ -133,12 +142,14 @@ namespace mcrfpydef {
|
||||||
" visible (bool): Visibility state. Default: True\n"
|
" visible (bool): Visibility state. Default: True\n"
|
||||||
" opacity (float): Opacity (0.0-1.0). Default: 1.0\n"
|
" opacity (float): Opacity (0.0-1.0). Default: 1.0\n"
|
||||||
" name (str): Element name for finding. Default: None\n"
|
" name (str): Element name for finding. Default: None\n"
|
||||||
" x (float): X grid position override. Default: 0\n"
|
" x (float): X grid position override (tile coords). Default: 0\n"
|
||||||
" y (float): Y grid position override. Default: 0\n\n"
|
" y (float): Y grid position override (tile coords). Default: 0\n\n"
|
||||||
"Attributes:\n"
|
"Attributes:\n"
|
||||||
" pos (tuple): Grid position as (x, y) tuple\n"
|
" pos (Vector): Pixel position relative to grid (requires grid attachment)\n"
|
||||||
" x, y (float): Grid position coordinates\n"
|
" x, y (float): Pixel position components (requires grid attachment)\n"
|
||||||
" draw_pos (tuple): Pixel position for rendering\n"
|
" grid_pos (Vector): Integer tile coordinates (logical game position)\n"
|
||||||
|
" grid_x, grid_y (int): Integer tile coordinate components\n"
|
||||||
|
" draw_pos (Vector): Fractional tile position for smooth animation\n"
|
||||||
" gridstate (GridPointState): Visibility state for grid points\n"
|
" gridstate (GridPointState): Visibility state for grid points\n"
|
||||||
" sprite_index (int): Current sprite index\n"
|
" sprite_index (int): Current sprite index\n"
|
||||||
" visible (bool): Visibility state\n"
|
" visible (bool): Visibility state\n"
|
||||||
|
|
|
||||||
|
|
@ -174,9 +174,9 @@ void UIFrame::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||||
PyObject* UIFrame::get_children(PyUIFrameObject* self, void* closure)
|
PyObject* UIFrame::get_children(PyUIFrameObject* self, void* closure)
|
||||||
{
|
{
|
||||||
// create PyUICollection instance pointing to self->data->children
|
// create PyUICollection instance pointing to self->data->children
|
||||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "UICollection");
|
// Use the type directly from namespace (#189 - type not exported to module)
|
||||||
|
PyTypeObject* type = &mcrfpydef::PyUICollectionType;
|
||||||
auto o = (PyUICollectionObject*)type->tp_alloc(type, 0);
|
auto o = (PyUICollectionObject*)type->tp_alloc(type, 0);
|
||||||
Py_DECREF(type);
|
|
||||||
if (o) {
|
if (o) {
|
||||||
o->data = self->data->children;
|
o->data = self->data->children;
|
||||||
o->owner = self->data; // #122: Set owner for parent tracking
|
o->owner = self->data; // #122: Set owner for parent tracking
|
||||||
|
|
|
||||||
142
src/UIGrid.cpp
142
src/UIGrid.cpp
|
|
@ -14,7 +14,7 @@
|
||||||
// UIDrawable methods now in UIBase.h
|
// UIDrawable methods now in UIBase.h
|
||||||
|
|
||||||
UIGrid::UIGrid()
|
UIGrid::UIGrid()
|
||||||
: grid_x(0), grid_y(0), zoom(1.0f), center_x(0.0f), center_y(0.0f), ptex(nullptr),
|
: grid_w(0), grid_h(0), zoom(1.0f), center_x(0.0f), center_y(0.0f), ptex(nullptr),
|
||||||
fill_color(8, 8, 8, 255), tcod_map(nullptr), tcod_dijkstra(nullptr), tcod_path(nullptr),
|
fill_color(8, 8, 8, 255), tcod_map(nullptr), tcod_dijkstra(nullptr), tcod_path(nullptr),
|
||||||
perspective_enabled(false), fov_algorithm(FOV_BASIC), fov_radius(10),
|
perspective_enabled(false), fov_algorithm(FOV_BASIC), fov_radius(10),
|
||||||
use_chunks(false) // Default to omniscient view
|
use_chunks(false) // Default to omniscient view
|
||||||
|
|
@ -39,12 +39,12 @@ UIGrid::UIGrid()
|
||||||
output.setPosition(0, 0);
|
output.setPosition(0, 0);
|
||||||
output.setTexture(renderTexture.getTexture());
|
output.setTexture(renderTexture.getTexture());
|
||||||
|
|
||||||
// Points vector starts empty (grid_x * grid_y = 0)
|
// Points vector starts empty (grid_w * grid_h = 0)
|
||||||
// TCOD map will be created when grid is resized
|
// 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)
|
UIGrid::UIGrid(int gx, int gy, std::shared_ptr<PyTexture> _ptex, sf::Vector2f _xy, sf::Vector2f _wh)
|
||||||
: grid_x(gx), grid_y(gy),
|
: grid_w(gx), grid_h(gy),
|
||||||
zoom(1.0f),
|
zoom(1.0f),
|
||||||
ptex(_ptex),
|
ptex(_ptex),
|
||||||
fill_color(8, 8, 8, 255), tcod_map(nullptr), tcod_dijkstra(nullptr), tcod_path(nullptr),
|
fill_color(8, 8, 8, 255), tcod_map(nullptr), tcod_dijkstra(nullptr), tcod_path(nullptr),
|
||||||
|
|
@ -166,10 +166,10 @@ void UIGrid::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||||
int top_spritepixels = center_y - (box.getSize().y / 2.0 / zoom);
|
int top_spritepixels = center_y - (box.getSize().y / 2.0 / zoom);
|
||||||
|
|
||||||
int x_limit = left_edge + width_sq + 2;
|
int x_limit = left_edge + width_sq + 2;
|
||||||
if (x_limit > grid_x) x_limit = grid_x;
|
if (x_limit > grid_w) x_limit = grid_w;
|
||||||
|
|
||||||
int y_limit = top_edge + height_sq + 2;
|
int y_limit = top_edge + height_sq + 2;
|
||||||
if (y_limit > grid_y) y_limit = grid_y;
|
if (y_limit > grid_h) y_limit = grid_h;
|
||||||
|
|
||||||
// #150 - Layers are now the sole source of grid rendering (base layer removed)
|
// #150 - Layers are now the sole source of grid rendering (base layer removed)
|
||||||
// Render layers with z_index < 0 (below entities)
|
// Render layers with z_index < 0 (below entities)
|
||||||
|
|
@ -274,14 +274,14 @@ void UIGrid::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||||
y+=1)
|
y+=1)
|
||||||
{
|
{
|
||||||
// Skip out-of-bounds cells
|
// Skip out-of-bounds cells
|
||||||
if (x < 0 || x >= grid_x || y < 0 || y >= grid_y) continue;
|
if (x < 0 || x >= grid_w || y < 0 || y >= grid_h) continue;
|
||||||
|
|
||||||
auto pixel_pos = sf::Vector2f(
|
auto pixel_pos = sf::Vector2f(
|
||||||
(x*cell_width - left_spritepixels) * zoom,
|
(x*cell_width - left_spritepixels) * zoom,
|
||||||
(y*cell_height - top_spritepixels) * zoom );
|
(y*cell_height - top_spritepixels) * zoom );
|
||||||
|
|
||||||
// Get visibility state from entity's perspective
|
// Get visibility state from entity's perspective
|
||||||
int idx = y * grid_x + x;
|
int idx = y * grid_w + x;
|
||||||
if (idx >= 0 && idx < static_cast<int>(entity->gridstate.size())) {
|
if (idx >= 0 && idx < static_cast<int>(entity->gridstate.size())) {
|
||||||
const auto& state = entity->gridstate[idx];
|
const auto& state = entity->gridstate[idx];
|
||||||
|
|
||||||
|
|
@ -313,7 +313,7 @@ void UIGrid::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||||
y+=1)
|
y+=1)
|
||||||
{
|
{
|
||||||
// Skip out-of-bounds cells
|
// Skip out-of-bounds cells
|
||||||
if (x < 0 || x >= grid_x || y < 0 || y >= grid_y) continue;
|
if (x < 0 || x >= grid_w || y < 0 || y >= grid_h) continue;
|
||||||
|
|
||||||
auto pixel_pos = sf::Vector2f(
|
auto pixel_pos = sf::Vector2f(
|
||||||
(x*cell_width - left_spritepixels) * zoom,
|
(x*cell_width - left_spritepixels) * zoom,
|
||||||
|
|
@ -361,7 +361,7 @@ UIGridPoint& UIGrid::at(int x, int y)
|
||||||
if (use_chunks && chunk_manager) {
|
if (use_chunks && chunk_manager) {
|
||||||
return chunk_manager->at(x, y);
|
return chunk_manager->at(x, y);
|
||||||
}
|
}
|
||||||
return points[y * grid_x + x];
|
return points[y * grid_w + x];
|
||||||
}
|
}
|
||||||
|
|
||||||
UIGrid::~UIGrid()
|
UIGrid::~UIGrid()
|
||||||
|
|
@ -387,7 +387,7 @@ PyObjectsEnum UIGrid::derived_type()
|
||||||
|
|
||||||
// #147 - Layer management methods
|
// #147 - Layer management methods
|
||||||
std::shared_ptr<ColorLayer> UIGrid::addColorLayer(int z_index, const std::string& name) {
|
std::shared_ptr<ColorLayer> UIGrid::addColorLayer(int z_index, const std::string& name) {
|
||||||
auto layer = std::make_shared<ColorLayer>(z_index, grid_x, grid_y, this);
|
auto layer = std::make_shared<ColorLayer>(z_index, grid_w, grid_h, this);
|
||||||
layer->name = name;
|
layer->name = name;
|
||||||
layers.push_back(layer);
|
layers.push_back(layer);
|
||||||
layers_need_sort = true;
|
layers_need_sort = true;
|
||||||
|
|
@ -395,7 +395,7 @@ std::shared_ptr<ColorLayer> UIGrid::addColorLayer(int z_index, const std::string
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<TileLayer> UIGrid::addTileLayer(int z_index, std::shared_ptr<PyTexture> texture, const std::string& name) {
|
std::shared_ptr<TileLayer> UIGrid::addTileLayer(int z_index, std::shared_ptr<PyTexture> texture, const std::string& name) {
|
||||||
auto layer = std::make_shared<TileLayer>(z_index, grid_x, grid_y, this, texture);
|
auto layer = std::make_shared<TileLayer>(z_index, grid_w, grid_h, this, texture);
|
||||||
layer->name = name;
|
layer->name = name;
|
||||||
layers.push_back(layer);
|
layers.push_back(layer);
|
||||||
layers_need_sort = true;
|
layers_need_sort = true;
|
||||||
|
|
@ -442,8 +442,8 @@ void UIGrid::syncTCODMap()
|
||||||
{
|
{
|
||||||
if (!tcod_map) return;
|
if (!tcod_map) return;
|
||||||
|
|
||||||
for (int y = 0; y < grid_y; y++) {
|
for (int y = 0; y < grid_h; y++) {
|
||||||
for (int x = 0; x < grid_x; x++) {
|
for (int x = 0; x < grid_w; x++) {
|
||||||
const UIGridPoint& point = at(x, y);
|
const UIGridPoint& point = at(x, y);
|
||||||
tcod_map->setProperties(x, y, point.transparent, point.walkable);
|
tcod_map->setProperties(x, y, point.transparent, point.walkable);
|
||||||
}
|
}
|
||||||
|
|
@ -452,15 +452,15 @@ void UIGrid::syncTCODMap()
|
||||||
|
|
||||||
void UIGrid::syncTCODMapCell(int x, int y)
|
void UIGrid::syncTCODMapCell(int x, int y)
|
||||||
{
|
{
|
||||||
if (!tcod_map || x < 0 || x >= grid_x || y < 0 || y >= grid_y) return;
|
if (!tcod_map || x < 0 || x >= grid_w || y < 0 || y >= grid_h) return;
|
||||||
|
|
||||||
const UIGridPoint& point = at(x, y);
|
const UIGridPoint& point = at(x, y);
|
||||||
tcod_map->setProperties(x, y, point.transparent, point.walkable);
|
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)
|
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;
|
if (!tcod_map || x < 0 || x >= grid_w || y < 0 || y >= grid_h) return;
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(fov_mutex);
|
std::lock_guard<std::mutex> lock(fov_mutex);
|
||||||
tcod_map->computeFov(x, y, radius, light_walls, algo);
|
tcod_map->computeFov(x, y, radius, light_walls, algo);
|
||||||
|
|
@ -468,7 +468,7 @@ void UIGrid::computeFOV(int x, int y, int radius, bool light_walls, TCOD_fov_alg
|
||||||
|
|
||||||
bool UIGrid::isInFOV(int x, int y) const
|
bool UIGrid::isInFOV(int x, int y) const
|
||||||
{
|
{
|
||||||
if (!tcod_map || x < 0 || x >= grid_x || y < 0 || y >= grid_y) return false;
|
if (!tcod_map || x < 0 || x >= grid_w || y < 0 || y >= grid_h) return false;
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(fov_mutex);
|
std::lock_guard<std::mutex> lock(fov_mutex);
|
||||||
return tcod_map->isInFov(x, y);
|
return tcod_map->isInFov(x, y);
|
||||||
|
|
@ -478,8 +478,8 @@ std::vector<std::pair<int, int>> UIGrid::findPath(int x1, int y1, int x2, int y2
|
||||||
{
|
{
|
||||||
std::vector<std::pair<int, int>> path;
|
std::vector<std::pair<int, int>> path;
|
||||||
|
|
||||||
if (!tcod_map || x1 < 0 || x1 >= grid_x || y1 < 0 || y1 >= grid_y ||
|
if (!tcod_map || x1 < 0 || x1 >= grid_w || y1 < 0 || y1 >= grid_h ||
|
||||||
x2 < 0 || x2 >= grid_x || y2 < 0 || y2 >= grid_y) {
|
x2 < 0 || x2 >= grid_w || y2 < 0 || y2 >= grid_h) {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -497,7 +497,7 @@ std::vector<std::pair<int, int>> UIGrid::findPath(int x1, int y1, int x2, int y2
|
||||||
|
|
||||||
void UIGrid::computeDijkstra(int rootX, int rootY, float diagonalCost)
|
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;
|
if (!tcod_map || !tcod_dijkstra || rootX < 0 || rootX >= grid_w || rootY < 0 || rootY >= grid_h) return;
|
||||||
|
|
||||||
// Compute the Dijkstra map from the root position
|
// Compute the Dijkstra map from the root position
|
||||||
tcod_dijkstra->compute(rootX, rootY);
|
tcod_dijkstra->compute(rootX, rootY);
|
||||||
|
|
@ -505,7 +505,7 @@ void UIGrid::computeDijkstra(int rootX, int rootY, float diagonalCost)
|
||||||
|
|
||||||
float UIGrid::getDijkstraDistance(int x, int y) const
|
float UIGrid::getDijkstraDistance(int x, int y) const
|
||||||
{
|
{
|
||||||
if (!tcod_dijkstra || x < 0 || x >= grid_x || y < 0 || y >= grid_y) {
|
if (!tcod_dijkstra || x < 0 || x >= grid_w || y < 0 || y >= grid_h) {
|
||||||
return -1.0f; // Invalid position
|
return -1.0f; // Invalid position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -516,7 +516,7 @@ std::vector<std::pair<int, int>> UIGrid::getDijkstraPath(int x, int y) const
|
||||||
{
|
{
|
||||||
std::vector<std::pair<int, int>> path;
|
std::vector<std::pair<int, int>> path;
|
||||||
|
|
||||||
if (!tcod_dijkstra || x < 0 || x >= grid_x || y < 0 || y >= grid_y) {
|
if (!tcod_dijkstra || x < 0 || x >= grid_w || y < 0 || y >= grid_h) {
|
||||||
return path; // Empty path for invalid position
|
return path; // Empty path for invalid position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -538,9 +538,9 @@ std::vector<std::pair<int, int>> UIGrid::computeAStarPath(int x1, int y1, int x2
|
||||||
std::vector<std::pair<int, int>> path;
|
std::vector<std::pair<int, int>> path;
|
||||||
|
|
||||||
// Validate inputs
|
// Validate inputs
|
||||||
if (!tcod_map || !tcod_path ||
|
if (!tcod_map || !tcod_path ||
|
||||||
x1 < 0 || x1 >= grid_x || y1 < 0 || y1 >= grid_y ||
|
x1 < 0 || x1 >= grid_w || y1 < 0 || y1 >= grid_h ||
|
||||||
x2 < 0 || x2 >= grid_x || y2 < 0 || y2 >= grid_y) {
|
x2 < 0 || x2 >= grid_w || y2 < 0 || y2 >= grid_h) {
|
||||||
return path; // Return empty path
|
return path; // Return empty path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -686,7 +686,7 @@ UIDrawable* UIGrid::click_at(sf::Vector2f point)
|
||||||
int cell_y = static_cast<int>(std::floor(grid_y));
|
int cell_y = static_cast<int>(std::floor(grid_y));
|
||||||
|
|
||||||
// Only fire if within valid grid bounds
|
// Only fire if within valid grid bounds
|
||||||
if (cell_x >= 0 && cell_x < this->grid_x && cell_y >= 0 && cell_y < this->grid_y) {
|
if (cell_x >= 0 && cell_x < this->grid_w && cell_y >= 0 && cell_y < this->grid_h) {
|
||||||
// Create Vector object for cell position - must fetch finalized type from module
|
// Create Vector object for cell position - must fetch finalized type from module
|
||||||
PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
|
PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
|
||||||
if (vector_type) {
|
if (vector_type) {
|
||||||
|
|
@ -719,7 +719,7 @@ UIDrawable* UIGrid::click_at(sf::Vector2f point)
|
||||||
int cell_y = static_cast<int>(std::floor(grid_y));
|
int cell_y = static_cast<int>(std::floor(grid_y));
|
||||||
|
|
||||||
// Only fire if within valid grid bounds
|
// Only fire if within valid grid bounds
|
||||||
if (cell_x >= 0 && cell_x < this->grid_x && cell_y >= 0 && cell_y < this->grid_y) {
|
if (cell_x >= 0 && cell_x < this->grid_w && cell_y >= 0 && cell_y < this->grid_h) {
|
||||||
// Create Vector object for cell position - must fetch finalized type from module
|
// Create Vector object for cell position - must fetch finalized type from module
|
||||||
PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
|
PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
|
||||||
if (vector_type) {
|
if (vector_type) {
|
||||||
|
|
@ -766,14 +766,14 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
||||||
int z_index = 0;
|
int z_index = 0;
|
||||||
const char* name = nullptr;
|
const char* name = nullptr;
|
||||||
float x = 0.0f, y = 0.0f, w = 0.0f, h = 0.0f;
|
float x = 0.0f, y = 0.0f, w = 0.0f, h = 0.0f;
|
||||||
int grid_x = 2, grid_y = 2; // Default to 2x2 grid
|
int grid_w = 2, grid_h = 2; // Default to 2x2 grid
|
||||||
|
|
||||||
// Keywords list matches the new spec: positional args first, then all keyword args
|
// Keywords list matches the new spec: positional args first, then all keyword args
|
||||||
static const char* kwlist[] = {
|
static const char* kwlist[] = {
|
||||||
"pos", "size", "grid_size", "texture", // Positional args (as per spec)
|
"pos", "size", "grid_size", "texture", // Positional args (as per spec)
|
||||||
// Keyword-only args
|
// Keyword-only args
|
||||||
"fill_color", "on_click", "center_x", "center_y", "zoom",
|
"fill_color", "on_click", "center_x", "center_y", "zoom",
|
||||||
"visible", "opacity", "z_index", "name", "x", "y", "w", "h", "grid_x", "grid_y",
|
"visible", "opacity", "z_index", "name", "x", "y", "w", "h", "grid_w", "grid_h",
|
||||||
"layers", // #150 - layers dict parameter
|
"layers", // #150 - layers dict parameter
|
||||||
nullptr
|
nullptr
|
||||||
};
|
};
|
||||||
|
|
@ -782,7 +782,7 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOOOfffifizffffiiO", const_cast<char**>(kwlist),
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOOOfffifizffffiiO", const_cast<char**>(kwlist),
|
||||||
&pos_obj, &size_obj, &grid_size_obj, &textureObj, // Positional
|
&pos_obj, &size_obj, &grid_size_obj, &textureObj, // Positional
|
||||||
&fill_color, &click_handler, ¢er_x, ¢er_y, &zoom,
|
&fill_color, &click_handler, ¢er_x, ¢er_y, &zoom,
|
||||||
&visible, &opacity, &z_index, &name, &x, &y, &w, &h, &grid_x, &grid_y,
|
&visible, &opacity, &z_index, &name, &x, &y, &w, &h, &grid_w, &grid_h,
|
||||||
&layers_obj)) {
|
&layers_obj)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -833,26 +833,26 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle grid_size argument (can be tuple or use grid_x/grid_y keywords)
|
// Handle grid_size argument (can be tuple or use grid_w/grid_h keywords)
|
||||||
if (grid_size_obj) {
|
if (grid_size_obj) {
|
||||||
if (PyTuple_Check(grid_size_obj) && PyTuple_Size(grid_size_obj) == 2) {
|
if (PyTuple_Check(grid_size_obj) && PyTuple_Size(grid_size_obj) == 2) {
|
||||||
PyObject* gx_val = PyTuple_GetItem(grid_size_obj, 0);
|
PyObject* gx_val = PyTuple_GetItem(grid_size_obj, 0);
|
||||||
PyObject* gy_val = PyTuple_GetItem(grid_size_obj, 1);
|
PyObject* gy_val = PyTuple_GetItem(grid_size_obj, 1);
|
||||||
if (PyLong_Check(gx_val) && PyLong_Check(gy_val)) {
|
if (PyLong_Check(gx_val) && PyLong_Check(gy_val)) {
|
||||||
grid_x = PyLong_AsLong(gx_val);
|
grid_w = PyLong_AsLong(gx_val);
|
||||||
grid_y = PyLong_AsLong(gy_val);
|
grid_h = PyLong_AsLong(gy_val);
|
||||||
} else {
|
} else {
|
||||||
PyErr_SetString(PyExc_TypeError, "grid_size tuple must contain integers");
|
PyErr_SetString(PyExc_TypeError, "grid_size tuple must contain integers");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
PyErr_SetString(PyExc_TypeError, "grid_size must be a tuple (grid_x, grid_y)");
|
PyErr_SetString(PyExc_TypeError, "grid_size must be a tuple (grid_w, grid_h)");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate grid dimensions
|
// Validate grid dimensions
|
||||||
if (grid_x <= 0 || grid_y <= 0) {
|
if (grid_w <= 0 || grid_h <= 0) {
|
||||||
PyErr_SetString(PyExc_ValueError, "Grid dimensions must be positive integers");
|
PyErr_SetString(PyExc_ValueError, "Grid dimensions must be positive integers");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -873,15 +873,15 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
||||||
|
|
||||||
// If size wasn't specified, calculate based on grid dimensions and texture
|
// If size wasn't specified, calculate based on grid dimensions and texture
|
||||||
if (!size_obj && texture_ptr) {
|
if (!size_obj && texture_ptr) {
|
||||||
w = grid_x * texture_ptr->sprite_width;
|
w = grid_w * texture_ptr->sprite_width;
|
||||||
h = grid_y * texture_ptr->sprite_height;
|
h = grid_h * texture_ptr->sprite_height;
|
||||||
} else if (!size_obj) {
|
} else if (!size_obj) {
|
||||||
w = grid_x * 16.0f; // Default tile size
|
w = grid_w * 16.0f; // Default tile size
|
||||||
h = grid_y * 16.0f;
|
h = grid_h * 16.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the grid
|
// Create the grid
|
||||||
self->data = std::make_shared<UIGrid>(grid_x, grid_y, texture_ptr,
|
self->data = std::make_shared<UIGrid>(grid_w, grid_h, texture_ptr,
|
||||||
sf::Vector2f(x, y), sf::Vector2f(w, h));
|
sf::Vector2f(x, y), sf::Vector2f(w, h));
|
||||||
|
|
||||||
// Set additional properties
|
// Set additional properties
|
||||||
|
|
@ -993,16 +993,16 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
|
||||||
|
|
||||||
// #179 - Return grid_size as Vector
|
// #179 - Return grid_size as Vector
|
||||||
PyObject* UIGrid::get_grid_size(PyUIGridObject* self, void* closure) {
|
PyObject* UIGrid::get_grid_size(PyUIGridObject* self, void* closure) {
|
||||||
return PyVector(sf::Vector2f(static_cast<float>(self->data->grid_x),
|
return PyVector(sf::Vector2f(static_cast<float>(self->data->grid_w),
|
||||||
static_cast<float>(self->data->grid_y))).pyObject();
|
static_cast<float>(self->data->grid_h))).pyObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject* UIGrid::get_grid_x(PyUIGridObject* self, void* closure) {
|
PyObject* UIGrid::get_grid_w(PyUIGridObject* self, void* closure) {
|
||||||
return PyLong_FromLong(self->data->grid_x);
|
return PyLong_FromLong(self->data->grid_w);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject* UIGrid::get_grid_y(PyUIGridObject* self, void* closure) {
|
PyObject* UIGrid::get_grid_h(PyUIGridObject* self, void* closure) {
|
||||||
return PyLong_FromLong(self->data->grid_y);
|
return PyLong_FromLong(self->data->grid_h);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject* UIGrid::get_position(PyUIGridObject* self, void* closure) {
|
PyObject* UIGrid::get_position(PyUIGridObject* self, void* closure) {
|
||||||
|
|
@ -1021,16 +1021,26 @@ int UIGrid::set_position(PyUIGridObject* self, PyObject* value, void* closure) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #181 - Return size as Vector
|
||||||
PyObject* UIGrid::get_size(PyUIGridObject* self, void* closure) {
|
PyObject* UIGrid::get_size(PyUIGridObject* self, void* closure) {
|
||||||
auto& box = self->data->box;
|
auto& box = self->data->box;
|
||||||
return Py_BuildValue("(ff)", box.getSize().x, box.getSize().y);
|
return PyVector(box.getSize()).pyObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
int UIGrid::set_size(PyUIGridObject* self, PyObject* value, void* closure) {
|
int UIGrid::set_size(PyUIGridObject* self, PyObject* value, void* closure) {
|
||||||
float w, h;
|
float w, h;
|
||||||
if (!PyArg_ParseTuple(value, "ff", &w, &h)) {
|
// Accept Vector or tuple
|
||||||
PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats");
|
PyVectorObject* vec = PyVector::from_arg(value);
|
||||||
return -1;
|
if (vec) {
|
||||||
|
w = vec->data.x;
|
||||||
|
h = vec->data.y;
|
||||||
|
Py_DECREF(vec);
|
||||||
|
} else {
|
||||||
|
PyErr_Clear();
|
||||||
|
if (!PyArg_ParseTuple(value, "ff", &w, &h)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "size must be a Vector or tuple (w, h)");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self->data->box.setSize(sf::Vector2f(w, h));
|
self->data->box.setSize(sf::Vector2f(w, h));
|
||||||
|
|
||||||
|
|
@ -1173,12 +1183,12 @@ PyObject* UIGrid::py_at(PyUIGridObject* self, PyObject* args, PyObject* kwds)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Range validation
|
// Range validation
|
||||||
if (x < 0 || x >= self->data->grid_x) {
|
if (x < 0 || x >= self->data->grid_w) {
|
||||||
PyErr_Format(PyExc_IndexError, "x index %d is out of range [0, %d)", x, self->data->grid_x);
|
PyErr_Format(PyExc_IndexError, "x index %d is out of range [0, %d)", x, self->data->grid_w);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (y < 0 || y >= self->data->grid_y) {
|
if (y < 0 || y >= self->data->grid_h) {
|
||||||
PyErr_Format(PyExc_IndexError, "y index %d is out of range [0, %d)", y, self->data->grid_y);
|
PyErr_Format(PyExc_IndexError, "y index %d is out of range [0, %d)", y, self->data->grid_h);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1777,8 +1787,8 @@ void UIGrid::center_camera() {
|
||||||
// Center on grid's middle tile
|
// Center on grid's middle tile
|
||||||
int cell_width = ptex ? ptex->sprite_width : DEFAULT_CELL_WIDTH;
|
int cell_width = ptex ? ptex->sprite_width : DEFAULT_CELL_WIDTH;
|
||||||
int cell_height = ptex ? ptex->sprite_height : DEFAULT_CELL_HEIGHT;
|
int cell_height = ptex ? ptex->sprite_height : DEFAULT_CELL_HEIGHT;
|
||||||
center_x = (grid_x / 2.0f) * cell_width;
|
center_x = (grid_w / 2.0f) * cell_width;
|
||||||
center_y = (grid_y / 2.0f) * cell_height;
|
center_y = (grid_h / 2.0f) * cell_height;
|
||||||
markDirty(); // #144 - View change affects content
|
markDirty(); // #144 - View change affects content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2043,12 +2053,12 @@ PyMethodDef UIGrid_all_methods[] = {
|
||||||
PyGetSetDef UIGrid::getsetters[] = {
|
PyGetSetDef UIGrid::getsetters[] = {
|
||||||
|
|
||||||
// TODO - refactor into get_vector_member with field identifier values `(void*)n`
|
// TODO - refactor into get_vector_member with field identifier values `(void*)n`
|
||||||
{"grid_size", (getter)UIGrid::get_grid_size, NULL, "Grid dimensions (grid_x, grid_y)", NULL},
|
{"grid_size", (getter)UIGrid::get_grid_size, NULL, "Grid dimensions (grid_w, grid_h)", NULL},
|
||||||
{"grid_x", (getter)UIGrid::get_grid_x, NULL, "Grid x dimension", NULL},
|
{"grid_w", (getter)UIGrid::get_grid_w, NULL, "Grid width in cells", NULL},
|
||||||
{"grid_y", (getter)UIGrid::get_grid_y, NULL, "Grid y dimension", NULL},
|
{"grid_h", (getter)UIGrid::get_grid_h, NULL, "Grid height in cells", NULL},
|
||||||
{"position", (getter)UIGrid::get_position, (setter)UIGrid::set_position, "Position of the grid (x, y)", NULL},
|
{"position", (getter)UIGrid::get_position, (setter)UIGrid::set_position, "Position of the grid (x, y)", NULL},
|
||||||
{"pos", (getter)UIDrawable::get_pos, (setter)UIDrawable::set_pos, "Position of the grid as Vector", (void*)PyObjectsEnum::UIGRID},
|
{"pos", (getter)UIDrawable::get_pos, (setter)UIDrawable::set_pos, "Position of the grid as Vector", (void*)PyObjectsEnum::UIGRID},
|
||||||
{"size", (getter)UIGrid::get_size, (setter)UIGrid::set_size, "Size of the grid (width, height)", NULL},
|
{"size", (getter)UIGrid::get_size, (setter)UIGrid::set_size, "Size of the grid as Vector (width, height)", NULL},
|
||||||
{"center", (getter)UIGrid::get_center, (setter)UIGrid::set_center, "Grid coordinate at the center of the Grid's view (pan)", NULL},
|
{"center", (getter)UIGrid::get_center, (setter)UIGrid::set_center, "Grid coordinate at the center of the Grid's view (pan)", NULL},
|
||||||
|
|
||||||
{"entities", (getter)UIGrid::get_entities, NULL, "EntityCollection of entities on this grid", NULL},
|
{"entities", (getter)UIGrid::get_entities, NULL, "EntityCollection of entities on this grid", NULL},
|
||||||
|
|
@ -2119,9 +2129,9 @@ PyObject* UIGrid::get_entities(PyUIGridObject* self, void* closure)
|
||||||
PyObject* UIGrid::get_children(PyUIGridObject* self, void* closure)
|
PyObject* UIGrid::get_children(PyUIGridObject* self, void* closure)
|
||||||
{
|
{
|
||||||
// Returns UICollection for UIDrawable children (speech bubbles, effects, overlays)
|
// Returns UICollection for UIDrawable children (speech bubbles, effects, overlays)
|
||||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "UICollection");
|
// Use the type directly from namespace (#189 - type not exported to module)
|
||||||
|
PyTypeObject* type = &mcrfpydef::PyUICollectionType;
|
||||||
auto o = (PyUICollectionObject*)type->tp_alloc(type, 0);
|
auto o = (PyUICollectionObject*)type->tp_alloc(type, 0);
|
||||||
Py_DECREF(type);
|
|
||||||
if (o) {
|
if (o) {
|
||||||
o->data = self->data->children;
|
o->data = self->data->children;
|
||||||
o->owner = self->data; // #122: Set owner for parent tracking
|
o->owner = self->data; // #122: Set owner for parent tracking
|
||||||
|
|
@ -2245,7 +2255,7 @@ std::optional<sf::Vector2i> UIGrid::screenToCell(sf::Vector2f screen_pos) const
|
||||||
int cell_y = static_cast<int>(std::floor(grid_space_y / (ptex ? ptex->sprite_height : DEFAULT_CELL_HEIGHT)));
|
int cell_y = static_cast<int>(std::floor(grid_space_y / (ptex ? ptex->sprite_height : DEFAULT_CELL_HEIGHT)));
|
||||||
|
|
||||||
// Check if within valid cell range
|
// Check if within valid cell range
|
||||||
if (cell_x < 0 || cell_x >= grid_x || cell_y < 0 || cell_y >= grid_y) {
|
if (cell_x < 0 || cell_x >= grid_w || cell_y < 0 || cell_y >= grid_h) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2642,7 +2652,7 @@ PyObject* UIEntityCollection::append(PyUIEntityCollectionObject* self, PyObject*
|
||||||
|
|
||||||
// Initialize gridstate if not already done
|
// Initialize gridstate if not already done
|
||||||
if (entity->data->gridstate.size() == 0 && self->grid) {
|
if (entity->data->gridstate.size() == 0 && self->grid) {
|
||||||
entity->data->gridstate.resize(self->grid->grid_x * self->grid->grid_y);
|
entity->data->gridstate.resize(self->grid->grid_w * self->grid->grid_h);
|
||||||
// Initialize all cells as not visible/discovered
|
// Initialize all cells as not visible/discovered
|
||||||
for (auto& state : entity->data->gridstate) {
|
for (auto& state : entity->data->gridstate) {
|
||||||
state.visible = false;
|
state.visible = false;
|
||||||
|
|
@ -2850,7 +2860,7 @@ PyObject* UIEntityCollection::insert(PyUIEntityCollectionObject* self, PyObject*
|
||||||
|
|
||||||
// Initialize gridstate if needed
|
// Initialize gridstate if needed
|
||||||
if (entity->data->gridstate.size() == 0 && self->grid) {
|
if (entity->data->gridstate.size() == 0 && self->grid) {
|
||||||
entity->data->gridstate.resize(self->grid->grid_x * self->grid->grid_y);
|
entity->data->gridstate.resize(self->grid->grid_w * self->grid->grid_h);
|
||||||
for (auto& state : entity->data->gridstate) {
|
for (auto& state : entity->data->gridstate) {
|
||||||
state.visible = false;
|
state.visible = false;
|
||||||
state.discovered = false;
|
state.discovered = false;
|
||||||
|
|
|
||||||
20
src/UIGrid.h
20
src/UIGrid.h
|
|
@ -69,7 +69,7 @@ public:
|
||||||
void resize(float w, float h) override;
|
void resize(float w, float h) override;
|
||||||
void onPositionChanged() override;
|
void onPositionChanged() override;
|
||||||
|
|
||||||
int grid_x, grid_y;
|
int grid_w, grid_h;
|
||||||
//int grid_size; // grid sizes are implied by IndexTexture now
|
//int grid_size; // grid sizes are implied by IndexTexture now
|
||||||
sf::RectangleShape box;
|
sf::RectangleShape box;
|
||||||
float center_x, center_y, zoom;
|
float center_x, center_y, zoom;
|
||||||
|
|
@ -142,8 +142,8 @@ public:
|
||||||
|
|
||||||
static int init(PyUIGridObject* self, PyObject* args, PyObject* kwds);
|
static int init(PyUIGridObject* self, PyObject* args, PyObject* kwds);
|
||||||
static PyObject* get_grid_size(PyUIGridObject* self, void* closure);
|
static PyObject* get_grid_size(PyUIGridObject* self, void* closure);
|
||||||
static PyObject* get_grid_x(PyUIGridObject* self, void* closure);
|
static PyObject* get_grid_w(PyUIGridObject* self, void* closure);
|
||||||
static PyObject* get_grid_y(PyUIGridObject* self, void* closure);
|
static PyObject* get_grid_h(PyUIGridObject* self, void* closure);
|
||||||
static PyObject* get_position(PyUIGridObject* self, void* closure);
|
static PyObject* get_position(PyUIGridObject* self, void* closure);
|
||||||
static int set_position(PyUIGridObject* self, PyObject* value, void* closure);
|
static int set_position(PyUIGridObject* self, PyObject* value, void* closure);
|
||||||
static PyObject* get_size(PyUIGridObject* self, void* closure);
|
static PyObject* get_size(PyUIGridObject* self, void* closure);
|
||||||
|
|
@ -279,7 +279,7 @@ namespace mcrfpydef {
|
||||||
"Args:\n"
|
"Args:\n"
|
||||||
" pos (tuple, optional): Position as (x, y) tuple. Default: (0, 0)\n"
|
" pos (tuple, optional): Position as (x, y) tuple. Default: (0, 0)\n"
|
||||||
" size (tuple, optional): Size as (width, height) tuple. Default: auto-calculated from grid_size\n"
|
" size (tuple, optional): Size as (width, height) tuple. Default: auto-calculated from grid_size\n"
|
||||||
" grid_size (tuple, optional): Grid dimensions as (grid_x, grid_y) tuple. Default: (2, 2)\n"
|
" grid_size (tuple, optional): Grid dimensions as (grid_w, grid_h) tuple. Default: (2, 2)\n"
|
||||||
" texture (Texture, optional): Texture containing tile sprites. Default: default texture\n\n"
|
" texture (Texture, optional): Texture containing tile sprites. Default: default texture\n\n"
|
||||||
"Keyword Args:\n"
|
"Keyword Args:\n"
|
||||||
" fill_color (Color): Background fill color. Default: None\n"
|
" fill_color (Color): Background fill color. Default: None\n"
|
||||||
|
|
@ -296,18 +296,18 @@ namespace mcrfpydef {
|
||||||
" y (float): Y position override. Default: 0\n"
|
" y (float): Y position override. Default: 0\n"
|
||||||
" w (float): Width override. Default: auto-calculated\n"
|
" w (float): Width override. Default: auto-calculated\n"
|
||||||
" h (float): Height override. Default: auto-calculated\n"
|
" h (float): Height override. Default: auto-calculated\n"
|
||||||
" grid_x (int): Grid width override. Default: 2\n"
|
" grid_w (int): Grid width override. Default: 2\n"
|
||||||
" grid_y (int): Grid height override. Default: 2\n\n"
|
" grid_h (int): Grid height override. Default: 2\n\n"
|
||||||
"Attributes:\n"
|
"Attributes:\n"
|
||||||
" x, y (float): Position in pixels\n"
|
" x, y (float): Position in pixels\n"
|
||||||
" w, h (float): Size in pixels\n"
|
" w, h (float): Size in pixels\n"
|
||||||
" pos (Vector): Position as a Vector object\n"
|
" pos (Vector): Position as a Vector object\n"
|
||||||
" size (tuple): Size as (width, height) tuple\n"
|
" size (Vector): Size as (width, height) Vector\n"
|
||||||
" center (tuple): Center point as (x, y) tuple\n"
|
" center (Vector): Center point as (x, y) Vector\n"
|
||||||
" center_x, center_y (float): Center point coordinates\n"
|
" center_x, center_y (float): Center point coordinates\n"
|
||||||
" zoom (float): Zoom level for rendering\n"
|
" zoom (float): Zoom level for rendering\n"
|
||||||
" grid_size (tuple): Grid dimensions (width, height) in tiles\n"
|
" grid_size (Vector): Grid dimensions (width, height) in tiles\n"
|
||||||
" grid_x, grid_y (int): Grid dimensions\n"
|
" grid_w, grid_h (int): Grid dimensions\n"
|
||||||
" texture (Texture): Tile texture atlas\n"
|
" texture (Texture): Tile texture atlas\n"
|
||||||
" fill_color (Color): Background color\n"
|
" fill_color (Color): Background color\n"
|
||||||
" entities (EntityCollection): Collection of entities in the grid\n"
|
" entities (EntityCollection): Collection of entities in the grid\n"
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ PyObject* UIGridPointState::get_point(PyUIGridPointStateObject* self, void* clos
|
||||||
if (!obj) return NULL;
|
if (!obj) return NULL;
|
||||||
|
|
||||||
// Get the GridPoint from the grid
|
// Get the GridPoint from the grid
|
||||||
int idx = self->y * self->grid->grid_x + self->x;
|
int idx = self->y * self->grid->grid_w + self->x;
|
||||||
if (idx < 0 || idx >= static_cast<int>(self->grid->points.size())) {
|
if (idx < 0 || idx >= static_cast<int>(self->grid->points.size())) {
|
||||||
Py_DECREF(obj);
|
Py_DECREF(obj);
|
||||||
PyErr_SetString(PyExc_IndexError, "GridPointState position out of bounds");
|
PyErr_SetString(PyExc_IndexError, "GridPointState position out of bounds");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue