diff --git a/src/McRFPy_API.cpp b/src/McRFPy_API.cpp index 47cd05a..7e2a30f 100644 --- a/src/McRFPy_API.cpp +++ b/src/McRFPy_API.cpp @@ -140,6 +140,10 @@ PyObject* PyInit_mcrfpy() PyModule_AddType(m, &mcrfpydef::PyUIGridPointType); PyModule_AddType(m, &mcrfpydef::PyUIGridPointStateType); PyModule_AddType(m, &mcrfpydef::PyUIEntityType); + + PyModule_AddType(m, &mcrfpydef::PyUIEntityCollectionIterType); + PyModule_AddType(m, &mcrfpydef::PyUIEntityCollectionType); + PyModule_AddType(m, &mcrfpydef::PyUIGridType); diff --git a/src/UI.cpp b/src/UI.cpp index f1d7ac6..4ff2077 100644 --- a/src/UI.cpp +++ b/src/UI.cpp @@ -162,6 +162,13 @@ UIGridPoint::UIGridPoint() { } +UIEntity::UIEntity() {} // this will not work lol. TODO remove default constructor by finding the shared pointer inits that use it + +UIEntity::UIEntity(UIGrid& grid) +: gridstate(grid.grid_x * grid.grid_y) +{ +} + // UIGrid methods UIGrid::UIGrid() @@ -195,11 +202,17 @@ UIGrid::UIGrid(int gx, int gy, IndexTexture* _itex, sf::Vector2f _xy, sf::Vector zoom(1.0f), center_x((gx/2) * _itex->grid_size), center_y((gy/2) * _itex->grid_size), itex(_itex), points(gx * gy) { + // set up blank list of entities + entities = std::make_shared>>(); + box.setSize(_wh); box.setPosition(_xy); box.setFillColor(sf::Color(0,0,0,0)); - renderTexture.create(_wh.x, _wh.y); + //renderTexture.create(_wh.x, _wh.y); + // create renderTexture with maximum theoretical size; sprite can resize to show whatever amount needs to be rendered + renderTexture.create(1920, 1080); // TODO - renderTexture should be window size; above 1080p this will cause rendering errors + sprite.setTexture(_itex->texture); output.setTextureRect( sf::IntRect(0, 0, @@ -222,7 +235,12 @@ void UIGrid::setSprite(int ti) void UIGrid::render(sf::Vector2f) { - renderTexture.clear(); + output.setPosition(box.getPosition()); // output sprite can move; update position when drawing + // output size can change; update size when drawing + output.setTextureRect( + sf::IntRect(0, 0, + box.getSize().x, box.getSize().y)); + renderTexture.clear(sf::Color(8, 8, 8, 255)); // TODO - UIGrid needs a "background color" field // sprites that are visible according to zoom, center_x, center_y, and box width float center_x_sq = center_x / itex->grid_size; float center_y_sq = center_y / itex->grid_size; diff --git a/src/UI.h b/src/UI.h index 7869cbb..2c655db 100644 --- a/src/UI.h +++ b/src/UI.h @@ -131,6 +131,9 @@ public: UISprite sprite; sf::Vector2f position; //(x,y) in grid coordinates; float for animation void render(sf::Vector2f); //override final; + + UIEntity(); + UIEntity(UIGrid&); }; @@ -191,17 +194,21 @@ typedef struct { typedef struct { PyObject_HEAD - std::shared_ptr data; + UIGridPoint* data; + std::shared_ptr grid; } PyUIGridPointObject; typedef struct { PyObject_HEAD - std::shared_ptr data; + UIGridPointState* data; + std::shared_ptr grid; + std::shared_ptr entity; } PyUIGridPointStateObject; typedef struct { PyObject_HEAD std::shared_ptr data; + PyObject* texture; } PyUIEntityObject; typedef struct { @@ -1438,20 +1445,60 @@ static PyObject* PyUIEntity_get_gridstate(PyUIEntityObject* self, void* closure) return UIGridPointStateVector_to_PyList(self->data->gridstate); } +static PyObject* PyUIEntity_get_spritenumber(PyUIEntityObject* self, void* closure) { + return PyLong_FromDouble(self->data->sprite.sprite_index); +} + +static int PyUIEntity_set_spritenumber(PyUIEntityObject* self, PyObject* value, void* closure) { + int val; + if (PyLong_Check(value)) + val = PyLong_AsLong(value); + else + { + PyErr_SetString(PyExc_TypeError, "Value must be an integer."); + return -1; + } + self->data->sprite.sprite_index = val; + self->data->sprite.sprite.setTextureRect(self->data->sprite.itex->spriteCoordinates(val)); // TODO - I don't like ".sprite.sprite" in this stack of UIEntity.UISprite.sf::Sprite + return 0; +} + +static PyObject* PyUIEntity_at(PyUIEntityObject* self, PyObject* o) +{ + int x, y; + if (!PyArg_ParseTuple(o, "ii", &x, &y)) { + PyErr_SetString(PyExc_TypeError, "UIEntity.at requires two integer arguments: (x, y)"); + return NULL; + } + + if (self->data->grid == NULL) { + PyErr_SetString(PyExc_ValueError, "Entity cannot access surroundings because it is not associated with a grid"); + return NULL; + } + + PyUIGridPointStateObject* obj = (PyUIGridPointStateObject*)((&PyUIGridPointStateType)->tp_alloc(&PyUIGridPointStateType, 0)); + //auto target = std::static_pointer_cast(target); + obj->data = &(self->data->gridstate[y + self->data->grid->grid_x * x]); + obj->grid = self->data->grid; + obj->entity = self->data; + return (PyObject*)obj; +} + +static PyMethodDef PyUIEntity_methods[] = { + {"at", (PyCFunction)PyUIEntity_at, METH_O}, + {NULL, NULL, 0, NULL} +}; + // Define getters and setters static PyGetSetDef PyUIEntity_getsetters[] = { {"position", (getter)PyUIEntity_get_position, (setter)PyUIEntity_set_position, "Entity position", NULL}, {"gridstate", (getter)PyUIEntity_get_gridstate, NULL, "Grid point states for the entity", NULL}, + {"sprite_number", (getter)PyUIEntity_get_spritenumber, (setter)PyUIEntity_set_spritenumber, "Sprite number (index) on the texture on the display", NULL}, {NULL} /* Sentinel */ }; +static int PyUIEntity_init(PyUIEntityObject*, PyObject*, PyObject*); // forward declare -// Implement the init function for UIEntity -static int PyUIEntity_init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) { - // Parse arguments to initialize a UIEntity instance - // This function should handle parsing the Python arguments and initializing the UIEntity instance appropriately - return 0; -} // Define the PyTypeObject for UIEntity @@ -1463,6 +1510,7 @@ static PyTypeObject PyUIEntityType = { // Methods omitted for brevity .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = "UIEntity objects", + .tp_methods = PyUIEntity_methods, .tp_getset = PyUIEntity_getsetters, .tp_init = (initproc)PyUIEntity_init, .tp_new = PyType_GenericNew, @@ -1540,6 +1588,79 @@ static int PyUIGrid_set_size(PyUIGridObject* self, PyObject* value, void* closur return 0; } +static PyObject* PyUIGrid_get_center(PyUIGridObject* self, void* closure) { + return Py_BuildValue("(ff)", self->data->center_x, self->data->center_y); +} + +static int PyUIGrid_set_center(PyUIGridObject* self, PyObject* value, void* closure) { + float x, y; + if (!PyArg_ParseTuple(value, "ff", &x, &y)) { + PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); + return -1; + } + self->data->center_x = x; + self->data->center_y = y; + return 0; +} + +static PyObject* PyUIGrid_get_float_member(PyUIGridObject* self, void* closure) +{ + auto member_ptr = reinterpret_cast(closure); + if (member_ptr == 0) // x + return PyFloat_FromDouble(self->data->box.getPosition().x); + else if (member_ptr == 1) // y + return PyFloat_FromDouble(self->data->box.getPosition().y); + else if (member_ptr == 2) // w + return PyFloat_FromDouble(self->data->box.getSize().x); + else if (member_ptr == 3) // h + return PyFloat_FromDouble(self->data->box.getSize().y); + else if (member_ptr == 4) // center_x + return PyFloat_FromDouble(self->data->center_x); + else if (member_ptr == 5) // center_y + return PyFloat_FromDouble(self->data->center_y); + else if (member_ptr == 6) // zoom + return PyFloat_FromDouble(self->data->zoom); + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } +} + + +static int PyUIGrid_set_float_member(PyUIGridObject* self, PyObject* value, void* closure) +{ + float val; + auto member_ptr = reinterpret_cast(closure); + if (PyFloat_Check(value)) + { + val = PyFloat_AsDouble(value); + } + else if (PyLong_Check(value)) + { + val = PyLong_AsLong(value); + } + else + { + PyErr_SetString(PyExc_TypeError, "Value must be a floating point number."); + return -1; + } + if (member_ptr == 0) // x + self->data->box.setPosition(val, self->data->box.getPosition().y); + else if (member_ptr == 1) // y + self->data->box.setPosition(self->data->box.getPosition().x, val); + else if (member_ptr == 2) // w + self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y)); + else if (member_ptr == 3) // h + self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val)); + else if (member_ptr == 4) // center_x + self->data->center_x = val; + else if (member_ptr == 5) // center_y + self->data->center_y = val; + else if (member_ptr == 6) // zoom + self->data->zoom = val; + return 0; +} // TODO (7DRL Day 2, item 5.) return Texture object /* static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { @@ -1548,13 +1669,47 @@ static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { } */ +static PyObject* PyUIGrid_at(PyUIGridObject* self, PyObject* o) +{ + int x, y; + if (!PyArg_ParseTuple(o, "ii", &x, &y)) { + PyErr_SetString(PyExc_TypeError, "UIGrid.at requires two integer arguments: (x, y)"); + return NULL; + } + + PyUIGridPointObject* obj = (PyUIGridPointObject*)((&PyUIGridPointType)->tp_alloc(&PyUIGridPointType, 0)); + //auto target = std::static_pointer_cast(target); + obj->data = &(self->data->points[y + self->data->grid_x * x]); + obj->grid = self->data; + return (PyObject*)obj; +} + +static PyMethodDef PyUIGrid_methods[] = { + {"at", (PyCFunction)PyUIGrid_at, METH_O}, + {NULL, NULL, 0, NULL} +}; + static PyObject* PyUIGrid_get_children(PyUIGridObject* self, void* closure); // forward declare static PyGetSetDef PyUIGrid_getsetters[] = { + + // TODO - refactor into get_vector_member with field identifier values `(void*)n` {"grid_size", (getter)PyUIGrid_get_grid_size, NULL, "Grid dimensions (grid_x, grid_y)", NULL}, {"position", (getter)PyUIGrid_get_position, (setter)PyUIGrid_set_position, "Position of the grid (x, y)", NULL}, {"size", (getter)PyUIGrid_get_size, (setter)PyUIGrid_set_size, "Size of the grid (width, height)", NULL}, + {"center", (getter)PyUIGrid_get_center, (setter)PyUIGrid_set_center, "Grid coordinate at the center of the Grid's view (pan)", NULL}, + + // TODO / BUGFIX - everything about Entity collection {"entities", (getter)PyUIGrid_get_children, NULL, "EntityCollection of entities on this grid", NULL}, + + {"x", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "top-left corner X-coordinate", (void*)0}, + {"y", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "top-left corner Y-coordinate", (void*)1}, + {"w", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "visible widget width", (void*)2}, + {"h", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "visible widget height", (void*)3}, + {"center_x", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "center of the view X-coordinate", (void*)4}, + {"center_y", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "center of the view Y-coordinate", (void*)5}, + {"zoom", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "zoom factor for displaying the Grid", (void*)6}, + //{"texture", (getter)PyUIGrid_get_texture, NULL, "Texture of the grid", NULL}, //TODO 7DRL-day2-item5 {NULL} /* Sentinel */ }; @@ -1585,7 +1740,7 @@ static void PyUIGrid_dealloc(PyUIGridObject* self) { //.tp_iternext .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = PyDoc_STR("docstring"), - //.tp_methods = PyUIGrid_methods, + .tp_methods = PyUIGrid_methods, //.tp_members = PyUIGrid_members, .tp_getset = PyUIGrid_getsetters, //.tp_base = NULL, @@ -1605,6 +1760,59 @@ static void PyUIGrid_dealloc(PyUIGridObject* self) { * */ +// PyUIEntity_init defined here because it depends on the PyUIGridType (to accept grid optional keyword argument) +static int PyUIEntity_init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) { + static const char* keywords[] = { "x", "y", "texture", "sprite_index", "grid", nullptr }; + float x = 0.0f, y = 0.0f, scale = 1.0f; + int sprite_index = -1; + PyObject* texture = NULL; + PyObject* grid = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffOi|O", + const_cast(keywords), &x, &y, &texture, &sprite_index, &grid)) + { + return -1; + } + + // check types for texture + // + // Set Texture + // + if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&PyTextureType)){ + PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); + return -1; + } else if (texture != NULL) + { + self->texture = texture; + Py_INCREF(texture); + } else + { + // default tex? + } + + if (grid != NULL && !PyObject_IsInstance(grid, (PyObject*)&PyUIGridType)) { + PyErr_SetString(PyExc_TypeError, "grid must be a mcrfpy.Grid instance"); + return -1; + } + + auto pytexture = (PyTextureObject*)texture; + if (grid == NULL) + self->data = std::make_shared(); + else + self->data = std::make_shared(*((PyUIGridObject*)grid)->data); + + // TODO - PyTextureObjects and IndexTextures are a little bit of a mess with shared/unshared pointers + self->data->sprite = UISprite(pytexture->data.get(), sprite_index, sf::Vector2f(0,0), 1.0); + self->data->position = sf::Vector2f(x, y); + if (grid != NULL) { + PyUIGridObject* pygrid = (PyUIGridObject*)grid; + self->data->grid = pygrid->data; + // todone - on creation of Entity with Grid assignment, also append it to the entity list + pygrid->data->entities->push_back(self->data); + } + return 0; +} + /* * * Begin PyUIEntityCollectionIter defs @@ -1701,6 +1909,7 @@ static void PyUIGrid_dealloc(PyUIGridObject* self) { typedef struct { PyObject_HEAD std::shared_ptr>> data; + std::shared_ptr grid; } PyUIEntityCollectionObject; static Py_ssize_t PyUIEntityCollection_len(PyUIEntityCollectionObject* self) { @@ -1760,6 +1969,7 @@ static void PyUIGrid_dealloc(PyUIGridObject* self) { } PyUIEntityObject* entity = (PyUIEntityObject*)o; self->data->push_back(entity->data); + entity->data->grid = self->grid; Py_INCREF(Py_None); return Py_None; @@ -1786,9 +1996,10 @@ static void PyUIGrid_dealloc(PyUIGridObject* self) { // release the shared pointer at self->data[index]; //self->data->erase(self->data->begin() + index); // (Advance list to position) - auto l_front = self->data->begin(); - std::advance(l_front, index); + //auto l_front = self->data->begin(); + //std::advance(l_front, index); //self->data->erase(std::remove(l_front, std::next(l_front)); // TODO / BUGFIX - ??? + self->data->erase(std::next(self->data->begin(), index)); Py_INCREF(Py_None); return Py_None; } @@ -1864,8 +2075,10 @@ static void PyUIGrid_dealloc(PyUIGridObject* self) { { // create PyUICollection instance pointing to self->data->children PyUIEntityCollectionObject* o = (PyUIEntityCollectionObject*)PyUIEntityCollectionType.tp_alloc(&PyUIEntityCollectionType, 0); - if (o) - o->data = self->data->entities; // TODO / BUGFIX - entities isn't a shared pointer on UIGrid, what to do? + if (o) { + o->data = self->data->entities; // todone. / BUGFIX - entities isn't a shared pointer on UIGrid, what to do? -- I made it a sp>> + o->grid = self->data; + } return (PyObject*)o; } diff --git a/src/UITestScene.cpp b/src/UITestScene.cpp index 17f570a..4dbc9e9 100644 --- a/src/UITestScene.cpp +++ b/src/UITestScene.cpp @@ -119,7 +119,8 @@ UITestScene::UITestScene(GameEngine* g) : Scene(g) //UIEntity test: // asdf // TODO - reimplement UISprite style rendering within UIEntity class. Entities don't have a screen pixel position, they have a grid position, and grid sets zoom when rendering them. - auto e5a = std::make_shared(); // default constructor sucks: sprite position + zoom are irrelevant for UIEntity. + auto e5a = std::make_shared(*e5); // this basic constructor sucks: sprite position + zoom are irrelevant for UIEntity. + e5a->grid = e5; auto e5as = UISprite(indextex, 85, sf::Vector2f(0, 0), 1.0); e5a->sprite = e5as; // will copy constructor even exist for UISprite...? e5a->position = sf::Vector2f(1, 0);