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 <noreply@anthropic.com>
This commit is contained in:
John McCardle 2026-03-07 23:18:42 -05:00
commit 71eb01c950
32 changed files with 249 additions and 944 deletions

View file

@ -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;

View file

@ -390,15 +390,11 @@ PyObject* PyVoxelGrid::get_material(PyVoxelGridObject* self, PyObject* args) {
const mcrf::VoxelMaterial& mat = self->data->getMaterial(static_cast<uint8_t>(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;

View file

@ -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<PyHeightMapObject*>(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<PyHeightMapObject*>(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<PyHeightMapObject*>(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<PyHeightMapObject*>(hm_obj);
if (!hm->heightmap) {

View file

@ -514,8 +514,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<UIFrame>(drawable);
@ -526,8 +525,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<UICaption>(drawable);
@ -539,8 +537,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<UISprite>(drawable);
@ -551,8 +548,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<UIGrid>(drawable);
@ -563,8 +559,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<UILine>(drawable);
@ -575,8 +570,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<UICircle>(drawable);
@ -587,8 +581,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<UIArc>(drawable);
@ -601,10 +594,6 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> drawable) {
Py_RETURN_NONE;
}
if (type) {
Py_DECREF(type);
}
return obj ? obj : Py_None;
}
@ -622,13 +611,9 @@ static PyObject* convertEntityToPython(std::shared_ptr<UIEntity> 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<mcrf::Entity3D> 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;

View file

@ -1544,7 +1544,7 @@ static void find_in_collection(std::vector<std::shared_ptr<UIDrawable>>* collect
switch (drawable->derived_type()) {
case PyObjectsEnum::UIFRAME: {
auto frame = std::static_pointer_cast<UIFrame>(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<std::shared_ptr<UIDrawable>>* collect
}
case PyObjectsEnum::UICAPTION: {
auto caption = std::static_pointer_cast<UICaption>(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<std::shared_ptr<UIDrawable>>* collect
}
case PyObjectsEnum::UISPRITE: {
auto sprite = std::static_pointer_cast<UISprite>(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<std::shared_ptr<UIDrawable>>* collect
}
case PyObjectsEnum::UIGRID: {
auto grid = std::static_pointer_cast<UIGrid>(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<int>(vec->data.x);
*y = static_cast<int>(vec->data.y);
return true;
}
Py_XDECREF(vector_type);
PyErr_Format(PyExc_TypeError,
"%s: expected (x, y) tuple or Vector, got %s",

View file

@ -2,6 +2,7 @@
#include "McRFPy_API.h"
#include "GameEngine.h"
#include "PyPositionHelper.h"
#include "PyVector.h"
#include <fstream>
#include <iostream>
#include <sstream>
@ -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)

View file

@ -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;

View file

@ -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;

View file

@ -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();

View file

@ -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<sf::Uint8>(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);

View file

@ -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;

View file

@ -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();

View file

@ -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) {

View file

@ -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)

View file

@ -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();
}

View file

@ -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;

View file

@ -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 <algorithm>
@ -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();

View file

@ -6,6 +6,7 @@
#include "PyTransition.h"
#include "PyKey.h"
#include "PyInputState.h"
#include "PyVector.h"
#include <iostream>
// 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;
}

View file

@ -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();
}

View file

@ -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) {

View file

@ -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;
}

View file

@ -255,7 +255,7 @@ int UICaption::set_color_member(PyUICaptionObject* self, PyObject* value, void*
auto member_ptr = reinterpret_cast<intptr_t>(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> 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;
}

View file

@ -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;
}

View file

@ -20,7 +20,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<UIDrawable> 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<UIFrame>(drawable);
@ -47,8 +46,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<UICaption>(drawable);
@ -60,8 +58,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<UISprite>(drawable);
@ -72,8 +69,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<UIGrid>(drawable);
@ -84,8 +80,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<UILine>(drawable);
@ -96,8 +91,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<UICircle>(drawable);
@ -108,8 +102,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<UIArc>(drawable);
@ -120,8 +113,7 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<mcrf::Viewport3D>(drawable);
@ -134,10 +126,6 @@ static PyObject* convertDrawableToPython(std::shared_ptr<UIDrawable> 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<UIDrawable> drawable) {
return obj;
}
// Helper to extract shared_ptr<UIDrawable> from any UIDrawable Python subclass.
// Returns nullptr without setting an error if the type doesn't match.
static std::shared_ptr<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> owner_ptr = self->owner.lock();
// Helper lambda to add drawable with parent tracking
auto addDrawable = [&](std::shared_ptr<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> 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<UIDrawable> search_drawable = extractDrawable(value);
if (!search_drawable) {
return PyLong_FromLong(0);
}

View file

@ -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<UIFrame>(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<UICaption>(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<UISprite>(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<UIGrid>(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<UIDrawable> 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;

View file

@ -208,7 +208,7 @@ int UIEntity::init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) {
// Handle texture argument
std::shared_ptr<PyTexture> 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<float>(vec.x), static_cast<float>(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;

View file

@ -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<PyColorObject*>(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<intptr_t>(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<UIDrawable> 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;
}

View file

@ -873,7 +873,7 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
// Handle texture argument
std::shared_ptr<PyTexture> 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;

View file

@ -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<int>(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<int>(vec->data.x);
*y = static_cast<int>(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;

View file

@ -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<int>(entity->position.x) == target_x &&
static_cast<int>(entity->position.y) == target_y) {
// Create Python Entity object for this entity
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity");
if (!type) {
Py_DECREF(list);
return NULL;
}
auto type = &mcrfpydef::PyUIEntityType;
auto obj = (PyUIEntityObject*)type->tp_alloc(type, 0);
Py_DECREF(type);
if (!obj) {
Py_DECREF(list);
return NULL;

View file

@ -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;
}

View file

@ -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<PyTexture> 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;
}