From 71eb01c9508ffe535acc3425cd0f01bcf83080d1 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Sat, 7 Mar 2026 23:18:42 -0500 Subject: [PATCH] Replace PyObject_GetAttrString with direct type references Replace ~230 occurrences of PyObject_GetAttrString(McRFPy_API::mcrf_module, "TypeName") with direct &mcrfpydef::PyXxxType references across 32 source files. Each PyObject_GetAttrString call returns a new reference. When used inline in PyObject_IsInstance(), that reference was immediately leaked. When used for tp_alloc, the reference required careful Py_DECREF management that was often missing on error paths. Direct type references are compile-time constants that never need reference counting, eliminating ~230 potential leak sites and removing ~100 lines of Py_DECREF/Py_XDECREF cleanup code. Also adds extractDrawable() helper in UICollection.cpp to replace repeated 8-way type-check-and-extract chains with a single function call. Closes #267, closes #268 Co-Authored-By: Claude Opus 4.6 --- src/3d/Entity3D.cpp | 7 +- src/3d/PyVoxelGrid.cpp | 6 +- src/3d/Viewport3D.cpp | 45 +---- src/Animation.cpp | 37 +--- src/McRFPy_API.cpp | 15 +- src/McRFPy_Automation.cpp | 26 +-- src/PyAnimation.cpp | 25 +-- src/PyBSP.cpp | 9 +- src/PyCallable.cpp | 32 +-- src/PyColor.cpp | 25 +-- src/PyDiscreteMap.cpp | 39 +--- src/PyFont.cpp | 3 +- src/PyHeightMap.cpp | 54 +---- src/PyMouse.cpp | 9 +- src/PyMusic.cpp | 3 +- src/PyNoiseSource.cpp | 11 +- src/PyScene.cpp | 23 +-- src/PySceneObject.cpp | 22 +-- src/PySound.cpp | 3 +- src/PyVector.cpp | 34 ++-- src/UIArc.cpp | 13 +- src/UICaption.cpp | 10 +- src/UICircle.cpp | 26 +-- src/UICollection.cpp | 406 ++++++++------------------------------ src/UIDrawable.cpp | 88 ++------- src/UIEntity.cpp | 28 +-- src/UIFrame.cpp | 49 ++--- src/UIGrid.cpp | 64 ++---- src/UIGridPathfinding.cpp | 24 +-- src/UIGridPoint.cpp | 25 +-- src/UILine.cpp | 15 +- src/UISprite.cpp | 17 +- 32 files changed, 249 insertions(+), 944 deletions(-) diff --git a/src/3d/Entity3D.cpp b/src/3d/Entity3D.cpp index 667efad..f320aa7 100644 --- a/src/3d/Entity3D.cpp +++ b/src/3d/Entity3D.cpp @@ -1231,14 +1231,9 @@ PyObject* Entity3D::py_animate(PyEntity3DObject* self, PyObject* args, PyObject* } // Create and return a PyAnimation wrapper - PyTypeObject* animType = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Animation"); - if (!animType) { - PyErr_SetString(PyExc_RuntimeError, "Could not find Animation type"); - return NULL; - } + PyTypeObject* animType = &mcrfpydef::PyAnimationType; PyAnimationObject* pyAnim = (PyAnimationObject*)animType->tp_alloc(animType, 0); - Py_DECREF(animType); if (!pyAnim) { return NULL; diff --git a/src/3d/PyVoxelGrid.cpp b/src/3d/PyVoxelGrid.cpp index aaf5b60..8e0895b 100644 --- a/src/3d/PyVoxelGrid.cpp +++ b/src/3d/PyVoxelGrid.cpp @@ -390,15 +390,11 @@ PyObject* PyVoxelGrid::get_material(PyVoxelGridObject* self, PyObject* args) { const mcrf::VoxelMaterial& mat = self->data->getMaterial(static_cast(id)); // Create color object - PyObject* color_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"); - if (!color_type) { - return nullptr; - } + PyObject* color_type = (PyObject*)&mcrfpydef::PyColorType; PyObject* color_obj = PyObject_Call(color_type, Py_BuildValue("(iiii)", mat.color.r, mat.color.g, mat.color.b, mat.color.a), nullptr); - Py_DECREF(color_type); if (!color_obj) { return nullptr; diff --git a/src/3d/Viewport3D.cpp b/src/3d/Viewport3D.cpp index 481111b..0cfc228 100644 --- a/src/3d/Viewport3D.cpp +++ b/src/3d/Viewport3D.cpp @@ -1841,11 +1841,7 @@ int Viewport3D::init(PyViewport3DObject* self, PyObject* args, PyObject* kwds) { } // Check if this is a Python subclass (for callback method support) - PyObject* viewport3d_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Viewport3D"); - if (viewport3d_type) { - self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != viewport3d_type; - Py_DECREF(viewport3d_type); - } + self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != (PyObject*)&mcrfpydef::PyViewport3DType; return 0; } @@ -1940,19 +1936,10 @@ static PyObject* Viewport3D_build_terrain(PyViewport3DObject* self, PyObject* ar } // Check if heightmap_obj is a PyHeightMapObject - // Get the HeightMap type from the module - PyObject* heightmap_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "HeightMap"); - if (!heightmap_type) { - PyErr_SetString(PyExc_RuntimeError, "HeightMap type not found"); - return NULL; - } - - if (!PyObject_IsInstance(heightmap_obj, heightmap_type)) { - Py_DECREF(heightmap_type); + if (!PyObject_IsInstance(heightmap_obj, (PyObject*)&mcrfpydef::PyHeightMapType)) { PyErr_SetString(PyExc_TypeError, "heightmap must be a HeightMap object"); return NULL; } - Py_DECREF(heightmap_type); // Get the TCOD heightmap pointer from the Python object PyHeightMapObject* hm = reinterpret_cast(heightmap_obj); @@ -1987,20 +1974,14 @@ static PyObject* Viewport3D_apply_terrain_colors(PyViewport3DObject* self, PyObj } // Validate all three are HeightMap objects - PyObject* heightmap_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "HeightMap"); - if (!heightmap_type) { - PyErr_SetString(PyExc_RuntimeError, "HeightMap type not found"); - return NULL; - } + PyObject* heightmap_type = (PyObject*)&mcrfpydef::PyHeightMapType; if (!PyObject_IsInstance(r_obj, heightmap_type) || !PyObject_IsInstance(g_obj, heightmap_type) || !PyObject_IsInstance(b_obj, heightmap_type)) { - Py_DECREF(heightmap_type); PyErr_SetString(PyExc_TypeError, "r_map, g_map, and b_map must all be HeightMap objects"); return NULL; } - Py_DECREF(heightmap_type); // Get the TCOD heightmap pointers PyHeightMapObject* r_hm = reinterpret_cast(r_obj); @@ -2082,18 +2063,10 @@ static PyObject* Viewport3D_apply_heightmap(PyViewport3DObject* self, PyObject* } // Validate HeightMap type - PyObject* heightmap_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "HeightMap"); - if (!heightmap_type) { - PyErr_SetString(PyExc_RuntimeError, "HeightMap type not found"); - return NULL; - } - - if (!PyObject_IsInstance(hm_obj, heightmap_type)) { - Py_DECREF(heightmap_type); + if (!PyObject_IsInstance(hm_obj, (PyObject*)&mcrfpydef::PyHeightMapType)) { PyErr_SetString(PyExc_TypeError, "heightmap must be a HeightMap object"); return NULL; } - Py_DECREF(heightmap_type); PyHeightMapObject* hm = reinterpret_cast(hm_obj); if (!hm->heightmap) { @@ -2118,18 +2091,10 @@ static PyObject* Viewport3D_apply_threshold(PyViewport3DObject* self, PyObject* } // Validate HeightMap type - PyObject* heightmap_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "HeightMap"); - if (!heightmap_type) { - PyErr_SetString(PyExc_RuntimeError, "HeightMap type not found"); - return NULL; - } - - if (!PyObject_IsInstance(hm_obj, heightmap_type)) { - Py_DECREF(heightmap_type); + if (!PyObject_IsInstance(hm_obj, (PyObject*)&mcrfpydef::PyHeightMapType)) { PyErr_SetString(PyExc_TypeError, "heightmap must be a HeightMap object"); return NULL; } - Py_DECREF(heightmap_type); PyHeightMapObject* hm = reinterpret_cast(hm_obj); if (!hm->heightmap) { diff --git a/src/Animation.cpp b/src/Animation.cpp index 37b602f..1a52936 100644 --- a/src/Animation.cpp +++ b/src/Animation.cpp @@ -514,8 +514,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { switch (drawable->derived_type()) { case PyObjectsEnum::UIFRAME: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"); - if (!type) return nullptr; + type = &mcrfpydef::PyUIFrameType; auto pyObj = (PyUIFrameObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(drawable); @@ -526,8 +525,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { } case PyObjectsEnum::UICAPTION: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"); - if (!type) return nullptr; + type = &mcrfpydef::PyUICaptionType; auto pyObj = (PyUICaptionObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(drawable); @@ -539,8 +537,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { } case PyObjectsEnum::UISPRITE: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"); - if (!type) return nullptr; + type = &mcrfpydef::PyUISpriteType; auto pyObj = (PyUISpriteObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(drawable); @@ -551,8 +548,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { } case PyObjectsEnum::UIGRID: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"); - if (!type) return nullptr; + type = &mcrfpydef::PyUIGridType; auto pyObj = (PyUIGridObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(drawable); @@ -563,8 +559,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { } case PyObjectsEnum::UILINE: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Line"); - if (!type) return nullptr; + type = &mcrfpydef::PyUILineType; auto pyObj = (PyUILineObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(drawable); @@ -575,8 +570,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { } case PyObjectsEnum::UICIRCLE: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Circle"); - if (!type) return nullptr; + type = &mcrfpydef::PyUICircleType; auto pyObj = (PyUICircleObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(drawable); @@ -587,8 +581,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { } case PyObjectsEnum::UIARC: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Arc"); - if (!type) return nullptr; + type = &mcrfpydef::PyUIArcType; auto pyObj = (PyUIArcObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(drawable); @@ -601,10 +594,6 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { Py_RETURN_NONE; } - if (type) { - Py_DECREF(type); - } - return obj ? obj : Py_None; } @@ -622,13 +611,9 @@ static PyObject* convertEntityToPython(std::shared_ptr entity) { } } - PyTypeObject* type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity"); - if (!type) { - Py_RETURN_NONE; - } + PyTypeObject* type = &mcrfpydef::PyUIEntityType; auto pyObj = (PyUIEntityObject*)type->tp_alloc(type, 0); - Py_DECREF(type); if (!pyObj) { Py_RETURN_NONE; @@ -653,13 +638,9 @@ static PyObject* convertEntity3DToPython(std::shared_ptr entity) } // Create a new wrapper - PyTypeObject* type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity3D"); - if (!type) { - Py_RETURN_NONE; - } + PyTypeObject* type = &mcrfpydef::PyEntity3DType; auto pyObj = (PyEntity3DObject*)type->tp_alloc(type, 0); - Py_DECREF(type); if (!pyObj) { Py_RETURN_NONE; diff --git a/src/McRFPy_API.cpp b/src/McRFPy_API.cpp index a900713..328be76 100644 --- a/src/McRFPy_API.cpp +++ b/src/McRFPy_API.cpp @@ -1544,7 +1544,7 @@ static void find_in_collection(std::vector>* collect switch (drawable->derived_type()) { case PyObjectsEnum::UIFRAME: { auto frame = std::static_pointer_cast(drawable); - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"); + auto type = &mcrfpydef::PyUIFrameType; auto o = (PyUIFrameObject*)type->tp_alloc(type, 0); if (o) { o->data = frame; @@ -1554,7 +1554,7 @@ static void find_in_collection(std::vector>* collect } case PyObjectsEnum::UICAPTION: { auto caption = std::static_pointer_cast(drawable); - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"); + auto type = &mcrfpydef::PyUICaptionType; auto o = (PyUICaptionObject*)type->tp_alloc(type, 0); if (o) { o->data = caption; @@ -1564,7 +1564,7 @@ static void find_in_collection(std::vector>* collect } case PyObjectsEnum::UISPRITE: { auto sprite = std::static_pointer_cast(drawable); - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"); + auto type = &mcrfpydef::PyUISpriteType; auto o = (PyUISpriteObject*)type->tp_alloc(type, 0); if (o) { o->data = sprite; @@ -1574,7 +1574,7 @@ static void find_in_collection(std::vector>* collect } case PyObjectsEnum::UIGRID: { auto grid = std::static_pointer_cast(drawable); - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"); + auto type = &mcrfpydef::PyUIGridType; auto o = (PyUIGridObject*)type->tp_alloc(type, 0); if (o) { o->data = grid; @@ -1620,7 +1620,7 @@ static void find_in_grid_entities(UIGrid* grid, const std::string& pattern, // Entities delegate name to their sprite if (name_matches_pattern(entity->sprite.name, pattern)) { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity"); + auto type = &mcrfpydef::PyUIEntityType; auto o = (PyUIEntityObject*)type->tp_alloc(type, 0); if (o) { o->data = entity; @@ -1817,15 +1817,12 @@ static bool extract_point(PyObject* obj, int* x, int* y, const char* arg_name) { } // Try Vector type - PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (vector_type && PyObject_IsInstance(obj, vector_type)) { - Py_DECREF(vector_type); + if (PyObject_IsInstance(obj, (PyObject*)&mcrfpydef::PyVectorType)) { PyVectorObject* vec = (PyVectorObject*)obj; *x = static_cast(vec->data.x); *y = static_cast(vec->data.y); return true; } - Py_XDECREF(vector_type); PyErr_Format(PyExc_TypeError, "%s: expected (x, y) tuple or Vector, got %s", diff --git a/src/McRFPy_Automation.cpp b/src/McRFPy_Automation.cpp index 7ca8fe2..2d465e7 100644 --- a/src/McRFPy_Automation.cpp +++ b/src/McRFPy_Automation.cpp @@ -2,6 +2,7 @@ #include "McRFPy_API.h" #include "GameEngine.h" #include "PyPositionHelper.h" +#include "PyVector.h" #include #include #include @@ -262,37 +263,20 @@ PyObject* McRFPy_Automation::_position(PyObject* self, PyObject* args) { y = simulated_mouse_pos.y; } - // Return a Vector object - get type from module to ensure we use the initialized type - auto vector_type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!vector_type) { - PyErr_SetString(PyExc_RuntimeError, "Vector type not found in mcrfpy module"); - return NULL; - } - PyObject* result = PyObject_CallFunction((PyObject*)vector_type, "ff", (float)x, (float)y); - Py_DECREF(vector_type); + // Return a Vector object + PyObject* result = PyObject_CallFunction((PyObject*)&mcrfpydef::PyVectorType, "ff", (float)x, (float)y); return result; } // Get screen size - returns Vector object PyObject* McRFPy_Automation::_size(PyObject* self, PyObject* args) { - // Get Vector type from module to ensure we use the initialized type - auto vector_type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!vector_type) { - PyErr_SetString(PyExc_RuntimeError, "Vector type not found in mcrfpy module"); - return NULL; - } - auto engine = getGameEngine(); if (!engine || !engine->getRenderTargetPtr()) { - PyObject* result = PyObject_CallFunction((PyObject*)vector_type, "ff", 1024.0f, 768.0f); // Default size - Py_DECREF(vector_type); - return result; + return PyObject_CallFunction((PyObject*)&mcrfpydef::PyVectorType, "ff", 1024.0f, 768.0f); // Default size } sf::Vector2u size = engine->getRenderTarget().getSize(); - PyObject* result = PyObject_CallFunction((PyObject*)vector_type, "ff", (float)size.x, (float)size.y); - Py_DECREF(vector_type); - return result; + return PyObject_CallFunction((PyObject*)&mcrfpydef::PyVectorType, "ff", (float)size.x, (float)size.y); } // Check if coordinates are on screen - accepts onScreen(x, y) or onScreen(pos) diff --git a/src/PyAnimation.cpp b/src/PyAnimation.cpp index e044b98..38ac6fc 100644 --- a/src/PyAnimation.cpp +++ b/src/PyAnimation.cpp @@ -216,17 +216,11 @@ PyObject* PyAnimation::start(PyAnimationObject* self, PyObject* args, PyObject* return NULL; // Error already set } - // Get type objects from the module to ensure they're initialized - PyObject* frame_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"); - PyObject* caption_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"); - PyObject* sprite_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"); - PyObject* grid_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"); - PyObject* entity_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity"); - + // Use direct type references (no ref counting needed for static types) bool handled = false; // Use PyObject_IsInstance to support inheritance - if (frame_type && PyObject_IsInstance(target_obj, frame_type)) { + if (PyObject_IsInstance(target_obj, (PyObject*)&mcrfpydef::PyUIFrameType)) { PyUIFrameObject* frame = (PyUIFrameObject*)target_obj; if (frame->data) { self->data->start(frame->data); @@ -234,7 +228,7 @@ PyObject* PyAnimation::start(PyAnimationObject* self, PyObject* args, PyObject* handled = true; } } - else if (caption_type && PyObject_IsInstance(target_obj, caption_type)) { + else if (PyObject_IsInstance(target_obj, (PyObject*)&mcrfpydef::PyUICaptionType)) { PyUICaptionObject* caption = (PyUICaptionObject*)target_obj; if (caption->data) { self->data->start(caption->data); @@ -242,7 +236,7 @@ PyObject* PyAnimation::start(PyAnimationObject* self, PyObject* args, PyObject* handled = true; } } - else if (sprite_type && PyObject_IsInstance(target_obj, sprite_type)) { + else if (PyObject_IsInstance(target_obj, (PyObject*)&mcrfpydef::PyUISpriteType)) { PyUISpriteObject* sprite = (PyUISpriteObject*)target_obj; if (sprite->data) { self->data->start(sprite->data); @@ -250,7 +244,7 @@ PyObject* PyAnimation::start(PyAnimationObject* self, PyObject* args, PyObject* handled = true; } } - else if (grid_type && PyObject_IsInstance(target_obj, grid_type)) { + else if (PyObject_IsInstance(target_obj, (PyObject*)&mcrfpydef::PyUIGridType)) { PyUIGridObject* grid = (PyUIGridObject*)target_obj; if (grid->data) { self->data->start(grid->data); @@ -258,7 +252,7 @@ PyObject* PyAnimation::start(PyAnimationObject* self, PyObject* args, PyObject* handled = true; } } - else if (entity_type && PyObject_IsInstance(target_obj, entity_type)) { + else if (PyObject_IsInstance(target_obj, (PyObject*)&mcrfpydef::PyUIEntityType)) { // Special handling for Entity since it doesn't inherit from UIDrawable PyUIEntityObject* entity = (PyUIEntityObject*)target_obj; if (entity->data) { @@ -268,13 +262,6 @@ PyObject* PyAnimation::start(PyAnimationObject* self, PyObject* args, PyObject* } } - // Clean up references - Py_XDECREF(frame_type); - Py_XDECREF(caption_type); - Py_XDECREF(sprite_type); - Py_XDECREF(grid_type); - Py_XDECREF(entity_type); - if (!handled) { PyErr_SetString(PyExc_TypeError, "Target must be a Frame, Caption, Sprite, Grid, or Entity (or a subclass of these)"); return NULL; diff --git a/src/PyBSP.cpp b/src/PyBSP.cpp index dd898ef..2968d7e 100644 --- a/src/PyBSP.cpp +++ b/src/PyBSP.cpp @@ -947,19 +947,12 @@ PyObject* PyBSP::to_heightmap(PyBSPObject* self, PyObject* args, PyObject* kwds) } // Create HeightMap - PyObject* heightmap_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "HeightMap"); - if (!heightmap_type) { - PyErr_SetString(PyExc_RuntimeError, "HeightMap type not found"); - return nullptr; - } - PyObject* size_tuple = Py_BuildValue("(ii)", width, height); PyObject* hmap_args = PyTuple_Pack(1, size_tuple); Py_DECREF(size_tuple); - PyHeightMapObject* hmap = (PyHeightMapObject*)PyObject_Call(heightmap_type, hmap_args, nullptr); + PyHeightMapObject* hmap = (PyHeightMapObject*)PyObject_Call((PyObject*)&mcrfpydef::PyHeightMapType, hmap_args, nullptr); Py_DECREF(hmap_args); - Py_DECREF(heightmap_type); if (!hmap) { return nullptr; diff --git a/src/PyCallable.cpp b/src/PyCallable.cpp index eafa205..d98bd5a 100644 --- a/src/PyCallable.cpp +++ b/src/PyCallable.cpp @@ -53,16 +53,8 @@ PyClickCallable::PyClickCallable() void PyClickCallable::call(sf::Vector2f mousepos, std::string button, std::string action) { - // Create a Vector object for the position - must fetch the finalized type from the module - PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!vector_type) { - std::cerr << "Failed to get Vector type for click callback" << std::endl; - PyErr_Print(); - PyErr_Clear(); - return; - } - PyObject* pos = PyObject_CallFunction(vector_type, "ff", mousepos.x, mousepos.y); - Py_DECREF(vector_type); + // Create a Vector object for the position + PyObject* pos = PyObject_CallFunction((PyObject*)&mcrfpydef::PyVectorType, "ff", mousepos.x, mousepos.y); if (!pos) { std::cerr << "Failed to create Vector object for click callback" << std::endl; PyErr_Print(); @@ -203,15 +195,7 @@ void PyHoverCallable::call(sf::Vector2f mousepos) if (target == Py_None || target == NULL) return; // Create a Vector object for the position - PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!vector_type) { - std::cerr << "Failed to get Vector type for hover callback" << std::endl; - PyErr_Print(); - PyErr_Clear(); - return; - } - PyObject* pos = PyObject_CallFunction(vector_type, "ff", mousepos.x, mousepos.y); - Py_DECREF(vector_type); + PyObject* pos = PyObject_CallFunction((PyObject*)&mcrfpydef::PyVectorType, "ff", mousepos.x, mousepos.y); if (!pos) { std::cerr << "Failed to create Vector object for hover callback" << std::endl; PyErr_Print(); @@ -263,15 +247,7 @@ void PyCellHoverCallable::call(sf::Vector2i cellpos) if (target == Py_None || target == NULL) return; // Create a Vector object for the cell position - PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!vector_type) { - std::cerr << "Failed to get Vector type for cell hover callback" << std::endl; - PyErr_Print(); - PyErr_Clear(); - return; - } - PyObject* pos = PyObject_CallFunction(vector_type, "ii", cellpos.x, cellpos.y); - Py_DECREF(vector_type); + PyObject* pos = PyObject_CallFunction((PyObject*)&mcrfpydef::PyVectorType, "ii", cellpos.x, cellpos.y); if (!pos) { std::cerr << "Failed to create Vector object for cell hover callback" << std::endl; PyErr_Print(); diff --git a/src/PyColor.cpp b/src/PyColor.cpp index 9a284e0..f09a6fe 100644 --- a/src/PyColor.cpp +++ b/src/PyColor.cpp @@ -52,12 +52,8 @@ PyColor::PyColor(sf::Color target) PyObject* PyColor::pyObject() { - PyTypeObject* type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"); - if (!type) return nullptr; - - PyColorObject* obj = (PyColorObject*)type->tp_alloc(type, 0); - Py_DECREF(type); - + PyColorObject* obj = (PyColorObject*)mcrfpydef::PyColorType.tp_alloc(&mcrfpydef::PyColorType, 0); + if (obj) { obj->data = data; } @@ -72,14 +68,9 @@ sf::Color PyColor::fromPy(PyObject* obj) } // Check if it's already a Color object - PyTypeObject* color_type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"); - if (color_type) { - bool is_color = PyObject_TypeCheck(obj, color_type); - Py_DECREF(color_type); - if (is_color) { - PyColorObject* self = (PyColorObject*)obj; - return self->data; - } + if (PyObject_TypeCheck(obj, &mcrfpydef::PyColorType)) { + PyColorObject* self = (PyColorObject*)obj; + return self->data; } // Handle tuple or list input @@ -382,9 +373,7 @@ PyObject* PyColor::lerp(PyColorObject* self, PyObject* args) } // Validate other color - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"); - if (!PyObject_IsInstance(other_obj, (PyObject*)type)) { - Py_DECREF(type); + if (!PyObject_IsInstance(other_obj, (PyObject*)&mcrfpydef::PyColorType)) { PyErr_SetString(PyExc_TypeError, "First argument must be a Color"); return NULL; } @@ -402,8 +391,8 @@ PyObject* PyColor::lerp(PyColorObject* self, PyObject* args) sf::Uint8 a = static_cast(self->data.a + (other->data.a - self->data.a) * t); // Create new Color object + auto type = &mcrfpydef::PyColorType; PyColorObject* result = (PyColorObject*)type->tp_alloc(type, 0); - Py_DECREF(type); if (result) { result->data = sf::Color(r, g, b, a); diff --git a/src/PyDiscreteMap.cpp b/src/PyDiscreteMap.cpp index ccb600b..1939de7 100644 --- a/src/PyDiscreteMap.cpp +++ b/src/PyDiscreteMap.cpp @@ -56,17 +56,12 @@ static PyObject* valueToResult(uint8_t value, PyObject* enum_type) { // Helper: Create a new DiscreteMap object with given dimensions // ============================================================================ static PyDiscreteMapObject* CreateNewDiscreteMap(int width, int height) { - // Get the DiscreteMap type from the module - PyObject* dmap_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "DiscreteMap"); - if (!dmap_type) { - PyErr_SetString(PyExc_RuntimeError, "DiscreteMap type not found in module"); - return nullptr; - } + // Get the DiscreteMap type + PyObject* dmap_type = (PyObject*)&mcrfpydef::PyDiscreteMapType; // Create size tuple PyObject* size_tuple = Py_BuildValue("(ii)", width, height); if (!size_tuple) { - Py_DECREF(dmap_type); return nullptr; } @@ -74,14 +69,12 @@ static PyDiscreteMapObject* CreateNewDiscreteMap(int width, int height) { PyObject* args = PyTuple_Pack(1, size_tuple); Py_DECREF(size_tuple); if (!args) { - Py_DECREF(dmap_type); return nullptr; } // Create the new object PyDiscreteMapObject* new_dmap = (PyDiscreteMapObject*)PyObject_Call(dmap_type, args, nullptr); Py_DECREF(args); - Py_DECREF(dmap_type); if (!new_dmap) { return nullptr; // Python error already set @@ -94,16 +87,8 @@ static PyDiscreteMapObject* CreateNewDiscreteMap(int width, int height) { // Helper: Validate another DiscreteMap for binary operations // ============================================================================ static PyDiscreteMapObject* validateOtherDiscreteMapType(PyObject* other_obj, const char* method_name) { - // Get the DiscreteMap type from the module - PyObject* dmap_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "DiscreteMap"); - if (!dmap_type) { - PyErr_SetString(PyExc_RuntimeError, "DiscreteMap type not found in module"); - return nullptr; - } - // Check if other is a DiscreteMap - int is_dmap = PyObject_IsInstance(other_obj, dmap_type); - Py_DECREF(dmap_type); + int is_dmap = PyObject_IsInstance(other_obj, (PyObject*)&mcrfpydef::PyDiscreteMapType); if (is_dmap < 0) { return nullptr; // Error during check @@ -1331,14 +1316,7 @@ PyObject* PyDiscreteMap::from_heightmap(PyTypeObject* type, PyObject* args, PyOb } // Validate HeightMap - PyObject* hmap_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "HeightMap"); - if (!hmap_type) { - PyErr_SetString(PyExc_RuntimeError, "HeightMap type not found in module"); - return nullptr; - } - - int is_hmap = PyObject_IsInstance(hmap_obj, hmap_type); - Py_DECREF(hmap_type); + int is_hmap = PyObject_IsInstance(hmap_obj, (PyObject*)&mcrfpydef::PyHeightMapType); if (is_hmap < 0) { return nullptr; @@ -1507,28 +1485,21 @@ PyObject* PyDiscreteMap::to_heightmap(PyDiscreteMapObject* self, PyObject* args, } // Get HeightMap type and create new instance - PyObject* hmap_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "HeightMap"); - if (!hmap_type) { - PyErr_SetString(PyExc_RuntimeError, "HeightMap type not found in module"); - return nullptr; - } + PyObject* hmap_type = (PyObject*)&mcrfpydef::PyHeightMapType; PyObject* size_tuple = Py_BuildValue("(ii)", self->w, self->h); if (!size_tuple) { - Py_DECREF(hmap_type); return nullptr; } PyObject* hmap_args = PyTuple_Pack(1, size_tuple); Py_DECREF(size_tuple); if (!hmap_args) { - Py_DECREF(hmap_type); return nullptr; } PyHeightMapObject* result = (PyHeightMapObject*)PyObject_Call(hmap_type, hmap_args, nullptr); Py_DECREF(hmap_args); - Py_DECREF(hmap_type); if (!result) { return nullptr; diff --git a/src/PyFont.cpp b/src/PyFont.cpp index 22ba217..a1c4fb2 100644 --- a/src/PyFont.cpp +++ b/src/PyFont.cpp @@ -12,8 +12,7 @@ PyFont::PyFont(std::string filename) PyObject* PyFont::pyObject() { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Font"); - //PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyFontType, 0); + auto type = &mcrfpydef::PyFontType; PyObject* obj = PyFont::pynew(type, Py_None, Py_None); try { ((PyFontObject*)obj)->data = shared_from_this(); diff --git a/src/PyHeightMap.cpp b/src/PyHeightMap.cpp index 0c47033..081619f 100644 --- a/src/PyHeightMap.cpp +++ b/src/PyHeightMap.cpp @@ -1218,17 +1218,12 @@ static PyHeightMapObject* validateOtherHeightMapType(PyObject* other_obj, const // Helper: Create a new HeightMap object with same dimensions static PyHeightMapObject* CreateNewHeightMap(int width, int height) { - // Get the HeightMap type from the module - PyObject* heightmap_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "HeightMap"); - if (!heightmap_type) { - PyErr_SetString(PyExc_RuntimeError, "HeightMap type not found in module"); - return nullptr; - } + // Get the HeightMap type + PyObject* heightmap_type = (PyObject*)&mcrfpydef::PyHeightMapType; // Create size tuple PyObject* size_tuple = Py_BuildValue("(ii)", width, height); if (!size_tuple) { - Py_DECREF(heightmap_type); return nullptr; } @@ -1236,14 +1231,12 @@ static PyHeightMapObject* CreateNewHeightMap(int width, int height) PyObject* args = PyTuple_Pack(1, size_tuple); Py_DECREF(size_tuple); if (!args) { - Py_DECREF(heightmap_type); return nullptr; } // Create the new object PyHeightMapObject* new_hmap = (PyHeightMapObject*)PyObject_Call(heightmap_type, args, nullptr); Py_DECREF(args); - Py_DECREF(heightmap_type); if (!new_hmap) { return nullptr; // Python error already set @@ -1981,14 +1974,7 @@ PyObject* PyHeightMap::gradients(PyHeightMapObject* self, PyObject* args, PyObje static PyHeightMapObject* validateOtherHeightMapType(PyObject* other_obj, const char* method_name) { // Check that other is a HeightMap - PyObject* heightmap_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "HeightMap"); - if (!heightmap_type) { - PyErr_SetString(PyExc_RuntimeError, "HeightMap type not found in module"); - return nullptr; - } - - int is_hmap = PyObject_IsInstance(other_obj, heightmap_type); - Py_DECREF(heightmap_type); + int is_hmap = PyObject_IsInstance(other_obj, (PyObject*)&mcrfpydef::PyHeightMapType); if (is_hmap < 0) { return nullptr; // Error in isinstance check @@ -2326,13 +2312,7 @@ static bool parseNoiseSampleParams( } // Validate source is a NoiseSource - PyObject* noise_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "NoiseSource"); - if (!noise_type) { - PyErr_SetString(PyExc_RuntimeError, "NoiseSource type not found in module"); - return false; - } - int is_noise = PyObject_IsInstance(source_obj, noise_type); - Py_DECREF(noise_type); + int is_noise = PyObject_IsInstance(source_obj, (PyObject*)&mcrfpydef::PyNoiseSourceType); if (is_noise < 0) return false; if (!is_noise) { @@ -2539,34 +2519,26 @@ static bool collectBSPNodes( return false; } - PyObject* bspnode_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "BSPNode"); - if (!bspnode_type) { - PyErr_SetString(PyExc_RuntimeError, "BSPNode type not found in module"); - return false; - } + PyObject* bspnode_type = (PyObject*)&mcrfpydef::PyBSPNodeType; Py_ssize_t count = PyList_Size(nodes_list); for (Py_ssize_t i = 0; i < count; i++) { PyObject* item = PyList_GetItem(nodes_list, i); int is_node = PyObject_IsInstance(item, bspnode_type); if (is_node < 0) { - Py_DECREF(bspnode_type); return false; } if (!is_node) { - Py_DECREF(bspnode_type); PyErr_Format(PyExc_TypeError, "%s() nodes[%zd] is not a BSPNode", method_name, i); return false; } PyBSPNodeObject* node = (PyBSPNodeObject*)item; if (!PyBSPNode::checkValid(node)) { - Py_DECREF(bspnode_type); return false; // Error already set } out_nodes.push_back(node->node); } - Py_DECREF(bspnode_type); return true; } @@ -2646,13 +2618,7 @@ PyObject* PyHeightMap::add_bsp(PyHeightMapObject* self, PyObject* args, PyObject } // Validate bsp is a BSP - PyObject* bsp_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "BSP"); - if (!bsp_type) { - PyErr_SetString(PyExc_RuntimeError, "BSP type not found in module"); - return nullptr; - } - int is_bsp = PyObject_IsInstance(bsp_obj, bsp_type); - Py_DECREF(bsp_type); + int is_bsp = PyObject_IsInstance(bsp_obj, (PyObject*)&mcrfpydef::PyBSPType); if (is_bsp < 0) return nullptr; if (!is_bsp) { @@ -2740,13 +2706,7 @@ PyObject* PyHeightMap::multiply_bsp(PyHeightMapObject* self, PyObject* args, PyO } // Validate bsp is a BSP - PyObject* bsp_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "BSP"); - if (!bsp_type) { - PyErr_SetString(PyExc_RuntimeError, "BSP type not found in module"); - return nullptr; - } - int is_bsp = PyObject_IsInstance(bsp_obj, bsp_type); - Py_DECREF(bsp_type); + int is_bsp = PyObject_IsInstance(bsp_obj, (PyObject*)&mcrfpydef::PyBSPType); if (is_bsp < 0) return nullptr; if (!is_bsp) { diff --git a/src/PyMouse.cpp b/src/PyMouse.cpp index a450fce..1561713 100644 --- a/src/PyMouse.cpp +++ b/src/PyMouse.cpp @@ -74,14 +74,7 @@ PyObject* PyMouse::get_pos(PyObject* self, void* closure) sf::Vector2i pos = getMousePosition(); // Return a Vector object - auto vector_type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!vector_type) { - PyErr_SetString(PyExc_RuntimeError, "Vector type not found in mcrfpy module"); - return NULL; - } - PyObject* result = PyObject_CallFunction((PyObject*)vector_type, "ff", (float)pos.x, (float)pos.y); - Py_DECREF(vector_type); - return result; + return PyObject_CallFunction((PyObject*)&mcrfpydef::PyVectorType, "ff", (float)pos.x, (float)pos.y); } PyObject* PyMouse::get_left(PyObject* self, void* closure) diff --git a/src/PyMusic.cpp b/src/PyMusic.cpp index a8dedab..fbd4782 100644 --- a/src/PyMusic.cpp +++ b/src/PyMusic.cpp @@ -76,9 +76,8 @@ void PyMusic::setPosition(float pos) PyObject* PyMusic::pyObject() { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Music"); + auto type = &mcrfpydef::PyMusicType; PyObject* obj = PyMusic::pynew(type, Py_None, Py_None); - Py_DECREF(type); try { ((PyMusicObject*)obj)->data = shared_from_this(); } diff --git a/src/PyNoiseSource.cpp b/src/PyNoiseSource.cpp index 83792cf..258cf41 100644 --- a/src/PyNoiseSource.cpp +++ b/src/PyNoiseSource.cpp @@ -464,28 +464,19 @@ PyObject* PyNoiseSource::sample(PyNoiseSourceObject* self, PyObject* args, PyObj } // Create HeightMap - PyObject* heightmap_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "HeightMap"); - if (!heightmap_type) { - PyErr_SetString(PyExc_RuntimeError, "HeightMap type not found in module"); - return nullptr; - } - PyObject* size_tuple = Py_BuildValue("(ii)", width, height); if (!size_tuple) { - Py_DECREF(heightmap_type); return nullptr; } PyObject* hmap_args = PyTuple_Pack(1, size_tuple); Py_DECREF(size_tuple); if (!hmap_args) { - Py_DECREF(heightmap_type); return nullptr; } - PyHeightMapObject* hmap = (PyHeightMapObject*)PyObject_Call(heightmap_type, hmap_args, nullptr); + PyHeightMapObject* hmap = (PyHeightMapObject*)PyObject_Call((PyObject*)&mcrfpydef::PyHeightMapType, hmap_args, nullptr); Py_DECREF(hmap_args); - Py_DECREF(heightmap_type); if (!hmap) { return nullptr; diff --git a/src/PyScene.cpp b/src/PyScene.cpp index cb72b71..eb053a0 100644 --- a/src/PyScene.cpp +++ b/src/PyScene.cpp @@ -7,6 +7,7 @@ #include "McRFPy_Automation.h" // #111 - For simulated mouse position #include "PythonObjectCache.h" // #184 - For subclass callback support #include "McRFPy_API.h" // For Vector type access +#include "PyVector.h" // For direct PyVectorType reference #include "PyMouseButton.h" // For MouseButton enum #include "PyInputState.h" // For InputState enum #include @@ -55,16 +56,7 @@ static bool tryCallPythonMethod(UIDrawable* drawable, const char* method_name, if (method && PyCallable_Check(method) && method != Py_None) { // Create Vector object for position (matches property callback signature) - PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!vector_type) { - PyErr_Print(); - PyErr_Clear(); - Py_XDECREF(method); - Py_DECREF(pyObj); - return false; - } - PyObject* pos = PyObject_CallFunction(vector_type, "ff", mousepos.x, mousepos.y); - Py_DECREF(vector_type); + PyObject* pos = PyObject_CallFunction((PyObject*)&mcrfpydef::PyVectorType, "ff", mousepos.x, mousepos.y); if (!pos) { PyErr_Print(); PyErr_Clear(); @@ -164,16 +156,7 @@ static bool tryCallPythonMethod(UIDrawable* drawable, const char* method_name, if (method && PyCallable_Check(method) && method != Py_None) { // Create Vector object for position - PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!vector_type) { - PyErr_Print(); - PyErr_Clear(); - Py_XDECREF(method); - Py_DECREF(pyObj); - return false; - } - PyObject* pos = PyObject_CallFunction(vector_type, "ff", mousepos.x, mousepos.y); - Py_DECREF(vector_type); + PyObject* pos = PyObject_CallFunction((PyObject*)&mcrfpydef::PyVectorType, "ff", mousepos.x, mousepos.y); if (!pos) { PyErr_Print(); PyErr_Clear(); diff --git a/src/PySceneObject.cpp b/src/PySceneObject.cpp index d21634f..dc26564 100644 --- a/src/PySceneObject.cpp +++ b/src/PySceneObject.cpp @@ -6,6 +6,7 @@ #include "PyTransition.h" #include "PyKey.h" #include "PyInputState.h" +#include "PyVector.h" #include // Static map to store Python scene objects by name @@ -211,11 +212,8 @@ static PyObject* PySceneClass_get_pos(PySceneObject* self, void* closure) } // Create a Vector object - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!type) return NULL; PyObject* args = Py_BuildValue("(ff)", self->scene->position.x, self->scene->position.y); - PyObject* result = PyObject_CallObject((PyObject*)type, args); - Py_DECREF(type); + PyObject* result = PyObject_CallObject((PyObject*)&mcrfpydef::PyVectorType, args); Py_DECREF(args); return result; } @@ -425,15 +423,8 @@ void PySceneClass::call_on_resize(PySceneObject* self, sf::Vector2u new_size) PyObject* method = PyObject_GetAttrString((PyObject*)self, "on_resize"); if (method && PyCallable_Check(method)) { // Create a Vector object to pass - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!type) { - PyErr_Print(); - Py_DECREF(method); - return; - } PyObject* args = Py_BuildValue("(ff)", (float)new_size.x, (float)new_size.y); - PyObject* vector = PyObject_CallObject((PyObject*)type, args); - Py_DECREF(type); + PyObject* vector = PyObject_CallObject((PyObject*)&mcrfpydef::PyVectorType, args); Py_DECREF(args); if (!vector) { @@ -716,14 +707,11 @@ int McRFPy_API::api_set_current_scene(PyObject* value) if (PyUnicode_Check(value)) { scene_name = PyUnicode_AsUTF8(value); } else { - // Check if value is a Scene or Scene subclass - use same pattern as rest of codebase - PyObject* scene_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Scene"); - if (scene_type && PyObject_IsInstance(value, scene_type)) { - Py_DECREF(scene_type); + // Check if value is a Scene or Scene subclass + if (PyObject_IsInstance(value, (PyObject*)&mcrfpydef::PySceneType)) { PySceneObject* scene_obj = (PySceneObject*)value; scene_name = scene_obj->name.c_str(); } else { - Py_XDECREF(scene_type); PyErr_SetString(PyExc_TypeError, "current_scene must be a Scene object or scene name string"); return -1; } diff --git a/src/PySound.cpp b/src/PySound.cpp index 45d801a..1bf4e63 100644 --- a/src/PySound.cpp +++ b/src/PySound.cpp @@ -91,9 +91,8 @@ void PySound::setPitch(float pitch) PyObject* PySound::pyObject() { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sound"); + auto type = &mcrfpydef::PySoundType; PyObject* obj = PySound::pynew(type, Py_None, Py_None); - Py_DECREF(type); try { ((PySoundObject*)obj)->data = shared_from_this(); } diff --git a/src/PyVector.cpp b/src/PyVector.cpp index 8967d93..c36a2ee 100644 --- a/src/PyVector.cpp +++ b/src/PyVector.cpp @@ -130,11 +130,9 @@ PyVector::PyVector(sf::Vector2f target) PyObject* PyVector::pyObject() { - PyTypeObject* type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!type) return nullptr; - + PyTypeObject* type = &mcrfpydef::PyVectorType; + PyVectorObject* obj = (PyVectorObject*)type->tp_alloc(type, 0); - Py_DECREF(type); if (obj) { obj->data = data; @@ -304,7 +302,7 @@ PyVectorObject* PyVector::from_arg(PyObject* args) PyObject* PyVector::add(PyObject* left, PyObject* right) { // Check if both operands are vectors - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); + auto type = &mcrfpydef::PyVectorType; PyVectorObject* vec1 = nullptr; PyVectorObject* vec2 = nullptr; @@ -326,7 +324,7 @@ PyObject* PyVector::add(PyObject* left, PyObject* right) PyObject* PyVector::subtract(PyObject* left, PyObject* right) { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); + auto type = &mcrfpydef::PyVectorType; PyVectorObject* vec1 = nullptr; PyVectorObject* vec2 = nullptr; @@ -348,7 +346,7 @@ PyObject* PyVector::subtract(PyObject* left, PyObject* right) PyObject* PyVector::multiply(PyObject* left, PyObject* right) { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); + auto type = &mcrfpydef::PyVectorType; PyVectorObject* vec = nullptr; double scalar = 0.0; @@ -377,7 +375,7 @@ PyObject* PyVector::multiply(PyObject* left, PyObject* right) PyObject* PyVector::divide(PyObject* left, PyObject* right) { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); + auto type = &mcrfpydef::PyVectorType; // Only support Vector / scalar if (!PyObject_IsInstance(left, (PyObject*)type) || (!PyFloat_Check(right) && !PyLong_Check(right))) { @@ -402,7 +400,7 @@ PyObject* PyVector::divide(PyObject* left, PyObject* right) PyObject* PyVector::negative(PyObject* self) { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); + auto type = &mcrfpydef::PyVectorType; PyVectorObject* vec = (PyVectorObject*)self; auto result = (PyVectorObject*)type->tp_alloc(type, 0); @@ -426,7 +424,7 @@ int PyVector::bool_check(PyObject* self) PyObject* PyVector::richcompare(PyObject* left, PyObject* right, int op) { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); + auto type = &mcrfpydef::PyVectorType; float left_x, left_y, right_x, right_y; @@ -509,9 +507,9 @@ PyObject* PyVector::normalize(PyVectorObject* self, PyObject* Py_UNUSED(ignored) { float mag = std::sqrt(self->data.x * self->data.x + self->data.y * self->data.y); - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); + auto type = &mcrfpydef::PyVectorType; auto result = (PyVectorObject*)type->tp_alloc(type, 0); - + if (result) { if (mag > 0.0f) { result->data = sf::Vector2f(self->data.x / mag, self->data.y / mag); @@ -526,9 +524,7 @@ PyObject* PyVector::normalize(PyVectorObject* self, PyObject* Py_UNUSED(ignored) PyObject* PyVector::dot(PyVectorObject* self, PyObject* other) { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - - if (!PyObject_IsInstance(other, (PyObject*)type)) { + if (!PyObject_IsInstance(other, (PyObject*)&mcrfpydef::PyVectorType)) { PyErr_SetString(PyExc_TypeError, "Argument must be a Vector"); return NULL; } @@ -541,9 +537,7 @@ PyObject* PyVector::dot(PyVectorObject* self, PyObject* other) PyObject* PyVector::distance_to(PyVectorObject* self, PyObject* other) { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - - if (!PyObject_IsInstance(other, (PyObject*)type)) { + if (!PyObject_IsInstance(other, (PyObject*)&mcrfpydef::PyVectorType)) { PyErr_SetString(PyExc_TypeError, "Argument must be a Vector"); return NULL; } @@ -564,7 +558,7 @@ PyObject* PyVector::angle(PyVectorObject* self, PyObject* Py_UNUSED(ignored)) PyObject* PyVector::copy(PyVectorObject* self, PyObject* Py_UNUSED(ignored)) { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); + auto type = &mcrfpydef::PyVectorType; auto result = (PyVectorObject*)type->tp_alloc(type, 0); if (result) { @@ -576,7 +570,7 @@ PyObject* PyVector::copy(PyVectorObject* self, PyObject* Py_UNUSED(ignored)) PyObject* PyVector::floor(PyVectorObject* self, PyObject* Py_UNUSED(ignored)) { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); + auto type = &mcrfpydef::PyVectorType; auto result = (PyVectorObject*)type->tp_alloc(type, 0); if (result) { diff --git a/src/UIArc.cpp b/src/UIArc.cpp index ab12bde..e8a6add 100644 --- a/src/UIArc.cpp +++ b/src/UIArc.cpp @@ -391,10 +391,8 @@ bool UIArc::hasProperty(const std::string& name) const { // Python API implementation PyObject* UIArc::get_center(PyUIArcObject* self, void* closure) { auto center = self->data->getCenter(); - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!type) return NULL; + auto type = &mcrfpydef::PyVectorType; PyObject* result = PyObject_CallFunction((PyObject*)type, "ff", center.x, center.y); - Py_DECREF(type); return result; } @@ -450,11 +448,10 @@ int UIArc::set_end_angle(PyUIArcObject* self, PyObject* value, void* closure) { PyObject* UIArc::get_color(PyUIArcObject* self, void* closure) { auto color = self->data->getColor(); - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"); + auto type = &mcrfpydef::PyColorType; PyObject* args = Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a); PyObject* obj = PyObject_CallObject((PyObject*)type, args); Py_DECREF(args); - Py_DECREF(type); return obj; } @@ -634,11 +631,7 @@ int UIArc::init(PyUIArcObject* self, PyObject* args, PyObject* kwds) { } // #184: Check if this is a Python subclass (for callback method support) - PyObject* arc_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Arc"); - if (arc_type) { - self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != arc_type; - Py_DECREF(arc_type); - } + self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != (PyObject*)&mcrfpydef::PyUIArcType; return 0; } diff --git a/src/UICaption.cpp b/src/UICaption.cpp index f833fb5..b3d8690 100644 --- a/src/UICaption.cpp +++ b/src/UICaption.cpp @@ -255,7 +255,7 @@ int UICaption::set_color_member(PyUICaptionObject* self, PyObject* value, void* auto member_ptr = reinterpret_cast(closure); //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse int r, g, b, a; - if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color") /*(PyObject*)&mcrfpydef::PyColorType)*/)) + if (PyObject_IsInstance(value, (PyObject*)&mcrfpydef::PyColorType)) { // get value from mcrfpy.Color instance auto c = ((PyColorObject*)value)->data; @@ -479,7 +479,7 @@ int UICaption::init(PyUICaptionObject* self, PyObject* args, PyObject* kwds) // Handle font argument std::shared_ptr pyfont = nullptr; if (font && font != Py_None) { - if (!PyObject_IsInstance(font, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Font"))) { + if (!PyObject_IsInstance(font, (PyObject*)&mcrfpydef::PyFontType)) { PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance"); return -1; } @@ -571,11 +571,7 @@ int UICaption::init(PyUICaptionObject* self, PyObject* args, PyObject* kwds) } // #184: Check if this is a Python subclass (for callback method support) - PyObject* caption_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"); - if (caption_type) { - self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != caption_type; - Py_DECREF(caption_type); - } + self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != (PyObject*)&mcrfpydef::PyUICaptionType; return 0; } diff --git a/src/UICircle.cpp b/src/UICircle.cpp index b7a9550..c70f348 100644 --- a/src/UICircle.cpp +++ b/src/UICircle.cpp @@ -342,10 +342,8 @@ int UICircle::set_radius(PyUICircleObject* self, PyObject* value, void* closure) PyObject* UICircle::get_center(PyUICircleObject* self, void* closure) { sf::Vector2f center = self->data->getCenter(); - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!type) return NULL; + auto type = &mcrfpydef::PyVectorType; PyObject* result = PyObject_CallFunction((PyObject*)type, "ff", center.x, center.y); - Py_DECREF(type); return result; } @@ -362,16 +360,14 @@ int UICircle::set_center(PyUICircleObject* self, PyObject* value, void* closure) PyObject* UICircle::get_fill_color(PyUICircleObject* self, void* closure) { sf::Color c = self->data->getFillColor(); - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"); - if (!type) return NULL; + auto type = &mcrfpydef::PyColorType; PyObject* result = PyObject_CallFunction((PyObject*)type, "iiii", c.r, c.g, c.b, c.a); - Py_DECREF(type); return result; } int UICircle::set_fill_color(PyUICircleObject* self, PyObject* value, void* closure) { sf::Color color; - if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"))) { + if (PyObject_IsInstance(value, (PyObject*)&mcrfpydef::PyColorType)) { auto pyColor = (PyColorObject*)value; color = pyColor->data; } else if (PyTuple_Check(value)) { @@ -391,16 +387,14 @@ int UICircle::set_fill_color(PyUICircleObject* self, PyObject* value, void* clos PyObject* UICircle::get_outline_color(PyUICircleObject* self, void* closure) { sf::Color c = self->data->getOutlineColor(); - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"); - if (!type) return NULL; + auto type = &mcrfpydef::PyColorType; PyObject* result = PyObject_CallFunction((PyObject*)type, "iiii", c.r, c.g, c.b, c.a); - Py_DECREF(type); return result; } int UICircle::set_outline_color(PyUICircleObject* self, PyObject* value, void* closure) { sf::Color color; - if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"))) { + if (PyObject_IsInstance(value, (PyObject*)&mcrfpydef::PyColorType)) { auto pyColor = (PyColorObject*)value; color = pyColor->data; } else if (PyTuple_Check(value)) { @@ -527,7 +521,7 @@ int UICircle::init(PyUICircleObject* self, PyObject* args, PyObject* kwds) { // Set fill color if provided if (fill_color_obj && fill_color_obj != Py_None) { sf::Color color; - if (PyObject_IsInstance(fill_color_obj, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"))) { + if (PyObject_IsInstance(fill_color_obj, (PyObject*)&mcrfpydef::PyColorType)) { color = ((PyColorObject*)fill_color_obj)->data; } else if (PyTuple_Check(fill_color_obj)) { int r, g, b, a = 255; @@ -546,7 +540,7 @@ int UICircle::init(PyUICircleObject* self, PyObject* args, PyObject* kwds) { // Set outline color if provided if (outline_color_obj && outline_color_obj != Py_None) { sf::Color color; - if (PyObject_IsInstance(outline_color_obj, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"))) { + if (PyObject_IsInstance(outline_color_obj, (PyObject*)&mcrfpydef::PyColorType)) { color = ((PyColorObject*)outline_color_obj)->data; } else if (PyTuple_Check(outline_color_obj)) { int r, g, b, a = 255; @@ -596,11 +590,7 @@ int UICircle::init(PyUICircleObject* self, PyObject* args, PyObject* kwds) { } // #184: Check if this is a Python subclass (for callback method support) - PyObject* circle_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Circle"); - if (circle_type) { - self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != circle_type; - Py_DECREF(circle_type); - } + self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != (PyObject*)&mcrfpydef::PyUICircleType; return 0; } diff --git a/src/UICollection.cpp b/src/UICollection.cpp index 383646c..821ebf8 100644 --- a/src/UICollection.cpp +++ b/src/UICollection.cpp @@ -20,7 +20,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { if (!drawable) { Py_RETURN_NONE; } - + // Check cache first if (drawable->serial_number != 0) { PyObject* cached = PythonObjectCache::getInstance().lookup(drawable->serial_number); @@ -28,15 +28,14 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { return cached; // Already INCREF'd by lookup } } - + PyTypeObject* type = nullptr; PyObject* obj = nullptr; - + switch (drawable->derived_type()) { case PyObjectsEnum::UIFRAME: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"); - if (!type) return nullptr; + type = &PyUIFrameType; auto pyObj = (PyUIFrameObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(drawable); @@ -47,8 +46,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { } case PyObjectsEnum::UICAPTION: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"); - if (!type) return nullptr; + type = &PyUICaptionType; auto pyObj = (PyUICaptionObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(drawable); @@ -60,8 +58,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { } case PyObjectsEnum::UISPRITE: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"); - if (!type) return nullptr; + type = &PyUISpriteType; auto pyObj = (PyUISpriteObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(drawable); @@ -72,8 +69,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { } case PyObjectsEnum::UIGRID: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"); - if (!type) return nullptr; + type = &PyUIGridType; auto pyObj = (PyUIGridObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(drawable); @@ -84,8 +80,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { } case PyObjectsEnum::UILINE: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Line"); - if (!type) return nullptr; + type = &PyUILineType; auto pyObj = (PyUILineObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(drawable); @@ -96,8 +91,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { } case PyObjectsEnum::UICIRCLE: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Circle"); - if (!type) return nullptr; + type = &PyUICircleType; auto pyObj = (PyUICircleObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(drawable); @@ -108,8 +102,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { } case PyObjectsEnum::UIARC: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Arc"); - if (!type) return nullptr; + type = &PyUIArcType; auto pyObj = (PyUIArcObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(drawable); @@ -120,8 +113,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { } case PyObjectsEnum::UIVIEWPORT3D: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Viewport3D"); - if (!type) return nullptr; + type = &PyViewport3DType; auto pyObj = (PyViewport3DObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(drawable); @@ -134,10 +126,6 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { PyErr_SetString(PyExc_TypeError, "Unknown UIDrawable derived type"); return nullptr; } - - if (type) { - Py_DECREF(type); - } // Re-register in cache if the object has a serial number // This handles the case where the original Python wrapper was GC'd @@ -153,6 +141,28 @@ static PyObject* convertDrawableToPython(std::shared_ptr drawable) { return obj; } +// Helper to extract shared_ptr from any UIDrawable Python subclass. +// Returns nullptr without setting an error if the type doesn't match. +static std::shared_ptr extractDrawable(PyObject* o) { + if (PyObject_IsInstance(o, (PyObject*)&PyUIFrameType)) + return ((PyUIFrameObject*)o)->data; + if (PyObject_IsInstance(o, (PyObject*)&PyUICaptionType)) + return ((PyUICaptionObject*)o)->data; + if (PyObject_IsInstance(o, (PyObject*)&PyUISpriteType)) + return ((PyUISpriteObject*)o)->data; + if (PyObject_IsInstance(o, (PyObject*)&PyUIGridType)) + return ((PyUIGridObject*)o)->data; + if (PyObject_IsInstance(o, (PyObject*)&PyUILineType)) + return ((PyUILineObject*)o)->data; + if (PyObject_IsInstance(o, (PyObject*)&PyUICircleType)) + return ((PyUICircleObject*)o)->data; + if (PyObject_IsInstance(o, (PyObject*)&PyUIArcType)) + return ((PyUIArcObject*)o)->data; + if (PyObject_IsInstance(o, (PyObject*)&PyViewport3DType)) + return ((PyViewport3DObject*)o)->data; + return nullptr; +} + int UICollectionIter::init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds) { PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); @@ -250,37 +260,13 @@ int UICollection::setitem(PyUICollectionObject* self, Py_ssize_t index, PyObject return 0; } - // Type checking - must be a UIDrawable subclass - if (!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) && - !PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) && - !PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) && - !PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) { - PyErr_SetString(PyExc_TypeError, "UICollection can only contain Frame, Caption, Sprite, and Grid objects"); - return -1; - } - - // Get the C++ object from the Python object - std::shared_ptr new_drawable = nullptr; - int old_z_index = (*vec)[index]->z_index; // Preserve the z_index - - if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) { - PyUIFrameObject* frame = (PyUIFrameObject*)value; - new_drawable = frame->data; - } else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) { - PyUICaptionObject* caption = (PyUICaptionObject*)value; - new_drawable = caption->data; - } else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) { - PyUISpriteObject* sprite = (PyUISpriteObject*)value; - new_drawable = sprite->data; - } else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) { - PyUIGridObject* grid = (PyUIGridObject*)value; - new_drawable = grid->data; - } - + // Extract drawable from Python object (type-checks internally) + std::shared_ptr new_drawable = extractDrawable(value); if (!new_drawable) { - PyErr_SetString(PyExc_RuntimeError, "Failed to extract C++ object from Python object"); + PyErr_SetString(PyExc_TypeError, "UICollection can only contain Drawable objects"); return -1; } + int old_z_index = (*vec)[index]->z_index; // Preserve the z_index // #122: Clear parent of old element (*vec)[index]->setParent(nullptr); @@ -312,32 +298,8 @@ int UICollection::contains(PyUICollectionObject* self, PyObject* value) { return -1; } - // Type checking - must be a UIDrawable subclass - if (!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) && - !PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) && - !PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) && - !PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) { - // Not a valid type, so it can't be in the collection - return 0; - } - - // Get the C++ object from the Python object - std::shared_ptr search_drawable = nullptr; - - if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) { - PyUIFrameObject* frame = (PyUIFrameObject*)value; - search_drawable = frame->data; - } else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) { - PyUICaptionObject* caption = (PyUICaptionObject*)value; - search_drawable = caption->data; - } else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) { - PyUISpriteObject* sprite = (PyUISpriteObject*)value; - search_drawable = sprite->data; - } else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) { - PyUIGridObject* grid = (PyUIGridObject*)value; - search_drawable = grid->data; - } - + // Extract drawable (returns nullptr for non-drawable types) + std::shared_ptr search_drawable = extractDrawable(value); if (!search_drawable) { return 0; } @@ -412,14 +374,11 @@ PyObject* UICollection::inplace_concat(PyUICollectionObject* self, PyObject* oth return NULL; } - // Type check - if (!PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) && - !PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) && - !PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) && - !PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) { + // Type check - must be a UIDrawable subclass + if (!PyObject_IsInstance(item, (PyObject*)&PyDrawableType)) { Py_DECREF(item); - PyErr_Format(PyExc_TypeError, - "UICollection can only contain Frame, Caption, Sprite, and Grid objects; " + PyErr_Format(PyExc_TypeError, + "UICollection can only contain Drawable objects; " "got %s at index %zd", Py_TYPE(item)->tp_name, i); return NULL; } @@ -544,20 +503,11 @@ int UICollection::ass_subscript(PyUICollectionObject* self, PyObject* key, PyObj } // Type check and extract C++ object - std::shared_ptr drawable = nullptr; - - if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) { - drawable = ((PyUIFrameObject*)item)->data; - } else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) { - drawable = ((PyUICaptionObject*)item)->data; - } else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) { - drawable = ((PyUISpriteObject*)item)->data; - } else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) { - drawable = ((PyUIGridObject*)item)->data; - } else { + std::shared_ptr drawable = extractDrawable(item); + if (!drawable) { Py_DECREF(item); - PyErr_Format(PyExc_TypeError, - "UICollection can only contain Frame, Caption, Sprite, and Grid objects; " + PyErr_Format(PyExc_TypeError, + "UICollection can only contain Drawable objects; " "got %s at index %zd", Py_TYPE(item)->tp_name, i); return -1; } @@ -629,45 +579,22 @@ PySequenceMethods UICollection::sqmethods = { .sq_inplace_repeat = NULL }; -/* Idiomatic way to fetch complete types from the API rather than referencing their PyTypeObject struct - -auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"); - -I never identified why `using namespace mcrfpydef;` doesn't solve the segfault issue. -The horrible macro in UIDrawable was originally a workaround for this, but as I interact with the types outside of the monster UI.h, a more general (and less icky) solution is required. - -*/ PyObject* UICollection::append(PyUICollectionObject* self, PyObject* o) { // if not UIDrawable subclass, reject it // self->data->push_back( c++ object inside o ); - // Ensure module is initialized - if (!McRFPy_API::mcrf_module) { - PyErr_SetString(PyExc_RuntimeError, "mcrfpy module not initialized"); - return NULL; - } - - // this would be a great use case for .tp_base - if (!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) && - !PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) && - !PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) && - !PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid")) && - !PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Line")) && - !PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Circle")) && - !PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Arc")) && - !PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Viewport3D")) - ) - { - PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, Grid, Line, Circle, Arc, and Viewport3D objects can be added to UICollection"); + // Extract drawable from Python object + std::shared_ptr drawable = extractDrawable(o); + if (!drawable) { + PyErr_SetString(PyExc_TypeError, "Only Drawable objects can be added to UICollection"); return NULL; } // Calculate z_index for the new element int new_z_index = 0; if (!self->data->empty()) { - // Get the z_index of the last element and add 10 int last_z = self->data->back()->z_index; if (last_z <= INT_MAX - 10) { new_z_index = last_z + 10; @@ -676,60 +603,20 @@ PyObject* UICollection::append(PyUICollectionObject* self, PyObject* o) } } - // #122: Get the owner as parent for this drawable - std::shared_ptr owner_ptr = self->owner.lock(); + // #183: Remove from old parent (drawable or scene) + drawable->removeFromParent(); - // Helper lambda to add drawable with parent tracking - auto addDrawable = [&](std::shared_ptr drawable) { - // #183: Remove from old parent (drawable or scene) - drawable->removeFromParent(); + drawable->z_index = new_z_index; - drawable->z_index = new_z_index; + // #183: Set new parent - either scene or drawable + if (!self->scene_name.empty()) { + drawable->setParentScene(self->scene_name); + } else { + std::shared_ptr owner_ptr = self->owner.lock(); + drawable->setParent(owner_ptr); + } - // #183: Set new parent - either scene or drawable - if (!self->scene_name.empty()) { - // This is a scene's children collection - drawable->setParentScene(self->scene_name); - } else { - // This is a Frame/Grid's children collection - drawable->setParent(owner_ptr); - } - - self->data->push_back(drawable); - }; - - if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) - { - addDrawable(((PyUIFrameObject*)o)->data); - } - if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) - { - addDrawable(((PyUICaptionObject*)o)->data); - } - if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) - { - addDrawable(((PyUISpriteObject*)o)->data); - } - if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) - { - addDrawable(((PyUIGridObject*)o)->data); - } - if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Line"))) - { - addDrawable(((PyUILineObject*)o)->data); - } - if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Circle"))) - { - addDrawable(((PyUICircleObject*)o)->data); - } - if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Arc"))) - { - addDrawable(((PyUIArcObject*)o)->data); - } - if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Viewport3D"))) - { - addDrawable(((PyViewport3DObject*)o)->data); - } + self->data->push_back(drawable); // Mark scene as needing resort after adding element McRFPy_API::markSceneNeedsSort(); @@ -747,83 +634,34 @@ PyObject* UICollection::extend(PyUICollectionObject* self, PyObject* iterable) return NULL; } - // Ensure module is initialized - if (!McRFPy_API::mcrf_module) { - Py_DECREF(iterator); - PyErr_SetString(PyExc_RuntimeError, "mcrfpy module not initialized"); - return NULL; - } - // Get current highest z_index int current_z_index = 0; if (!self->data->empty()) { current_z_index = self->data->back()->z_index; } - + + std::shared_ptr owner_ptr = self->owner.lock(); + PyObject* item; while ((item = PyIter_Next(iterator)) != NULL) { - // Check if item is a UIDrawable subclass - if (!PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) && - !PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) && - !PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) && - !PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid")) && - !PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Line")) && - !PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Circle")) && - !PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Arc")) && - !PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Viewport3D"))) - { + std::shared_ptr drawable = extractDrawable(item); + if (!drawable) { Py_DECREF(item); Py_DECREF(iterator); - PyErr_SetString(PyExc_TypeError, "All items must be Frame, Caption, Sprite, Grid, Line, Circle, Arc, or Viewport3D objects"); + PyErr_SetString(PyExc_TypeError, "All items must be Drawable objects"); return NULL; } - - // Increment z_index for each new element + if (current_z_index <= INT_MAX - 10) { current_z_index += 10; } else { current_z_index = INT_MAX; } - - // #122: Get the owner as parent for this drawable - std::shared_ptr owner_ptr = self->owner.lock(); - // Helper lambda to add drawable with parent tracking - auto addDrawable = [&](std::shared_ptr drawable) { - // #122: Remove from old parent if it has one - if (auto old_parent = drawable->getParent()) { - drawable->removeFromParent(); - } - drawable->z_index = current_z_index; - drawable->setParent(owner_ptr); - self->data->push_back(drawable); - }; - - // Add the item based on its type - if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) { - addDrawable(((PyUIFrameObject*)item)->data); - } - else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) { - addDrawable(((PyUICaptionObject*)item)->data); - } - else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) { - addDrawable(((PyUISpriteObject*)item)->data); - } - else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) { - addDrawable(((PyUIGridObject*)item)->data); - } - else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Line"))) { - addDrawable(((PyUILineObject*)item)->data); - } - else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Circle"))) { - addDrawable(((PyUICircleObject*)item)->data); - } - else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Arc"))) { - addDrawable(((PyUIArcObject*)item)->data); - } - else if (PyObject_IsInstance(item, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Viewport3D"))) { - addDrawable(((PyViewport3DObject*)item)->data); - } + drawable->removeFromParent(); + drawable->z_index = current_z_index; + drawable->setParent(owner_ptr); + self->data->push_back(drawable); Py_DECREF(item); } @@ -850,29 +688,11 @@ PyObject* UICollection::remove(PyUICollectionObject* self, PyObject* o) return NULL; } - // Type checking - must be a UIDrawable subclass - if (!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Drawable"))) { - PyErr_SetString(PyExc_TypeError, - "UICollection.remove requires a UI element (Frame, Caption, Sprite, Grid)"); - return NULL; - } - - // Get the C++ object from the Python object - std::shared_ptr search_drawable = nullptr; - - if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) { - search_drawable = ((PyUIFrameObject*)o)->data; - } else if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) { - search_drawable = ((PyUICaptionObject*)o)->data; - } else if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) { - search_drawable = ((PyUISpriteObject*)o)->data; - } else if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) { - search_drawable = ((PyUIGridObject*)o)->data; - } - + // Extract drawable from Python object + std::shared_ptr search_drawable = extractDrawable(o); if (!search_drawable) { PyErr_SetString(PyExc_TypeError, - "UICollection.remove requires a UI element (Frame, Caption, Sprite, Grid)"); + "UICollection.remove requires a Drawable object"); return NULL; } @@ -951,29 +771,11 @@ PyObject* UICollection::insert(PyUICollectionObject* self, PyObject* args) return NULL; } - // Type checking - must be a UIDrawable subclass - if (!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Drawable"))) { - PyErr_SetString(PyExc_TypeError, - "UICollection.insert requires a UI element (Frame, Caption, Sprite, Grid)"); - return NULL; - } - - // Get the C++ object from the Python object - std::shared_ptr drawable = nullptr; - - if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) { - drawable = ((PyUIFrameObject*)o)->data; - } else if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) { - drawable = ((PyUICaptionObject*)o)->data; - } else if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) { - drawable = ((PyUISpriteObject*)o)->data; - } else if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) { - drawable = ((PyUIGridObject*)o)->data; - } - + // Extract drawable from Python object + std::shared_ptr drawable = extractDrawable(o); if (!drawable) { PyErr_SetString(PyExc_TypeError, - "UICollection.insert requires a UI element (Frame, Caption, Sprite, Grid)"); + "UICollection.insert requires a Drawable object"); return NULL; } @@ -1011,30 +813,10 @@ PyObject* UICollection::index_method(PyUICollectionObject* self, PyObject* value return NULL; } - // Type checking - must be a UIDrawable subclass - if (!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) && - !PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) && - !PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) && - !PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) { - PyErr_SetString(PyExc_TypeError, "UICollection.index requires a Frame, Caption, Sprite, or Grid object"); - return NULL; - } - - // Get the C++ object from the Python object - std::shared_ptr search_drawable = nullptr; - - if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) { - search_drawable = ((PyUIFrameObject*)value)->data; - } else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) { - search_drawable = ((PyUICaptionObject*)value)->data; - } else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) { - search_drawable = ((PyUISpriteObject*)value)->data; - } else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) { - search_drawable = ((PyUIGridObject*)value)->data; - } - + // Extract drawable from Python object + std::shared_ptr search_drawable = extractDrawable(value); if (!search_drawable) { - PyErr_SetString(PyExc_RuntimeError, "Failed to extract C++ object from Python object"); + PyErr_SetString(PyExc_TypeError, "UICollection.index requires a Drawable object"); return NULL; } @@ -1056,28 +838,8 @@ PyObject* UICollection::count(PyUICollectionObject* self, PyObject* value) { return NULL; } - // Type checking - must be a UIDrawable subclass - if (!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) && - !PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) && - !PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) && - !PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) { - // Not a valid type, so count is 0 - return PyLong_FromLong(0); - } - - // Get the C++ object from the Python object - std::shared_ptr search_drawable = nullptr; - - if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) { - search_drawable = ((PyUIFrameObject*)value)->data; - } else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) { - search_drawable = ((PyUICaptionObject*)value)->data; - } else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) { - search_drawable = ((PyUISpriteObject*)value)->data; - } else if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) { - search_drawable = ((PyUIGridObject*)value)->data; - } - + // Extract drawable (returns nullptr for non-drawable types) + std::shared_ptr search_drawable = extractDrawable(value); if (!search_drawable) { return PyLong_FromLong(0); } diff --git a/src/UIDrawable.cpp b/src/UIDrawable.cpp index 5219965..06573f9 100644 --- a/src/UIDrawable.cpp +++ b/src/UIDrawable.cpp @@ -756,12 +756,10 @@ PyObject* UIDrawable::get_grid_pos(PyObject* self, void* closure) { float grid_y = drawable->position.y / cell_size.y; // Return as Vector - PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!vector_type) return NULL; + PyObject* vector_type = (PyObject*)&mcrfpydef::PyVectorType; PyObject* args = Py_BuildValue("(ff)", grid_x, grid_y); PyObject* result = PyObject_CallObject(vector_type, args); - Py_DECREF(vector_type); Py_DECREF(args); return result; @@ -868,12 +866,10 @@ PyObject* UIDrawable::get_grid_size(PyObject* self, void* closure) { float grid_h = bounds.height / cell_size.y; // Return as Vector - PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!vector_type) return NULL; + PyObject* vector_type = (PyObject*)&mcrfpydef::PyVectorType; PyObject* args = Py_BuildValue("(ff)", grid_w, grid_h); PyObject* result = PyObject_CallObject(vector_type, args); - Py_DECREF(vector_type); Py_DECREF(args); return result; @@ -1308,8 +1304,7 @@ PyObject* UIDrawable::get_parent(PyObject* self, void* closure) { switch (parent_ptr->derived_type()) { case PyObjectsEnum::UIFRAME: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"); - if (!type) return nullptr; + type = &mcrfpydef::PyUIFrameType; auto pyObj = (PyUIFrameObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(parent_ptr); @@ -1320,8 +1315,7 @@ PyObject* UIDrawable::get_parent(PyObject* self, void* closure) { } case PyObjectsEnum::UICAPTION: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"); - if (!type) return nullptr; + type = &mcrfpydef::PyUICaptionType; auto pyObj = (PyUICaptionObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(parent_ptr); @@ -1333,8 +1327,7 @@ PyObject* UIDrawable::get_parent(PyObject* self, void* closure) { } case PyObjectsEnum::UISPRITE: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"); - if (!type) return nullptr; + type = &mcrfpydef::PyUISpriteType; auto pyObj = (PyUISpriteObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(parent_ptr); @@ -1345,8 +1338,7 @@ PyObject* UIDrawable::get_parent(PyObject* self, void* closure) { } case PyObjectsEnum::UIGRID: { - type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"); - if (!type) return nullptr; + type = &mcrfpydef::PyUIGridType; auto pyObj = (PyUIGridObject*)type->tp_alloc(type, 0); if (pyObj) { pyObj->data = std::static_pointer_cast(parent_ptr); @@ -1359,9 +1351,6 @@ PyObject* UIDrawable::get_parent(PyObject* self, void* closure) { Py_RETURN_NONE; } - if (type) { - Py_DECREF(type); - } return obj; } @@ -1405,17 +1394,9 @@ int UIDrawable::set_parent(PyObject* self, PyObject* value, void* closure) { } // Value must be a Frame, Grid, or Scene (things with children collections) - PyTypeObject* frame_type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"); - PyTypeObject* grid_type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"); - PyTypeObject* scene_type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Scene"); - - bool is_frame = frame_type && PyObject_IsInstance(value, (PyObject*)frame_type); - bool is_grid = grid_type && PyObject_IsInstance(value, (PyObject*)grid_type); - bool is_scene = scene_type && PyObject_IsInstance(value, (PyObject*)scene_type); - - Py_XDECREF(frame_type); - Py_XDECREF(grid_type); - Py_XDECREF(scene_type); + bool is_frame = PyObject_IsInstance(value, (PyObject*)&mcrfpydef::PyUIFrameType); + bool is_grid = PyObject_IsInstance(value, (PyObject*)&mcrfpydef::PyUIGridType); + bool is_scene = PyObject_IsInstance(value, (PyObject*)&mcrfpydef::PySceneType); if (!is_frame && !is_grid && !is_scene) { PyErr_SetString(PyExc_TypeError, "parent must be a Frame, Grid, Scene, or None"); @@ -1519,16 +1500,14 @@ PyObject* UIDrawable::get_bounds_py(PyObject* self, void* closure) { sf::FloatRect bounds = drawable->get_bounds(); - // Get Vector type from mcrfpy module - PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!vector_type) return NULL; + // Get Vector type + PyObject* vector_type = (PyObject*)&mcrfpydef::PyVectorType; // Create pos vector PyObject* pos_args = Py_BuildValue("(ff)", bounds.left, bounds.top); PyObject* pos = PyObject_CallObject(vector_type, pos_args); Py_DECREF(pos_args); if (!pos) { - Py_DECREF(vector_type); return NULL; } @@ -1536,7 +1515,6 @@ PyObject* UIDrawable::get_bounds_py(PyObject* self, void* closure) { PyObject* size_args = Py_BuildValue("(ff)", bounds.width, bounds.height); PyObject* size = PyObject_CallObject(vector_type, size_args); Py_DECREF(size_args); - Py_DECREF(vector_type); if (!size) { Py_DECREF(pos); return NULL; @@ -1554,16 +1532,14 @@ PyObject* UIDrawable::get_global_bounds_py(PyObject* self, void* closure) { sf::FloatRect bounds = drawable->get_global_bounds(); - // Get Vector type from mcrfpy module - PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!vector_type) return NULL; + // Get Vector type + PyObject* vector_type = (PyObject*)&mcrfpydef::PyVectorType; // Create pos vector PyObject* pos_args = Py_BuildValue("(ff)", bounds.left, bounds.top); PyObject* pos = PyObject_CallObject(vector_type, pos_args); Py_DECREF(pos_args); if (!pos) { - Py_DECREF(vector_type); return NULL; } @@ -1571,7 +1547,6 @@ PyObject* UIDrawable::get_global_bounds_py(PyObject* self, void* closure) { PyObject* size_args = Py_BuildValue("(ff)", bounds.width, bounds.height); PyObject* size = PyObject_CallObject(vector_type, size_args); Py_DECREF(size_args); - Py_DECREF(vector_type); if (!size) { Py_DECREF(pos); return NULL; @@ -1976,14 +1951,9 @@ PyObject* UIDrawable_animate_impl(std::shared_ptr self, PyObject* ar } // Create and return a PyAnimation wrapper - PyTypeObject* animType = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Animation"); - if (!animType) { - PyErr_SetString(PyExc_RuntimeError, "Could not find Animation type"); - return NULL; - } + PyTypeObject* animType = &mcrfpydef::PyAnimationType; PyAnimationObject* pyAnim = (PyAnimationObject*)animType->tp_alloc(animType, 0); - Py_DECREF(animType); if (!pyAnim) { return NULL; @@ -2182,29 +2152,13 @@ PyObject* UIDrawable::py_realign(PyObject* self, PyObject* args) { PyObjectsEnum objtype = PyObjectsEnum::UIFRAME; // Default, will be set by type check // Determine the type from the Python object - PyObject* frame_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"); - PyObject* caption_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"); - PyObject* sprite_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"); - PyObject* grid_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"); - PyObject* line_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Line"); - PyObject* circle_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Circle"); - PyObject* arc_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Arc"); - - if (PyObject_IsInstance(self, frame_type)) objtype = PyObjectsEnum::UIFRAME; - else if (PyObject_IsInstance(self, caption_type)) objtype = PyObjectsEnum::UICAPTION; - else if (PyObject_IsInstance(self, sprite_type)) objtype = PyObjectsEnum::UISPRITE; - else if (PyObject_IsInstance(self, grid_type)) objtype = PyObjectsEnum::UIGRID; - else if (PyObject_IsInstance(self, line_type)) objtype = PyObjectsEnum::UILINE; - else if (PyObject_IsInstance(self, circle_type)) objtype = PyObjectsEnum::UICIRCLE; - else if (PyObject_IsInstance(self, arc_type)) objtype = PyObjectsEnum::UIARC; - - Py_XDECREF(frame_type); - Py_XDECREF(caption_type); - Py_XDECREF(sprite_type); - Py_XDECREF(grid_type); - Py_XDECREF(line_type); - Py_XDECREF(circle_type); - Py_XDECREF(arc_type); + if (PyObject_IsInstance(self, (PyObject*)&mcrfpydef::PyUIFrameType)) objtype = PyObjectsEnum::UIFRAME; + else if (PyObject_IsInstance(self, (PyObject*)&mcrfpydef::PyUICaptionType)) objtype = PyObjectsEnum::UICAPTION; + else if (PyObject_IsInstance(self, (PyObject*)&mcrfpydef::PyUISpriteType)) objtype = PyObjectsEnum::UISPRITE; + else if (PyObject_IsInstance(self, (PyObject*)&mcrfpydef::PyUIGridType)) objtype = PyObjectsEnum::UIGRID; + else if (PyObject_IsInstance(self, (PyObject*)&mcrfpydef::PyUILineType)) objtype = PyObjectsEnum::UILINE; + else if (PyObject_IsInstance(self, (PyObject*)&mcrfpydef::PyUICircleType)) objtype = PyObjectsEnum::UICIRCLE; + else if (PyObject_IsInstance(self, (PyObject*)&mcrfpydef::PyUIArcType)) objtype = PyObjectsEnum::UIARC; UIDrawable* drawable = extractDrawable(self, objtype); if (!drawable) return NULL; diff --git a/src/UIEntity.cpp b/src/UIEntity.cpp index 55bb3ac..7a3a774 100644 --- a/src/UIEntity.cpp +++ b/src/UIEntity.cpp @@ -208,7 +208,7 @@ int UIEntity::init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) { // Handle texture argument std::shared_ptr texture_ptr = nullptr; if (texture && texture != Py_None) { - if (!PyObject_IsInstance(texture, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))) { + if (!PyObject_IsInstance(texture, (PyObject*)&mcrfpydef::PyTextureType)) { PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance or None"); return -1; } @@ -220,7 +220,7 @@ int UIEntity::init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) { } // Handle grid argument - if (grid_obj && !PyObject_IsInstance(grid_obj, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) { + if (grid_obj && !PyObject_IsInstance(grid_obj, (PyObject*)&mcrfpydef::PyUIGridType)) { PyErr_SetString(PyExc_TypeError, "grid must be a mcrfpy.Grid instance"); return -1; } @@ -292,7 +292,7 @@ PyObject* UIEntity::get_spritenumber(PyUIEntityObject* self, void* closure) { } PyObject* sfVector2f_to_PyObject(sf::Vector2f vec) { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); + auto type = &mcrfpydef::PyVectorType; auto obj = (PyVectorObject*)type->tp_alloc(type, 0); if (obj) { obj->data = vec; @@ -301,7 +301,7 @@ PyObject* sfVector2f_to_PyObject(sf::Vector2f vec) { } PyObject* sfVector2i_to_PyObject(sf::Vector2i vec) { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); + auto type = &mcrfpydef::PyVectorType; auto obj = (PyVectorObject*)type->tp_alloc(type, 0); if (obj) { obj->data = sf::Vector2f(static_cast(vec.x), static_cast(vec.y)); @@ -636,11 +636,9 @@ PyObject* UIEntity::get_grid(PyUIEntityObject* self, void* closure) } // 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 grid_type = &mcrfpydef::PyUIGridType; auto pyGrid = (PyUIGridObject*)grid_type->tp_alloc(grid_type, 0); - Py_DECREF(grid_type); if (pyGrid) { pyGrid->data = self->data->grid; @@ -885,11 +883,7 @@ PyObject* UIEntity::visible_entities(PyUIEntityObject* self, PyObject* args, PyO if (!result) return PyErr_NoMemory(); // Get Entity type for creating Python objects - PyTypeObject* entity_type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity"); - if (!entity_type) { - Py_DECREF(result); - return NULL; - } + auto entity_type = &mcrfpydef::PyUIEntityType; // Iterate through all entities in the grid if (grid->entities) { @@ -908,7 +902,6 @@ PyObject* UIEntity::visible_entities(PyUIEntityObject* self, PyObject* args, PyO auto pyEntity = (PyUIEntityObject*)entity_type->tp_alloc(entity_type, 0); if (!pyEntity) { Py_DECREF(result); - Py_DECREF(entity_type); return PyErr_NoMemory(); } @@ -918,7 +911,6 @@ PyObject* UIEntity::visible_entities(PyUIEntityObject* self, PyObject* args, PyO if (PyList_Append(result, (PyObject*)pyEntity) < 0) { Py_DECREF(pyEntity); Py_DECREF(result); - Py_DECREF(entity_type); return NULL; } Py_DECREF(pyEntity); // List now owns the reference @@ -926,7 +918,6 @@ PyObject* UIEntity::visible_entities(PyUIEntityObject* self, PyObject* args, PyO } } - Py_DECREF(entity_type); return result; } @@ -1308,14 +1299,9 @@ PyObject* UIEntity::animate(PyUIEntityObject* self, PyObject* args, PyObject* kw } // Create and return a PyAnimation wrapper - PyTypeObject* animType = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Animation"); - if (!animType) { - PyErr_SetString(PyExc_RuntimeError, "Could not find Animation type"); - return NULL; - } + auto animType = &mcrfpydef::PyAnimationType; PyAnimationObject* pyAnim = (PyAnimationObject*)animType->tp_alloc(animType, 0); - Py_DECREF(animType); if (!pyAnim) { return NULL; diff --git a/src/UIFrame.cpp b/src/UIFrame.cpp index ca8bb36..bb25547 100644 --- a/src/UIFrame.cpp +++ b/src/UIFrame.cpp @@ -350,15 +350,7 @@ PyObject* UIFrame::get_color_member(PyUIFrameObject* self, void* closure) PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); return nullptr; } - //PyTypeObject* colorType = &PyColorType; - auto colorType = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"); - PyObject* pyColor = colorType->tp_alloc(colorType, 0); - if (pyColor == NULL) - { - std::cout << "failure to allocate mcrfpy.Color / PyColorType" << std::endl; - return NULL; - } - PyColorObject* pyColorObj = reinterpret_cast(pyColor); + auto colorType = &mcrfpydef::PyColorType; // fetch correct member data sf::Color color; @@ -381,7 +373,7 @@ int UIFrame::set_color_member(PyUIFrameObject* self, PyObject* value, void* clos //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse auto member_ptr = reinterpret_cast(closure); int r, g, b, a; - if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"))) + if (PyObject_IsInstance(value, (PyObject*)&mcrfpydef::PyColorType)) { sf::Color c = ((PyColorObject*)value)->data; r = c.r; g = c.g; b = c.b; a = c.a; @@ -432,7 +424,7 @@ int UIFrame::set_color_member(PyUIFrameObject* self, PyObject* value, void* clos PyObject* UIFrame::get_pos(PyUIFrameObject* self, void* closure) { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); + auto type = &mcrfpydef::PyVectorType; auto obj = (PyVectorObject*)type->tp_alloc(type, 0); if (obj) { auto pos = self->data->box.getPosition(); @@ -750,38 +742,27 @@ int UIFrame::init(PyUIFrameObject* self, PyObject* args, PyObject* kwds) if (!child) return -1; // Check if it's a UIDrawable (Frame, Caption, Sprite, or Grid) - PyObject* frame_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"); - PyObject* caption_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"); - PyObject* sprite_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"); - PyObject* grid_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"); - - if (!PyObject_IsInstance(child, frame_type) && - !PyObject_IsInstance(child, caption_type) && - !PyObject_IsInstance(child, sprite_type) && - !PyObject_IsInstance(child, grid_type)) { + if (!PyObject_IsInstance(child, (PyObject*)&mcrfpydef::PyUIFrameType) && + !PyObject_IsInstance(child, (PyObject*)&mcrfpydef::PyUICaptionType) && + !PyObject_IsInstance(child, (PyObject*)&mcrfpydef::PyUISpriteType) && + !PyObject_IsInstance(child, (PyObject*)&mcrfpydef::PyUIGridType)) { Py_DECREF(child); PyErr_SetString(PyExc_TypeError, "children must contain only Frame, Caption, Sprite, or Grid objects"); return -1; } - + // Get the shared_ptr and add to children std::shared_ptr drawable = nullptr; - if (PyObject_IsInstance(child, frame_type)) { + if (PyObject_IsInstance(child, (PyObject*)&mcrfpydef::PyUIFrameType)) { drawable = ((PyUIFrameObject*)child)->data; - } else if (PyObject_IsInstance(child, caption_type)) { + } else if (PyObject_IsInstance(child, (PyObject*)&mcrfpydef::PyUICaptionType)) { drawable = ((PyUICaptionObject*)child)->data; - } else if (PyObject_IsInstance(child, sprite_type)) { + } else if (PyObject_IsInstance(child, (PyObject*)&mcrfpydef::PyUISpriteType)) { drawable = ((PyUISpriteObject*)child)->data; - } else if (PyObject_IsInstance(child, grid_type)) { + } else if (PyObject_IsInstance(child, (PyObject*)&mcrfpydef::PyUIGridType)) { drawable = ((PyUIGridObject*)child)->data; } - // Clean up type references - Py_DECREF(frame_type); - Py_DECREF(caption_type); - Py_DECREF(sprite_type); - Py_DECREF(grid_type); - if (drawable) { drawable->setParent(self->data); // Set parent before adding (enables alignment) self->data->children->push_back(drawable); @@ -812,11 +793,7 @@ int UIFrame::init(PyUIFrameObject* self, PyObject* args, PyObject* kwds) } // #184: Check if this is a Python subclass (for callback method support) - PyObject* frame_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"); - if (frame_type) { - self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != frame_type; - Py_DECREF(frame_type); - } + self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != (PyObject*)&mcrfpydef::PyUIFrameType; return 0; } diff --git a/src/UIGrid.cpp b/src/UIGrid.cpp index 5756589..3bd41ab 100644 --- a/src/UIGrid.cpp +++ b/src/UIGrid.cpp @@ -873,7 +873,7 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) { // Handle texture argument std::shared_ptr texture_ptr = nullptr; if (textureObj && textureObj != Py_None) { - if (!PyObject_IsInstance(textureObj, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))) { + if (!PyObject_IsInstance(textureObj, (PyObject*)&mcrfpydef::PyTextureType)) { PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance or None"); return -1; } @@ -1139,11 +1139,7 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) { } // #184: Check if this is a Python subclass (for callback method support) - PyObject* grid_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"); - if (grid_type) { - self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != grid_type; - Py_DECREF(grid_type); - } + self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != (PyObject*)&mcrfpydef::PyUIGridType; return 0; // Success } @@ -1328,7 +1324,7 @@ PyObject* UIGrid::get_texture(PyUIGridObject* self, void* closure) { Py_RETURN_NONE; } - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"); + auto type = &mcrfpydef::PyTextureType; auto obj = (PyTextureObject*)type->tp_alloc(type, 0); obj->data = texture; return (PyObject*)obj; @@ -1415,17 +1411,16 @@ PyMappingMethods UIGrid::mpmethods = { PyObject* UIGrid::get_fill_color(PyUIGridObject* self, void* closure) { auto& color = self->data->fill_color; - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"); + auto type = &mcrfpydef::PyColorType; PyObject* args = Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a); PyObject* obj = PyObject_CallObject((PyObject*)type, args); Py_DECREF(args); - Py_DECREF(type); return obj; } int UIGrid::set_fill_color(PyUIGridObject* self, PyObject* value, void* closure) { - if (!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"))) { + if (!PyObject_IsInstance(value, (PyObject*)&mcrfpydef::PyColorType)) { PyErr_SetString(PyExc_TypeError, "fill_color must be a Color object"); return -1; } @@ -1454,15 +1449,13 @@ PyObject* UIGrid::get_perspective(PyUIGridObject* self, void* closure) } // Otherwise, create a new base Entity object - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity"); + auto type = &mcrfpydef::PyUIEntityType; auto o = (PyUIEntityObject*)type->tp_alloc(type, 0); if (o) { o->data = locked; o->weakreflist = NULL; - Py_DECREF(type); return (PyObject*)o; } - Py_XDECREF(type); } Py_RETURN_NONE; } @@ -1476,19 +1469,10 @@ int UIGrid::set_perspective(PyUIGridObject* self, PyObject* value, void* closure } // Extract UIEntity from PyObject - // Get the Entity type from the module - auto entity_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity"); - if (!entity_type) { - PyErr_SetString(PyExc_RuntimeError, "Could not get Entity type from mcrfpy module"); - return -1; - } - - if (!PyObject_IsInstance(value, entity_type)) { - Py_DECREF(entity_type); + if (!PyObject_IsInstance(value, (PyObject*)&mcrfpydef::PyUIEntityType)) { PyErr_SetString(PyExc_TypeError, "perspective must be a UIEntity or None"); return -1; } - Py_DECREF(entity_type); PyUIEntityObject* entity_obj = (PyUIEntityObject*)value; self->data->perspective_entity = entity_obj->data; @@ -2053,14 +2037,7 @@ PyObject* UIGrid::py_apply_threshold(PyUIGridObject* self, PyObject* args, PyObj } // Validate source is a HeightMap - PyObject* heightmap_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "HeightMap"); - if (!heightmap_type) { - PyErr_SetString(PyExc_RuntimeError, "HeightMap type not found in module"); - return nullptr; - } - bool is_heightmap = PyObject_IsInstance(source_obj, heightmap_type); - Py_DECREF(heightmap_type); - if (!is_heightmap) { + if (!PyObject_IsInstance(source_obj, (PyObject*)&mcrfpydef::PyHeightMapType)) { PyErr_SetString(PyExc_TypeError, "source must be a HeightMap"); return nullptr; } @@ -2140,14 +2117,7 @@ PyObject* UIGrid::py_apply_ranges(PyUIGridObject* self, PyObject* args) { } // Validate source is a HeightMap - PyObject* heightmap_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "HeightMap"); - if (!heightmap_type) { - PyErr_SetString(PyExc_RuntimeError, "HeightMap type not found in module"); - return nullptr; - } - bool is_heightmap = PyObject_IsInstance(source_obj, heightmap_type); - Py_DECREF(heightmap_type); - if (!is_heightmap) { + if (!PyObject_IsInstance(source_obj, (PyObject*)&mcrfpydef::PyHeightMapType)) { PyErr_SetString(PyExc_TypeError, "source must be a HeightMap"); return nullptr; } @@ -2793,13 +2763,7 @@ void UIGrid::refreshCellCallbackCache(PyObject* pyObj) { // Helper to create typed cell callback arguments: (Vector, MouseButton, InputState) static PyObject* createCellCallbackArgs(sf::Vector2i cell, const std::string& button, const std::string& action) { // Create Vector object for cell position - PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!vector_type) { - PyErr_Print(); - return nullptr; - } - PyObject* cell_pos = PyObject_CallFunction(vector_type, "ff", (float)cell.x, (float)cell.y); - Py_DECREF(vector_type); + PyObject* cell_pos = PyObject_CallFunction((PyObject*)&mcrfpydef::PyVectorType, "ff", (float)cell.x, (float)cell.y); if (!cell_pos) { PyErr_Print(); return nullptr; @@ -2834,13 +2798,7 @@ static PyObject* createCellCallbackArgs(sf::Vector2i cell, const std::string& bu // #230 - Helper to create cell hover callback arguments: (Vector) only static PyObject* createCellHoverArgs(sf::Vector2i cell) { // Create Vector object for cell position - PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - if (!vector_type) { - PyErr_Print(); - return nullptr; - } - PyObject* cell_pos = PyObject_CallFunction(vector_type, "ii", cell.x, cell.y); - Py_DECREF(vector_type); + PyObject* cell_pos = PyObject_CallFunction((PyObject*)&mcrfpydef::PyVectorType, "ii", cell.x, cell.y); if (!cell_pos) { PyErr_Print(); return nullptr; diff --git a/src/UIGridPathfinding.cpp b/src/UIGridPathfinding.cpp index 2644b7f..8f8331f 100644 --- a/src/UIGridPathfinding.cpp +++ b/src/UIGridPathfinding.cpp @@ -83,14 +83,8 @@ sf::Vector2i DijkstraMap::stepFrom(int x, int y, bool* valid) const { bool UIGridPathfinding::ExtractPosition(PyObject* obj, int* x, int* y, UIGrid* expected_grid, const char* arg_name) { - // Get types from module to avoid static type instance issues - PyObject* entity_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity"); - PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); - // Check if it's an Entity - if (entity_type && PyObject_IsInstance(obj, entity_type)) { - Py_DECREF(entity_type); - Py_XDECREF(vector_type); + if (PyObject_IsInstance(obj, (PyObject*)&mcrfpydef::PyUIEntityType)) { auto* entity = (PyUIEntityObject*)obj; if (!entity->data) { PyErr_Format(PyExc_RuntimeError, @@ -111,17 +105,14 @@ bool UIGridPathfinding::ExtractPosition(PyObject* obj, int* x, int* y, *y = static_cast(entity->data->position.y); return true; } - Py_XDECREF(entity_type); // Check if it's a Vector - if (vector_type && PyObject_IsInstance(obj, vector_type)) { - Py_DECREF(vector_type); + if (PyObject_IsInstance(obj, (PyObject*)&mcrfpydef::PyVectorType)) { auto* vec = (PyVectorObject*)obj; *x = static_cast(vec->data.x); *y = static_cast(vec->data.y); return true; } - Py_XDECREF(vector_type); // Try tuple/list if (PySequence_Check(obj) && PySequence_Size(obj) == 2) { @@ -428,20 +419,13 @@ PyObject* UIGridPathfinding::DijkstraMap_to_heightmap(PyDijkstraMapObject* self, } } - // Create HeightMap via Python API (same pattern as BSP.to_heightmap) - PyObject* hmap_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "HeightMap"); - if (!hmap_type) { - PyErr_SetString(PyExc_RuntimeError, "HeightMap type not found"); - return nullptr; - } - + // Create HeightMap via Python API PyObject* size_tuple = Py_BuildValue("(ii)", width, height); PyObject* hmap_args = PyTuple_Pack(1, size_tuple); Py_DECREF(size_tuple); - PyHeightMapObject* hmap = (PyHeightMapObject*)PyObject_Call(hmap_type, hmap_args, nullptr); + PyHeightMapObject* hmap = (PyHeightMapObject*)PyObject_Call((PyObject*)&mcrfpydef::PyHeightMapType, hmap_args, nullptr); Py_DECREF(hmap_args); - Py_DECREF(hmap_type); if (!hmap) { return nullptr; diff --git a/src/UIGridPoint.cpp b/src/UIGridPoint.cpp index 02131f4..c50a80b 100644 --- a/src/UIGridPoint.cpp +++ b/src/UIGridPoint.cpp @@ -17,24 +17,8 @@ PyObject* sfColor_to_PyObject(sf::Color color) { // Utility function to convert PyObject* to sf::Color sf::Color PyObject_to_sfColor(PyObject* obj) { - // Get the mcrfpy module and Color type - PyObject* module = PyImport_ImportModule("mcrfpy"); - if (!module) { - PyErr_SetString(PyExc_RuntimeError, "Failed to import mcrfpy module"); - return sf::Color(); - } - - PyObject* color_type = PyObject_GetAttrString(module, "Color"); - Py_DECREF(module); - - if (!color_type) { - PyErr_SetString(PyExc_RuntimeError, "Failed to get Color type from mcrfpy module"); - return sf::Color(); - } - // Check if it's a mcrfpy.Color object - int is_color = PyObject_IsInstance(obj, color_type); - Py_DECREF(color_type); + int is_color = PyObject_IsInstance(obj, (PyObject*)&mcrfpydef::PyColorType); if (is_color == 1) { PyColorObject* color_obj = (PyColorObject*)obj; @@ -110,13 +94,8 @@ PyObject* UIGridPoint::get_entities(PyUIGridPointObject* self, void* closure) { if (static_cast(entity->position.x) == target_x && static_cast(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 type = &mcrfpydef::PyUIEntityType; auto obj = (PyUIEntityObject*)type->tp_alloc(type, 0); - Py_DECREF(type); if (!obj) { Py_DECREF(list); return NULL; diff --git a/src/UILine.cpp b/src/UILine.cpp index b42b888..9f80377 100644 --- a/src/UILine.cpp +++ b/src/UILine.cpp @@ -408,9 +408,8 @@ bool UILine::hasProperty(const std::string& name) const { // Python API implementation PyObject* UILine::get_start(PyUILineObject* self, void* closure) { auto vec = self->data->getStart(); - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); + auto type = &mcrfpydef::PyVectorType; auto obj = (PyVectorObject*)type->tp_alloc(type, 0); - Py_DECREF(type); if (obj) { obj->data = vec; } @@ -429,9 +428,8 @@ int UILine::set_start(PyUILineObject* self, PyObject* value, void* closure) { PyObject* UILine::get_end(PyUILineObject* self, void* closure) { auto vec = self->data->getEnd(); - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); + auto type = &mcrfpydef::PyVectorType; auto obj = (PyVectorObject*)type->tp_alloc(type, 0); - Py_DECREF(type); if (obj) { obj->data = vec; } @@ -450,11 +448,10 @@ int UILine::set_end(PyUILineObject* self, PyObject* value, void* closure) { PyObject* UILine::get_color(PyUILineObject* self, void* closure) { auto color = self->data->getColor(); - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"); + auto type = &mcrfpydef::PyColorType; PyObject* args = Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a); PyObject* obj = PyObject_CallObject((PyObject*)type, args); Py_DECREF(args); - Py_DECREF(type); return obj; } @@ -660,11 +657,7 @@ int UILine::init(PyUILineObject* self, PyObject* args, PyObject* kwds) { } // #184: Check if this is a Python subclass (for callback method support) - PyObject* line_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Line"); - if (line_type) { - self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != line_type; - Py_DECREF(line_type); - } + self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != (PyObject*)&mcrfpydef::PyUILineType; return 0; } diff --git a/src/UISprite.cpp b/src/UISprite.cpp index fd1ff00..7559359 100644 --- a/src/UISprite.cpp +++ b/src/UISprite.cpp @@ -350,7 +350,7 @@ PyObject* UISprite::get_texture(PyUISpriteObject* self, void* closure) int UISprite::set_texture(PyUISpriteObject* self, PyObject* value, void* closure) { // Check if value is a Texture instance - if (!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))) { + if (!PyObject_IsInstance(value, (PyObject*)&mcrfpydef::PyTextureType)) { PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); return -1; } @@ -370,7 +370,7 @@ int UISprite::set_texture(PyUISpriteObject* self, PyObject* value, void* closure PyObject* UISprite::get_pos(PyUISpriteObject* self, void* closure) { - auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector"); + auto type = &mcrfpydef::PyVectorType; auto obj = (PyVectorObject*)type->tp_alloc(type, 0); if (obj) { auto pos = self->data->getPosition(); @@ -515,9 +515,7 @@ int UISprite::init(PyUISpriteObject* self, PyObject* args, PyObject* kwds) std::shared_ptr texture_ptr = nullptr; if (snapshot && snapshot != Py_None) { // Check if snapshot is a Frame (most common case) - PyObject* frame_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"); - if (PyObject_IsInstance(snapshot, frame_type)) { - Py_DECREF(frame_type); + if (PyObject_IsInstance(snapshot, (PyObject*)&mcrfpydef::PyUIFrameType)) { auto pyframe = (PyUIFrameObject*)snapshot; if (!pyframe->data) { PyErr_SetString(PyExc_ValueError, "Invalid Frame object for snapshot"); @@ -547,14 +545,13 @@ int UISprite::init(PyUISpriteObject* self, PyObject* args, PyObject* kwds) texture_ptr = PyTexture::from_rendered(render_tex); sprite_index = 0; // Snapshot is always sprite index 0 } else { - Py_DECREF(frame_type); PyErr_SetString(PyExc_TypeError, "snapshot must be a Frame instance"); return -1; } } // Handle texture - allow None or use default (only if no snapshot) else if (texture && texture != Py_None) { - if (!PyObject_IsInstance(texture, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))) { + if (!PyObject_IsInstance(texture, (PyObject*)&mcrfpydef::PyTextureType)) { PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance or None"); return -1; } @@ -616,11 +613,7 @@ int UISprite::init(PyUISpriteObject* self, PyObject* args, PyObject* kwds) } // #184: Check if this is a Python subclass (for callback method support) - PyObject* sprite_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"); - if (sprite_type) { - self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != sprite_type; - Py_DECREF(sprite_type); - } + self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != (PyObject*)&mcrfpydef::PyUISpriteType; return 0; }