diff --git a/src/UI.cpp b/src/UI.cpp index 5d43918..d56dc4a 100644 --- a/src/UI.cpp +++ b/src/UI.cpp @@ -1,527 +1,527 @@ -#include "UI.h" -#include "Resources.h" -#include "GameEngine.h" - -/* //callability fields & methods - PyObject* click_callable; - virtual UIDrawable* click_at(sf::Vector2f point); - void click_register(PyObject*); - void click_unregister(); -*/ - -UIDrawable::UIDrawable() { click_callable = NULL; } - -UIDrawable* UIFrame::click_at(sf::Vector2f point) -{ - for (auto e: *children) - { - auto p = e->click_at(point + box.getPosition()); - if (p) - return p; - } - if (click_callable) - { - float x = box.getPosition().x, y = box.getPosition().y, w = box.getSize().x, h = box.getSize().y; - if (point.x > x && point.y > y && point.x < x+w && point.y < y+h) return this; - } - return NULL; -} - -UIDrawable* UICaption::click_at(sf::Vector2f point) -{ - if (click_callable) - { - if (text.getGlobalBounds().contains(point)) return this; - } - return NULL; -} - -UIDrawable* UISprite::click_at(sf::Vector2f point) -{ - if (click_callable) - { - if(sprite.getGlobalBounds().contains(point)) return this; - } - return NULL; -} - -UIDrawable* UIGrid::click_at(sf::Vector2f point) -{ - if (click_callable) - { - if(box.getGlobalBounds().contains(point)) return this; - } - return NULL; -} - -void UIDrawable::click_register(PyObject* callable) -{ - /* - if (click_callable) - { - // decrement reference before overwriting - Py_DECREF(click_callable); - } - click_callable = callable; - Py_INCREF(click_callable); - */ - click_callable = std::make_unique(callable); -} - -void UIDrawable::click_unregister() -{ - /* - if (click_callable == NULL) return; - Py_DECREF(click_callable); - click_callable = NULL; - */ - click_callable.reset(); -} - -void UIDrawable::render() -{ - //std::cout << "Rendering base UIDrawable\n"; - render(sf::Vector2f()); -} -UIFrame::UIFrame(): +//#include "UI.h" +//#include "Resources.h" +//#include "GameEngine.h" +// +///* //callability fields & methods +// PyObject* click_callable; +// virtual UIDrawable* click_at(sf::Vector2f point); +// void click_register(PyObject*); +// void click_unregister(); +//*/ +// +//UIDrawable::UIDrawable() { click_callable = NULL; } +// +//UIDrawable* UIFrame::click_at(sf::Vector2f point) +//{ +// for (auto e: *children) +// { +// auto p = e->click_at(point + box.getPosition()); +// if (p) +// return p; +// } +// if (click_callable) +// { +// float x = box.getPosition().x, y = box.getPosition().y, w = box.getSize().x, h = box.getSize().y; +// if (point.x > x && point.y > y && point.x < x+w && point.y < y+h) return this; +// } +// return NULL; +//} +// +//UIDrawable* UICaption::click_at(sf::Vector2f point) +//{ +// if (click_callable) +// { +// if (text.getGlobalBounds().contains(point)) return this; +// } +// return NULL; +//} +// +//UIDrawable* UISprite::click_at(sf::Vector2f point) +//{ +// if (click_callable) +// { +// if(sprite.getGlobalBounds().contains(point)) return this; +// } +// return NULL; +//} +// +//UIDrawable* UIGrid::click_at(sf::Vector2f point) +//{ +// if (click_callable) +// { +// if(box.getGlobalBounds().contains(point)) return this; +// } +// return NULL; +//} +// +//void UIDrawable::click_register(PyObject* callable) +//{ +// /* +// if (click_callable) +// { +// // decrement reference before overwriting +// Py_DECREF(click_callable); +// } +// click_callable = callable; +// Py_INCREF(click_callable); +// */ +// click_callable = std::make_unique(callable); +//} +// +//void UIDrawable::click_unregister() +//{ +// /* +// if (click_callable == NULL) return; +// Py_DECREF(click_callable); +// click_callable = NULL; +// */ +// click_callable.reset(); +//} +// +//void UIDrawable::render() +//{ +// //std::cout << "Rendering base UIDrawable\n"; +// render(sf::Vector2f()); +//} +//UIFrame::UIFrame(): //x(0), y(0), w(0), h(0), -outline(0) -{ - children = std::make_shared>>(); - box.setPosition(0, 0); - box.setSize(sf::Vector2f(0, 0)); - /* - pyOutlineColor = NULL; - pyFillColor = NULL; - _outlineColor = NULL; - _fillColor = NULL; - */ -} - -UIFrame::UIFrame(float _x, float _y, float _w, float _h): +//outline(0) +//{ +// children = std::make_shared>>(); +// box.setPosition(0, 0); +// box.setSize(sf::Vector2f(0, 0)); +// /* +// pyOutlineColor = NULL; +// pyFillColor = NULL; +// _outlineColor = NULL; +// _fillColor = NULL; +// */ +//} +// +//UIFrame::UIFrame(float _x, float _y, float _w, float _h): //x(_x), y(_y), w(_w), h(_h), -outline(0) -{ - box.setPosition(_x, _y); - box.setSize(sf::Vector2f(_w, _h)); - children = std::make_shared>>(); - /* - pyOutlineColor = NULL; - pyFillColor = NULL; - _outlineColor = NULL; - _fillColor = NULL; - */ -} - -UIFrame::~UIFrame() -{ - children.reset(); - /* - if (pyOutlineColor) Py_DECREF(pyOutlineColor); - else if (_outlineColor) delete _outlineColor; - if (pyFillColor) Py_DECREF(pyFillColor); - else if (_fillColor) delete _fillColor; - */ -} - -/* - sf::Color& fillColor(); // getter - void fillColor(sf::Color c); // C++ setter - void fillColor(PyObject* pyColor); // Python setter - - sf::Color& outlineColor(); // getter - void outlineColor(sf::Color c); // C++ setter - void outlineColor(PyObject* pyColor); // Python setter -*/ - - -PyObjectsEnum UIFrame::derived_type() -{ - return PyObjectsEnum::UIFRAME; -} - -void UIFrame::render(sf::Vector2f offset) -{ - //std::cout << "Rendering UIFrame w/ offset " << offset.x << ", " << offset.y << "\n"; - //std::cout << "position = " << x << ", " << y << "\n"; - box.move(offset); - Resources::game->getWindow().draw(box); - box.move(-offset); - //sf::RectangleShape box = sf::RectangleShape(sf::Vector2f(w,h)); - //sf::Vector2f pos = sf::Vector2f(x, y); - //box.setPosition(offset + pos); - //if (_fillColor) { box.setFillColor(fillColor()); } - //if (_outlineColor) { box.setOutlineColor(outlineColor()); } - //box.setOutlineThickness(outline); - //Resources::game->getWindow().draw(box); - for (auto drawable : *children) { - drawable->render(offset + box.getPosition()); - } -} - -void UICaption::render(sf::Vector2f offset) -{ - //std::cout << "Rendering Caption with offset\n"; - text.move(offset); - Resources::game->getWindow().draw(text); - text.move(-offset); -} - -UISprite::UISprite() {} -/* - // * tearing down the old IndexTexture way of life -UISprite::UISprite(IndexTexture* _itex, int _sprite_index, float x = 0.0, float y = 0.0, float s = 1.0) -: itex(_itex), sprite_index(_sprite_index) -{ - sprite.setTexture(_itex->texture); - sprite.setTextureRect(_itex->spriteCoordinates(_sprite_index)); - sprite.setPosition(sf::Vector2f(x, y)); - sprite.setScale(sf::Vector2f(s, s)); -} - -UISprite::UISprite(IndexTexture* _itex, int _sprite_index, sf::Vector2f pos, float s = 1.0) -: itex(_itex), sprite_index(_sprite_index) -{ - sprite.setTexture(_itex->texture); - sprite.setTextureRect(_itex->spriteCoordinates(_sprite_index)); - sprite.setPosition(pos); - sprite.setScale(sf::Vector2f(s, s)); -} -*/ - -UISprite::UISprite(std::shared_ptr _ptex, int _sprite_index, sf::Vector2f _pos, float _scale) -: ptex(_ptex), sprite_index(_sprite_index) -{ - sprite = ptex->sprite(sprite_index, _pos, sf::Vector2f(_scale, _scale)); -} - +//outline(0) +//{ +// box.setPosition(_x, _y); +// box.setSize(sf::Vector2f(_w, _h)); +// children = std::make_shared>>(); +// /* +// pyOutlineColor = NULL; +// pyFillColor = NULL; +// _outlineColor = NULL; +// _fillColor = NULL; +// */ +//} +// +//UIFrame::~UIFrame() +//{ +// children.reset(); +// /* +// if (pyOutlineColor) Py_DECREF(pyOutlineColor); +// else if (_outlineColor) delete _outlineColor; +// if (pyFillColor) Py_DECREF(pyFillColor); +// else if (_fillColor) delete _fillColor; +// */ +//} +// +///* +// sf::Color& fillColor(); // getter +// void fillColor(sf::Color c); // C++ setter +// void fillColor(PyObject* pyColor); // Python setter +// +// sf::Color& outlineColor(); // getter +// void outlineColor(sf::Color c); // C++ setter +// void outlineColor(PyObject* pyColor); // Python setter +//*/ +// +// +//PyObjectsEnum UIFrame::derived_type() +//{ +// return PyObjectsEnum::UIFRAME; +//} +// +//void UIFrame::render(sf::Vector2f offset) +//{ +// //std::cout << "Rendering UIFrame w/ offset " << offset.x << ", " << offset.y << "\n"; +// //std::cout << "position = " << x << ", " << y << "\n"; +// box.move(offset); +// Resources::game->getWindow().draw(box); +// box.move(-offset); +// //sf::RectangleShape box = sf::RectangleShape(sf::Vector2f(w,h)); +// //sf::Vector2f pos = sf::Vector2f(x, y); +// //box.setPosition(offset + pos); +// //if (_fillColor) { box.setFillColor(fillColor()); } +// //if (_outlineColor) { box.setOutlineColor(outlineColor()); } +// //box.setOutlineThickness(outline); +// //Resources::game->getWindow().draw(box); +// for (auto drawable : *children) { +// drawable->render(offset + box.getPosition()); +// } +//} +// +//void UICaption::render(sf::Vector2f offset) +//{ +// //std::cout << "Rendering Caption with offset\n"; +// text.move(offset); +// Resources::game->getWindow().draw(text); +// text.move(-offset); +//} +// +//UISprite::UISprite() {} +///* +// // * tearing down the old IndexTexture way of life +//UISprite::UISprite(IndexTexture* _itex, int _sprite_index, float x = 0.0, float y = 0.0, float s = 1.0) +//: itex(_itex), sprite_index(_sprite_index) +//{ +// sprite.setTexture(_itex->texture); +// sprite.setTextureRect(_itex->spriteCoordinates(_sprite_index)); +// sprite.setPosition(sf::Vector2f(x, y)); +// sprite.setScale(sf::Vector2f(s, s)); +//} +// +//UISprite::UISprite(IndexTexture* _itex, int _sprite_index, sf::Vector2f pos, float s = 1.0) +//: itex(_itex), sprite_index(_sprite_index) +//{ +// sprite.setTexture(_itex->texture); +// sprite.setTextureRect(_itex->spriteCoordinates(_sprite_index)); +// sprite.setPosition(pos); +// sprite.setScale(sf::Vector2f(s, s)); +//} +//*/ +// +//UISprite::UISprite(std::shared_ptr _ptex, int _sprite_index, sf::Vector2f _pos, float _scale) +//: ptex(_ptex), sprite_index(_sprite_index) +//{ +// sprite = ptex->sprite(sprite_index, _pos, sf::Vector2f(_scale, _scale)); +//} +// //void UISprite::update() //{ - //auto& tex = Resources::game->textures[texture_index]; - //sprite.setTexture(tex.texture); - //sprite.setScale(sf::Vector2f(scale, scale)); - //sprite.setPosition(sf::Vector2f(x, y)); - //std::cout << "Drawable position: " << x << ", " << y << " -> " << s.getPosition().x << ", " << s.getPosition().y << std::endl; - //sprite.setTextureRect(tex.spriteCoordinates(sprite_index)); +// //auto& tex = Resources::game->textures[texture_index]; +// //sprite.setTexture(tex.texture); +// //sprite.setScale(sf::Vector2f(scale, scale)); +// //sprite.setPosition(sf::Vector2f(x, y)); +// //std::cout << "Drawable position: " << x << ", " << y << " -> " << s.getPosition().x << ", " << s.getPosition().y << std::endl; +// //sprite.setTextureRect(tex.spriteCoordinates(sprite_index)); //} - -void UISprite::render(sf::Vector2f offset) -{ - sprite.move(offset); - Resources::game->getWindow().draw(sprite); - sprite.move(-offset); -} - +// +//void UISprite::render(sf::Vector2f offset) +//{ +// sprite.move(offset); +// Resources::game->getWindow().draw(sprite); +// sprite.move(-offset); +//} +// // 7DRL hack; needed to draw entities to UIGrid. TODO, apply this technique to all UIDrawables -void UISprite::render(sf::Vector2f offset, sf::RenderTexture& target) -{ - sprite.move(offset); - target.draw(sprite); - sprite.move(-offset); -} - -/* -void UISprite::setPosition(float x, float y) -{ - setPosition(sf::Vector2f(x, y)); -} -*/ - -void UISprite::setPosition(sf::Vector2f pos) -{ - sprite.setPosition(pos); -} - -void UISprite::setScale(sf::Vector2f s) -{ - sprite.setScale(s); -} - -void UISprite::setTexture(std::shared_ptr _ptex, int _sprite_index) -{ - ptex = _ptex; - if (_sprite_index != -1) // if you are changing textures, there's a good chance you need a new index too - sprite_index = _sprite_index; - sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale()); -} - -void UISprite::setSpriteIndex(int _sprite_index) -{ - sprite_index = _sprite_index; - sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale()); -} - -sf::Vector2f UISprite::getScale() -{ - return sprite.getScale(); -} - -sf::Vector2f UISprite::getPosition() -{ - return sprite.getPosition(); -} - -std::shared_ptr UISprite::getTexture() -{ - return ptex; -} - -int UISprite::getSpriteIndex() -{ - return sprite_index; -} - -PyObjectsEnum UICaption::derived_type() -{ - return PyObjectsEnum::UICAPTION; -} - -PyObjectsEnum UISprite::derived_type() -{ - return PyObjectsEnum::UISPRITE; -} - +//void UISprite::render(sf::Vector2f offset, sf::RenderTexture& target) +//{ +// sprite.move(offset); +// target.draw(sprite); +// sprite.move(-offset); +//} +// +///* +//void UISprite::setPosition(float x, float y) +//{ +// setPosition(sf::Vector2f(x, y)); +//} +//*/ +// +//void UISprite::setPosition(sf::Vector2f pos) +//{ +// sprite.setPosition(pos); +//} +// +//void UISprite::setScale(sf::Vector2f s) +//{ +// sprite.setScale(s); +//} +// +//void UISprite::setTexture(std::shared_ptr _ptex, int _sprite_index) +//{ +// ptex = _ptex; +// if (_sprite_index != -1) // if you are changing textures, there's a good chance you need a new index too +// sprite_index = _sprite_index; +// sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale()); +//} +// +//void UISprite::setSpriteIndex(int _sprite_index) +//{ +// sprite_index = _sprite_index; +// sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale()); +//} +// +//sf::Vector2f UISprite::getScale() +//{ +// return sprite.getScale(); +//} +// +//sf::Vector2f UISprite::getPosition() +//{ +// return sprite.getPosition(); +//} +// +//std::shared_ptr UISprite::getTexture() +//{ +// return ptex; +//} +// +//int UISprite::getSpriteIndex() +//{ +// return sprite_index; +//} +// +//PyObjectsEnum UICaption::derived_type() +//{ +// return PyObjectsEnum::UICAPTION; +//} +// +//PyObjectsEnum UISprite::derived_type() +//{ +// return PyObjectsEnum::UISPRITE; +//} +// // UIGrid support classes' methods - -UIGridPoint::UIGridPoint() -:color(1.0f, 1.0f, 1.0f), color_overlay(0.0f, 0.0f, 0.0f), walkable(false), transparent(false), - tilesprite(-1), tile_overlay(-1), uisprite(-1) -{ -} - -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) -{ -} - +// +//UIGridPoint::UIGridPoint() +//:color(1.0f, 1.0f, 1.0f), color_overlay(0.0f, 0.0f, 0.0f), walkable(false), transparent(false), +// tilesprite(-1), tile_overlay(-1), uisprite(-1) +//{ +//} +// +//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() -{ -} - -/* -UIGrid::UIGrid(int gx, int gy, std::shared_ptr _ptex, float _x, float _y, float _w, float _h) -: grid_x(gx), grid_y(gy), - zoom(1.0f), center_x((gx/2) * _ptex->sheet_width), center_y((gy/2) * _ptex->sheet_height), - itex(_itex), points(gx * gy) -{ - // set up blank list of entities - entities = std::make_shared>>(); - - box.setSize(sf::Vector2f(_w, _h)); - box.setPosition(sf::Vector2f(_x, _y)); - - box.setFillColor(sf::Color(0,0,0,0)); - renderTexture.create(_w, _h); - sprite.setTexture(_itex->texture); - output.setTextureRect( - sf::IntRect(0, 0, - box.getSize().x, box.getSize().y)); - output.setPosition(box.getPosition()); - // textures are upside-down inside renderTexture - output.setTexture(renderTexture.getTexture()); -} -*/ - -UIGrid::UIGrid(int gx, int gy, std::shared_ptr _ptex, sf::Vector2f _xy, sf::Vector2f _wh) -: grid_x(gx), grid_y(gy), - zoom(1.0f), center_x((gx/2) * _ptex->sprite_width), center_y((gy/2) * _ptex->sprite_height), - ptex(_ptex), 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); - // 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); - sprite = ptex->sprite(0); - - output.setTextureRect( - sf::IntRect(0, 0, - box.getSize().x, box.getSize().y)); - output.setPosition(box.getPosition()); - // textures are upside-down inside renderTexture - output.setTexture(renderTexture.getTexture()); - -} - -void UIGrid::update() -{ -} - -/* -void UIGrid::setSprite(int ti) -{ - //int tx = ti % itex->grid_width, ty = ti / itex->grid_width; - // sprite.setTextureRect(sf::IntRect(tx * itex->grid_size, ty * itex->grid_size, itex->grid_size, itex->grid_size)); - sprite = ptex->sprite(ti); -} -*/ - -void UIGrid::render(sf::Vector2f) -{ - 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 / ptex->sprite_width; - float center_y_sq = center_y / ptex->sprite_height; - - float width_sq = box.getSize().x / (ptex->sprite_width * zoom); - float height_sq = box.getSize().y / (ptex->sprite_height * zoom); - float left_edge = center_x_sq - (width_sq / 2.0); - float top_edge = center_y_sq - (height_sq / 2.0); - - int left_spritepixels = center_x - (box.getSize().x / 2.0 / zoom); - int top_spritepixels = center_y - (box.getSize().y / 2.0 / zoom); - - //sprite.setScale(sf::Vector2f(zoom, zoom)); - sf::RectangleShape r; // for colors and overlays - r.setSize(sf::Vector2f(ptex->sprite_width * zoom, ptex->sprite_height * zoom)); - r.setOutlineThickness(0); - - int x_limit = left_edge + width_sq + 2; - if (x_limit > grid_x) x_limit = grid_x; - - int y_limit = top_edge + height_sq + 2; - if (y_limit > grid_y) y_limit = grid_y; - - // base layer - bottom color, tile sprite ("ground") - for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0); - x < x_limit; //x < view_width; - x+=1) - { - //for (float y = (top_edge >= 0 ? top_edge : 0); - for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0); - y < y_limit; //y < view_height; - y+=1) - { - auto pixel_pos = sf::Vector2f( - (x*ptex->sprite_width - left_spritepixels) * zoom, - (y*ptex->sprite_height - top_spritepixels) * zoom ); - - auto gridpoint = at(std::floor(x), std::floor(y)); - - //sprite.setPosition(pixel_pos); - - r.setPosition(pixel_pos); - r.setFillColor(gridpoint.color); - renderTexture.draw(r); - - // tilesprite - // if discovered but not visible, set opacity to 90% - // if not discovered... just don't draw it? - if (gridpoint.tilesprite != -1) { - sprite = ptex->sprite(gridpoint.tilesprite, pixel_pos, sf::Vector2f(zoom, zoom)); //setSprite(gridpoint.tilesprite);; - renderTexture.draw(sprite); - } - } - } - - // middle layer - entities - // disabling entity rendering until I can render their UISprite inside the rendertexture (not directly to window) - for (auto e : *entities) { - // TODO skip out-of-bounds entities (grid square not visible at all, check for partially on visible grid squares / floating point grid position) - //auto drawent = e->cGrid->indexsprite.drawable(); - auto& drawent = e->sprite; - //drawent.setScale(zoom, zoom); - drawent.setScale(sf::Vector2f(zoom, zoom)); - auto pixel_pos = sf::Vector2f( - (e->position.x*ptex->sprite_width - left_spritepixels) * zoom, - (e->position.y*ptex->sprite_height - top_spritepixels) * zoom ); - //drawent.setPosition(pixel_pos); - //renderTexture.draw(drawent); - drawent.render(pixel_pos, renderTexture); - } - - - // top layer - opacity for discovered / visible status (debug, basically) - /* // Disabled until I attach a "perspective" - for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0); - x < x_limit; //x < view_width; - x+=1) - { - //for (float y = (top_edge >= 0 ? top_edge : 0); - for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0); - y < y_limit; //y < view_height; - y+=1) - { - - auto pixel_pos = sf::Vector2f( - (x*itex->grid_size - left_spritepixels) * zoom, - (y*itex->grid_size - top_spritepixels) * zoom ); - - auto gridpoint = at(std::floor(x), std::floor(y)); - - sprite.setPosition(pixel_pos); - - r.setPosition(pixel_pos); - - // visible & discovered layers for testing purposes - if (!gridpoint.discovered) { - r.setFillColor(sf::Color(16, 16, 20, 192)); // 255 opacity for actual blackout - renderTexture.draw(r); - } else if (!gridpoint.visible) { - r.setFillColor(sf::Color(32, 32, 40, 128)); - renderTexture.draw(r); - } - - // overlay - - // uisprite - } - } - */ - - // grid lines for testing & validation - /* - sf::Vertex line[] = - { - sf::Vertex(sf::Vector2f(0, 0), sf::Color::Red), - sf::Vertex(box.getSize(), sf::Color::Red), - - }; - - renderTexture.draw(line, 2, sf::Lines); - sf::Vertex lineb[] = - { - sf::Vertex(sf::Vector2f(0, box.getSize().y), sf::Color::Blue), - sf::Vertex(sf::Vector2f(box.getSize().x, 0), sf::Color::Blue), - - }; - - renderTexture.draw(lineb, 2, sf::Lines); - */ - - // render to window - renderTexture.display(); - Resources::game->getWindow().draw(output); - -} - -UIGridPoint& UIGrid::at(int x, int y) -{ - return points[y * grid_x + x]; -} - -PyObjectsEnum UIGrid::derived_type() -{ - return PyObjectsEnum::UIGRID; -} - -std::shared_ptr UIGrid::getTexture() -{ - return ptex; -} +// +//UIGrid::UIGrid() +//{ +//} +// +///* +//UIGrid::UIGrid(int gx, int gy, std::shared_ptr _ptex, float _x, float _y, float _w, float _h) +//: grid_x(gx), grid_y(gy), +// zoom(1.0f), center_x((gx/2) * _ptex->sheet_width), center_y((gy/2) * _ptex->sheet_height), +// itex(_itex), points(gx * gy) +//{ +// // set up blank list of entities +// entities = std::make_shared>>(); +// +// box.setSize(sf::Vector2f(_w, _h)); +// box.setPosition(sf::Vector2f(_x, _y)); +// +// box.setFillColor(sf::Color(0,0,0,0)); +// renderTexture.create(_w, _h); +// sprite.setTexture(_itex->texture); +// output.setTextureRect( +// sf::IntRect(0, 0, +// box.getSize().x, box.getSize().y)); +// output.setPosition(box.getPosition()); +// // textures are upside-down inside renderTexture +// output.setTexture(renderTexture.getTexture()); +//} +//*/ +// +//UIGrid::UIGrid(int gx, int gy, std::shared_ptr _ptex, sf::Vector2f _xy, sf::Vector2f _wh) +//: grid_x(gx), grid_y(gy), +// zoom(1.0f), center_x((gx/2) * _ptex->sprite_width), center_y((gy/2) * _ptex->sprite_height), +// ptex(_ptex), 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); +// // 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); +// sprite = ptex->sprite(0); +// +// output.setTextureRect( +// sf::IntRect(0, 0, +// box.getSize().x, box.getSize().y)); +// output.setPosition(box.getPosition()); +// // textures are upside-down inside renderTexture +// output.setTexture(renderTexture.getTexture()); +// +//} +// +//void UIGrid::update() +//{ +//} +// +///* +//void UIGrid::setSprite(int ti) +//{ +// //int tx = ti % itex->grid_width, ty = ti / itex->grid_width; +// // sprite.setTextureRect(sf::IntRect(tx * itex->grid_size, ty * itex->grid_size, itex->grid_size, itex->grid_size)); +// sprite = ptex->sprite(ti); +//} +//*/ +// +//void UIGrid::render(sf::Vector2f) +//{ +// 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 / ptex->sprite_width; +// float center_y_sq = center_y / ptex->sprite_height; +// +// float width_sq = box.getSize().x / (ptex->sprite_width * zoom); +// float height_sq = box.getSize().y / (ptex->sprite_height * zoom); +// float left_edge = center_x_sq - (width_sq / 2.0); +// float top_edge = center_y_sq - (height_sq / 2.0); +// +// int left_spritepixels = center_x - (box.getSize().x / 2.0 / zoom); +// int top_spritepixels = center_y - (box.getSize().y / 2.0 / zoom); +// +// //sprite.setScale(sf::Vector2f(zoom, zoom)); +// sf::RectangleShape r; // for colors and overlays +// r.setSize(sf::Vector2f(ptex->sprite_width * zoom, ptex->sprite_height * zoom)); +// r.setOutlineThickness(0); +// +// int x_limit = left_edge + width_sq + 2; +// if (x_limit > grid_x) x_limit = grid_x; +// +// int y_limit = top_edge + height_sq + 2; +// if (y_limit > grid_y) y_limit = grid_y; +// +// // base layer - bottom color, tile sprite ("ground") +// for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0); +// x < x_limit; //x < view_width; +// x+=1) +// { +// //for (float y = (top_edge >= 0 ? top_edge : 0); +// for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0); +// y < y_limit; //y < view_height; +// y+=1) +// { +// auto pixel_pos = sf::Vector2f( +// (x*ptex->sprite_width - left_spritepixels) * zoom, +// (y*ptex->sprite_height - top_spritepixels) * zoom ); +// +// auto gridpoint = at(std::floor(x), std::floor(y)); +// +// //sprite.setPosition(pixel_pos); +// +// r.setPosition(pixel_pos); +// r.setFillColor(gridpoint.color); +// renderTexture.draw(r); +// +// // tilesprite +// // if discovered but not visible, set opacity to 90% +// // if not discovered... just don't draw it? +// if (gridpoint.tilesprite != -1) { +// sprite = ptex->sprite(gridpoint.tilesprite, pixel_pos, sf::Vector2f(zoom, zoom)); //setSprite(gridpoint.tilesprite);; +// renderTexture.draw(sprite); +// } +// } +// } +// +// // middle layer - entities +// // disabling entity rendering until I can render their UISprite inside the rendertexture (not directly to window) +// for (auto e : *entities) { +// // TODO skip out-of-bounds entities (grid square not visible at all, check for partially on visible grid squares / floating point grid position) +// //auto drawent = e->cGrid->indexsprite.drawable(); +// auto& drawent = e->sprite; +// //drawent.setScale(zoom, zoom); +// drawent.setScale(sf::Vector2f(zoom, zoom)); +// auto pixel_pos = sf::Vector2f( +// (e->position.x*ptex->sprite_width - left_spritepixels) * zoom, +// (e->position.y*ptex->sprite_height - top_spritepixels) * zoom ); +// //drawent.setPosition(pixel_pos); +// //renderTexture.draw(drawent); +// drawent.render(pixel_pos, renderTexture); +// } +// +// +// // top layer - opacity for discovered / visible status (debug, basically) +// /* // Disabled until I attach a "perspective" +// for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0); +// x < x_limit; //x < view_width; +// x+=1) +// { +// //for (float y = (top_edge >= 0 ? top_edge : 0); +// for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0); +// y < y_limit; //y < view_height; +// y+=1) +// { +// +// auto pixel_pos = sf::Vector2f( +// (x*itex->grid_size - left_spritepixels) * zoom, +// (y*itex->grid_size - top_spritepixels) * zoom ); +// +// auto gridpoint = at(std::floor(x), std::floor(y)); +// +// sprite.setPosition(pixel_pos); +// +// r.setPosition(pixel_pos); +// +// // visible & discovered layers for testing purposes +// if (!gridpoint.discovered) { +// r.setFillColor(sf::Color(16, 16, 20, 192)); // 255 opacity for actual blackout +// renderTexture.draw(r); +// } else if (!gridpoint.visible) { +// r.setFillColor(sf::Color(32, 32, 40, 128)); +// renderTexture.draw(r); +// } +// +// // overlay +// +// // uisprite +// } +// } +// */ +// +// // grid lines for testing & validation +// /* +// sf::Vertex line[] = +// { +// sf::Vertex(sf::Vector2f(0, 0), sf::Color::Red), +// sf::Vertex(box.getSize(), sf::Color::Red), +// +// }; +// +// renderTexture.draw(line, 2, sf::Lines); +// sf::Vertex lineb[] = +// { +// sf::Vertex(sf::Vector2f(0, box.getSize().y), sf::Color::Blue), +// sf::Vertex(sf::Vector2f(box.getSize().x, 0), sf::Color::Blue), +// +// }; +// +// renderTexture.draw(lineb, 2, sf::Lines); +// */ +// +// // render to window +// renderTexture.display(); +// Resources::game->getWindow().draw(output); +// +//} +// +//UIGridPoint& UIGrid::at(int x, int y) +//{ +// return points[y * grid_x + x]; +//} +// +//PyObjectsEnum UIGrid::derived_type() +//{ +// return PyObjectsEnum::UIGRID; +//} +// +//std::shared_ptr UIGrid::getTexture() +//{ +// return ptex; +//} diff --git a/src/UI.h b/src/UI.h index c845baf..73dd5bc 100644 --- a/src/UI.h +++ b/src/UI.h @@ -15,445 +15,411 @@ #include "PyVector.h" #include "PyFont.h" -enum PyObjectsEnum : int -{ - UIFRAME = 1, - UICAPTION, - UISPRITE, - UIGRID -}; +#include "UIDrawable.h" -class UIDrawable -{ -public: - //UIDrawable* parent; - void render(); - virtual void render(sf::Vector2f) = 0; - //virtual sf::Rect aabb(); // not sure I care about this yet - //virtual sf::Vector2i position(); - //bool handle_event(/* ??? click, scroll, keystroke*/); - //std::string action; - virtual PyObjectsEnum derived_type() = 0; - - // Mouse input handling - callable object, methods to find event's destination - //PyObject* click_callable; - std::unique_ptr click_callable; - virtual UIDrawable* click_at(sf::Vector2f point) = 0; - void click_register(PyObject*); - void click_unregister(); - - UIDrawable(); -}; - -//Python object types & forward declarations -/* -typedef struct { - PyObject_HEAD - sf::Color color; -} PyColorObject; -*/ - -/* // Moved to PyColor.h -typedef struct { - PyObject_HEAD - std::shared_ptr data; -} PyColorObject; -*/ - -class UIFrame: public UIDrawable -{ -public: - UIFrame(float, float, float, float); - UIFrame(); - ~UIFrame(); - sf::RectangleShape box; - // todone: why does UIFrame have x,y,w,h AND a box? Which one should be used for bounds checks? (floats removed) - - //Simulate RectangleShape - //float x, y, w, h, - float outline; - std::shared_ptr>> children; - void render(sf::Vector2f) override final; - void move(sf::Vector2f); - - PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UIFrame; }; - virtual UIDrawable* click_at(sf::Vector2f point) override final; - /* - sf::Color fillColor(); // getter - void fillColor(sf::Color c); // C++ setter - void fillColor(PyColorObject* pyColor); // Python setter - - sf::Color outlineColor(); // getter - void outlineColor(sf::Color c); // C++ setter - void outlineColor(PyColorObject* pyColor); // Python setter - */ - -private: - //std::shared_ptr fillColor, outlineColor; - /* - sf::Color *_fillColor, *_outlineColor; - PyColorObject *pyFillColor, *pyOutlineColor; - */ -}; - -class UICaption: public UIDrawable -{ -public: - sf::Text text; - void render(sf::Vector2f) override final; - PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UICaption; }; - virtual UIDrawable* click_at(sf::Vector2f point) override final; -}; - -class UISprite: public UIDrawable -{ -private: - int sprite_index; - sf::Sprite sprite; -protected: - std::shared_ptr ptex; -public: - UISprite(); - //UISprite(IndexTexture*, int, float, float, float); - //UISprite(IndexTexture*, int, sf::Vector2f, float); - UISprite(std::shared_ptr, int, sf::Vector2f, float); - void update(); - void render(sf::Vector2f) override final; - virtual UIDrawable* click_at(sf::Vector2f point) override final; - - // 7DRL hack - TODO apply RenderTexture concept to all UIDrawables (via `sf::RenderTarget`) - void render(sf::Vector2f, sf::RenderTexture&); - //IndexTexture* itex; - //sf::Vector2f pos; - //float scale; - //void setPosition(float, float); - void setPosition(sf::Vector2f); - sf::Vector2f getPosition(); - void setScale(sf::Vector2f); - sf::Vector2f getScale(); - void setSpriteIndex(int); - int getSpriteIndex(); - - void setTexture(std::shared_ptr _ptex, int _sprite_index=-1); - std::shared_ptr getTexture(); - - PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UISprite; }; -}; +#include "UIFrame.h" +#include "UICaption.h" +#include "UISprite.h" +#include "UIGridPoint.h" +#include "UIEntity.h" +#include "UIGrid.h" +#include "UICollection.h" +// +//class UIFrame: public UIDrawable +//{ +//public: +// UIFrame(float, float, float, float); +// UIFrame(); +// ~UIFrame(); +// sf::RectangleShape box; +// // todone: why does UIFrame have x,y,w,h AND a box? Which one should be used for bounds checks? (floats removed) +// +// //Simulate RectangleShape +// //float x, y, w, h, +// float outline; +// std::shared_ptr>> children; +// void render(sf::Vector2f) override final; +// void move(sf::Vector2f); +// +// PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UIFrame; }; +// virtual UIDrawable* click_at(sf::Vector2f point) override final; +// /* +// sf::Color fillColor(); // getter +// void fillColor(sf::Color c); // C++ setter +// void fillColor(PyColorObject* pyColor); // Python setter +// +// sf::Color outlineColor(); // getter +// void outlineColor(sf::Color c); // C++ setter +// void outlineColor(PyColorObject* pyColor); // Python setter +// */ +// +//private: +// //std::shared_ptr fillColor, outlineColor; +// /* +// sf::Color *_fillColor, *_outlineColor; +// PyColorObject *pyFillColor, *pyOutlineColor; +// */ +//}; +// +//class UICaption: public UIDrawable +//{ +//public: +// sf::Text text; +// void render(sf::Vector2f) override final; +// PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UICaption; }; +// virtual UIDrawable* click_at(sf::Vector2f point) override final; +//}; +// +//class UISprite: public UIDrawable +//{ +//private: +// int sprite_index; +// sf::Sprite sprite; +//protected: +// std::shared_ptr ptex; +//public: +// UISprite(); +// //UISprite(IndexTexture*, int, float, float, float); +// //UISprite(IndexTexture*, int, sf::Vector2f, float); +// UISprite(std::shared_ptr, int, sf::Vector2f, float); +// void update(); +// void render(sf::Vector2f) override final; +// virtual UIDrawable* click_at(sf::Vector2f point) override final; +// +// // 7DRL hack - TODO apply RenderTexture concept to all UIDrawables (via `sf::RenderTarget`) +// void render(sf::Vector2f, sf::RenderTexture&); +// //IndexTexture* itex; +// //sf::Vector2f pos; +// //float scale; +// //void setPosition(float, float); +// void setPosition(sf::Vector2f); +// sf::Vector2f getPosition(); +// void setScale(sf::Vector2f); +// sf::Vector2f getScale(); +// void setSpriteIndex(int); +// int getSpriteIndex(); +// +// void setTexture(std::shared_ptr _ptex, int _sprite_index=-1); +// std::shared_ptr getTexture(); +// +// PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UISprite; }; +//}; +// // UIGridPoint - revised grid data for each point -class UIGridPoint -{ -public: - sf::Color color, color_overlay; - bool walkable, transparent; - int tilesprite, tile_overlay, uisprite; - UIGridPoint(); -}; - +//class UIGridPoint +//{ +//public: +// sf::Color color, color_overlay; +// bool walkable, transparent; +// int tilesprite, tile_overlay, uisprite; +// UIGridPoint(); +//}; +// // UIGridPointState - entity-specific info for each cell -class UIGridPointState -{ -public: - bool visible, discovered; -}; - -class UIGrid; - +//class UIGridPointState +//{ +//public: +// bool visible, discovered; +//}; +// +//class UIGrid; +// // TODO: make UIEntity a drawable(?) Maybe just rely on UISprite/UIGrid to // somehow properly render the thing? Poorly designed interface -class UIEntity//: public UIDrawable -{ -public: - //PyObject* self; - std::shared_ptr grid; - std::vector gridstate; - UISprite sprite; - sf::Vector2f position; //(x,y) in grid coordinates; float for animation - void render(sf::Vector2f); //override final; - - UIEntity(); - UIEntity(UIGrid&); - -}; - -class UIGrid: public UIDrawable -{ -private: - std::shared_ptr ptex; -public: - UIGrid(); - //UIGrid(int, int, IndexTexture*, float, float, float, float); - UIGrid(int, int, std::shared_ptr, sf::Vector2f, sf::Vector2f); - void update(); - void render(sf::Vector2f) override final; - UIGridPoint& at(int, int); - PyObjectsEnum derived_type() override final; - //void setSprite(int); - virtual UIDrawable* click_at(sf::Vector2f point) override final; - - int grid_x, grid_y; - //int grid_size; // grid sizes are implied by IndexTexture now - sf::RectangleShape box; - float center_x, center_y, zoom; - //IndexTexture* itex; - std::shared_ptr getTexture(); - sf::Sprite sprite, output; - sf::RenderTexture renderTexture; - std::vector points; - std::shared_ptr>> entities; -}; - -/* -template -struct CPythonSharedObject { - PyObject_HEAD - std::shared_ptr data; -}; - -typedef CPythonSharedObject PyUIFrameObject; -*/ - -typedef struct { - PyObject_HEAD - std::shared_ptr data; -} PyUIFrameObject; - -typedef struct { - PyObject_HEAD - std::shared_ptr data; - PyObject* font; -} PyUICaptionObject; - -typedef struct { - PyObject_HEAD - std::shared_ptr data; - //PyObject* texture; -} PyUISpriteObject; - -typedef struct { - PyObject_HEAD - std::shared_ptr>> data; -} PyUICollectionObject; - -typedef struct { - PyObject_HEAD - UIGridPoint* data; - std::shared_ptr grid; -} PyUIGridPointObject; - -typedef struct { - PyObject_HEAD - UIGridPointState* data; - std::shared_ptr grid; - std::shared_ptr entity; -} PyUIGridPointStateObject; - -typedef struct { - PyObject_HEAD - std::shared_ptr data; - //PyObject* texture; -} PyUIEntityObject; - -typedef struct { - PyObject_HEAD - std::shared_ptr data; - //PyObject* texture; -} PyUIGridObject; - -namespace mcrfpydef { - //PyObject* py_instance(std::shared_ptr source); - // This function segfaults on tp_alloc for an unknown reason, but works inline with mcrfpydef:: methods. - -#define RET_PY_INSTANCE(target) { \ -switch (target->derived_type()) \ -{ \ - case PyObjectsEnum::UIFRAME: \ - { \ - PyUIFrameObject* o = (PyUIFrameObject*)((&PyUIFrameType)->tp_alloc(&PyUIFrameType, 0)); \ - if (o) \ - { \ - auto p = std::static_pointer_cast(target); \ - o->data = p; \ - auto utarget = o->data; \ - } \ - return (PyObject*)o; \ - } \ - case PyObjectsEnum::UICAPTION: \ - { \ - PyUICaptionObject* o = (PyUICaptionObject*)((&PyUICaptionType)->tp_alloc(&PyUICaptionType, 0)); \ - if (o) \ - { \ - auto p = std::static_pointer_cast(target); \ - o->data = p; \ - auto utarget = o->data; \ - } \ - return (PyObject*)o; \ - } \ - case PyObjectsEnum::UISPRITE: \ - { \ - PyUISpriteObject* o = (PyUISpriteObject*)((&PyUISpriteType)->tp_alloc(&PyUISpriteType, 0)); \ - if (o) \ - { \ - auto p = std::static_pointer_cast(target); \ - o->data = p; \ - auto utarget = o->data; \ - } \ - return (PyObject*)o; \ - } \ - case PyObjectsEnum::UIGRID: \ - { \ - PyUIGridObject* o = (PyUIGridObject*)((&PyUIGridType)->tp_alloc(&PyUIGridType, 0)); \ - if (o) \ - { \ - auto p = std::static_pointer_cast(target); \ - o->data = p; \ - auto utarget = o->data; \ - } \ - return (PyObject*)o; \ - } \ -} \ -} +//class UIEntity//: public UIDrawable +//{ +//public: +// //PyObject* self; +// std::shared_ptr grid; +// std::vector gridstate; +// UISprite sprite; +// sf::Vector2f position; //(x,y) in grid coordinates; float for animation +// void render(sf::Vector2f); //override final; +// +// UIEntity(); +// UIEntity(UIGrid&); +// +//}; +// +//class UIGrid: public UIDrawable +//{ +//private: +// std::shared_ptr ptex; +//public: +// UIGrid(); +// //UIGrid(int, int, IndexTexture*, float, float, float, float); +// UIGrid(int, int, std::shared_ptr, sf::Vector2f, sf::Vector2f); +// void update(); +// void render(sf::Vector2f) override final; +// UIGridPoint& at(int, int); +// PyObjectsEnum derived_type() override final; +// //void setSprite(int); +// virtual UIDrawable* click_at(sf::Vector2f point) override final; +// +// int grid_x, grid_y; +// //int grid_size; // grid sizes are implied by IndexTexture now +// sf::RectangleShape box; +// float center_x, center_y, zoom; +// //IndexTexture* itex; +// std::shared_ptr getTexture(); +// sf::Sprite sprite, output; +// sf::RenderTexture renderTexture; +// std::vector points; +// std::shared_ptr>> entities; +//}; +// +///* +//template +//struct CPythonSharedObject { +// PyObject_HEAD +// std::shared_ptr data; +//}; +// +//typedef CPythonSharedObject PyUIFrameObject; +//*/ +// +//typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +//} PyUIFrameObject; +// +//typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// PyObject* font; +//} PyUICaptionObject; +// +//typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// //PyObject* texture; +//} PyUISpriteObject; +// +//typedef struct { +// PyObject_HEAD +// std::shared_ptr>> data; +//} PyUICollectionObject; +// +//typedef struct { +// PyObject_HEAD +// UIGridPoint* data; +// std::shared_ptr grid; +//} PyUIGridPointObject; +// +//typedef struct { +// PyObject_HEAD +// UIGridPointState* data; +// std::shared_ptr grid; +// std::shared_ptr entity; +//} PyUIGridPointStateObject; +// +//typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// //PyObject* texture; +//} PyUIEntityObject; +// +//typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// //PyObject* texture; +//} PyUIGridObject; +// +//namespace mcrfpydef { +// //PyObject* py_instance(std::shared_ptr source); +// // This function segfaults on tp_alloc for an unknown reason, but works inline with mcrfpydef:: methods. +// +//#define RET_PY_INSTANCE(target) { \ +//switch (target->derived_type()) \ +//{ \ +// case PyObjectsEnum::UIFRAME: \ +// { \ +// PyUIFrameObject* o = (PyUIFrameObject*)((&PyUIFrameType)->tp_alloc(&PyUIFrameType, 0)); \ +// if (o) \ +// { \ +// auto p = std::static_pointer_cast(target); \ +// o->data = p; \ +// auto utarget = o->data; \ +// } \ +// return (PyObject*)o; \ +// } \ +// case PyObjectsEnum::UICAPTION: \ +// { \ +// PyUICaptionObject* o = (PyUICaptionObject*)((&PyUICaptionType)->tp_alloc(&PyUICaptionType, 0)); \ +// if (o) \ +// { \ +// auto p = std::static_pointer_cast(target); \ +// o->data = p; \ +// auto utarget = o->data; \ +// } \ +// return (PyObject*)o; \ +// } \ +// case PyObjectsEnum::UISPRITE: \ +// { \ +// PyUISpriteObject* o = (PyUISpriteObject*)((&PyUISpriteType)->tp_alloc(&PyUISpriteType, 0)); \ +// if (o) \ +// { \ +// auto p = std::static_pointer_cast(target); \ +// o->data = p; \ +// auto utarget = o->data; \ +// } \ +// return (PyObject*)o; \ +// } \ +// case PyObjectsEnum::UIGRID: \ +// { \ +// PyUIGridObject* o = (PyUIGridObject*)((&PyUIGridType)->tp_alloc(&PyUIGridType, 0)); \ +// if (o) \ +// { \ +// auto p = std::static_pointer_cast(target); \ +// o->data = p; \ +// auto utarget = o->data; \ +// } \ +// return (PyObject*)o; \ +// } \ +//} \ +//} // end macro definition - - // Color Definitions - // struct, members, new, set_member, PyTypeObject - - /* for reference: the structs to implement - typedef struct { - PyObject_HEAD - std::shared_ptr data; - } PyColorObject; - - typedef struct { - PyObject_HEAD - std::shared_ptr data; - } PyUIFrameObject; - - typedef struct { - PyObject_HEAD - std::shared_ptr data; - } PyUICaptionObject; - - typedef struct { - PyObject_HEAD - std::shared_ptr data; - } PyUISpriteObject; - */ - +// +// // Color Definitions +// // struct, members, new, set_member, PyTypeObject +// +// /* for reference: the structs to implement +// typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// } PyColorObject; +// +// typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// } PyUIFrameObject; +// +// typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// } PyUICaptionObject; +// +// typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// } PyUISpriteObject; +// */ +// // // Clickable / Callable Object Assignment // -static PyObject* PyUIDrawable_get_click(PyUIGridObject* self, void* closure) { - PyObjectsEnum objtype = static_cast(reinterpret_cast(closure)); // trust me bro, it's an Enum - PyObject* ptr; - - switch (objtype) - { - case PyObjectsEnum::UIFRAME: - ptr = ((PyUIFrameObject*)self)->data->click_callable->borrow(); - break; - case PyObjectsEnum::UICAPTION: - ptr = ((PyUICaptionObject*)self)->data->click_callable->borrow(); - break; - case PyObjectsEnum::UISPRITE: - ptr = ((PyUISpriteObject*)self)->data->click_callable->borrow(); - break; - case PyObjectsEnum::UIGRID: - ptr = ((PyUIGridObject*)self)->data->click_callable->borrow(); - break; - default: - PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _get_click"); - return NULL; - } - if (ptr && ptr != Py_None) - return ptr; - else - return Py_None; -} - -static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* closure) { - PyObjectsEnum objtype = static_cast(reinterpret_cast(closure)); // trust me bro, it's an Enum - UIDrawable* target; - switch (objtype) - { - case PyObjectsEnum::UIFRAME: - target = (((PyUIFrameObject*)self)->data.get()); - break; - case PyObjectsEnum::UICAPTION: - target = (((PyUICaptionObject*)self)->data.get()); - break; - case PyObjectsEnum::UISPRITE: - target = (((PyUISpriteObject*)self)->data.get()); - break; - case PyObjectsEnum::UIGRID: - target = (((PyUIGridObject*)self)->data.get()); - break; - default: - PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _set_click"); - return -1; - } - - if (value == Py_None) - { - target->click_unregister(); - } else { - target->click_register(value); - } - return 0; -} - +//static PyObject* PyUIDrawable_get_click(PyUIGridObject* self, void* closure) { +// PyObjectsEnum objtype = static_cast(reinterpret_cast(closure)); // trust me bro, it's an Enum +// PyObject* ptr; +// +// switch (objtype) +// { +// case PyObjectsEnum::UIFRAME: +// ptr = ((PyUIFrameObject*)self)->data->click_callable->borrow(); +// break; +// case PyObjectsEnum::UICAPTION: +// ptr = ((PyUICaptionObject*)self)->data->click_callable->borrow(); +// break; +// case PyObjectsEnum::UISPRITE: +// ptr = ((PyUISpriteObject*)self)->data->click_callable->borrow(); +// break; +// case PyObjectsEnum::UIGRID: +// ptr = ((PyUIGridObject*)self)->data->click_callable->borrow(); +// break; +// default: +// PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _get_click"); +// return NULL; +// } +// if (ptr && ptr != Py_None) +// return ptr; +// else +// return Py_None; +//} +// +//static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* closure) { +// PyObjectsEnum objtype = static_cast(reinterpret_cast(closure)); // trust me bro, it's an Enum +// UIDrawable* target; +// switch (objtype) +// { +// case PyObjectsEnum::UIFRAME: +// target = (((PyUIFrameObject*)self)->data.get()); +// break; +// case PyObjectsEnum::UICAPTION: +// target = (((PyUICaptionObject*)self)->data.get()); +// break; +// case PyObjectsEnum::UISPRITE: +// target = (((PyUISpriteObject*)self)->data.get()); +// break; +// case PyObjectsEnum::UIGRID: +// target = (((PyUIGridObject*)self)->data.get()); +// break; +// default: +// PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _set_click"); +// return -1; +// } +// +// if (value == Py_None) +// { +// target->click_unregister(); +// } else { +// target->click_register(value); +// } +// return 0; +//} +// // End Clickability implementation - - - /* - * - * Begin PyFontType defs - * - */ - /* - typedef struct { - PyObject_HEAD - std::shared_ptr data; - } PyFontObject; - - static int PyFont_init(PyFontObject* self, PyObject* args, PyObject* kwds) - { - //std::cout << "Init called\n"; - static const char* keywords[] = { "filename", nullptr }; - char* filename; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", const_cast(keywords), &filename)) - { - return -1; - } - self->data->loadFromFile((std::string)filename); - return 0; - } - - static PyTypeObject PyFontType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.Font", - .tp_basicsize = sizeof(PyFontObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("SFML Font Object"), - .tp_init = (initproc)PyFont_init, - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - PyFontObject* self = (PyFontObject*)type->tp_alloc(type, 0); - self->data = std::make_shared(); - return (PyObject*)self; - } - }; - */ - - /* - * - * End PyFontType defs - * - */ - - +// +// +// /* +// * +// * Begin PyFontType defs +// * +// */ +// /* +// typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// } PyFontObject; +// +// static int PyFont_init(PyFontObject* self, PyObject* args, PyObject* kwds) +// { +// //std::cout << "Init called\n"; +// static const char* keywords[] = { "filename", nullptr }; +// char* filename; +// +// if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", const_cast(keywords), &filename)) +// { +// return -1; +// } +// self->data->loadFromFile((std::string)filename); +// return 0; +// } +// +// static PyTypeObject PyFontType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.Font", +// .tp_basicsize = sizeof(PyFontObject), +// .tp_itemsize = 0, +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("SFML Font Object"), +// .tp_init = (initproc)PyFont_init, +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// PyFontObject* self = (PyFontObject*)type->tp_alloc(type, 0); +// self->data = std::make_shared(); +// return (PyObject*)self; +// } +// }; +// */ +// +// /* +// * +// * End PyFontType defs +// * +// */ +// +// // static PyObject* PyColor_get_member(PyColorObject* self, void* closure) // { // auto member_ptr = reinterpret_cast(closure); @@ -537,1978 +503,1978 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c // return (PyObject*)self; // } // }; - - /* - * - * Begin template generation for PyUICaptionType - * - */ - - static PyObject* PyUICaption_get_float_member(PyUICaptionObject* self, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - if (member_ptr == 0) - return PyFloat_FromDouble(self->data->text.getPosition().x); - else if (member_ptr == 1) - return PyFloat_FromDouble(self->data->text.getPosition().y); - else if (member_ptr == 4) - return PyFloat_FromDouble(self->data->text.getOutlineThickness()); - else if (member_ptr == 5) - return PyLong_FromLong(self->data->text.getCharacterSize()); - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - } - - static int PyUICaption_set_float_member(PyUICaptionObject* 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 an integer."); - return -1; - } - if (member_ptr == 0) //x - self->data->text.setPosition(val, self->data->text.getPosition().y); - else if (member_ptr == 1) //y - self->data->text.setPosition(self->data->text.getPosition().x, val); - else if (member_ptr == 4) //outline - self->data->text.setOutlineThickness(val); - else if (member_ptr == 5) // character size - self->data->text.setCharacterSize(val); - return 0; - } - - static PyObject* PyUICaption_get_vec_member(PyUICaptionObject* self, void* closure) - { - return PyVector(self->data->text.getPosition()).pyObject(); - } - - static int PyUICaption_set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure) - { - self->data->text.setPosition(PyVector::fromPy(value)); - return 0; - } - - static PyObject* PyUICaption_get_color_member(PyUICaptionObject* self, void* closure) - { - // validate closure (should be impossible to be wrong, but it's thorough) - auto member_ptr = reinterpret_cast(closure); - if (member_ptr != 0 && member_ptr != 1) - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - - // TODO: manually calling tp_alloc to create a PyColorObject seems like an antipattern - /* - PyTypeObject* colorType = &PyLinkedColorType; - PyObject* pyColor = colorType->tp_alloc(colorLinkedType, 0); - if (pyColor == NULL) - { - std::cout << "failure to allocate mcrfpy.LinkedColor / PyLinkedColorType" << std::endl; - return NULL; - } - PyColorObject* pyColorObj = reinterpret_cast(pyColor); - */ - - // fetch correct member data - sf::Color color; - //sf::Color (*cgetter)(); - //void (*csetter)(sf::Color); - //std::function csetter; - //std::function cgetter; - if (member_ptr == 0) - { - color = self->data->text.getFillColor(); - //return Py_BuildValue("(iii)", color.r, color.g, color.b); - //csetter = &self->data->text.setFillColor; - //cgetter = &self->data->text.getFillColor; - //csetter = [s = self->data](sf::Color c){s->text.setFillColor(c);}; - //cgetter = [s = self->data](){return s->text.getFillColor();}; - } - else if (member_ptr == 1) - { - color = self->data->text.getOutlineColor(); - //return Py_BuildValue("(iii)", color.r, color.g, color.b); - //csetter = &self->data->text.setOutlineColor; - //cgetter = &self->data->text.getOutlineColor; - //csetter = [s = self->data](sf::Color c){s->text.setOutlineColor(c);}; - //cgetter = [s = self->data](){return s->text.getOutlineColor();}; - } - - // initialize new mcrfpy.Color instance - //pyColorObj->data = std::make_shared(color); - //PyLinkedColor::fromPy(pyColorObj).set(color); - //auto linkedcolor = PyLinkedColor(csetter, cgetter, self->data, member_ptr); - //linkedcolor.set(color); // don't need to set a linked color! - - //return pyColor; - return PyColor(color).pyObject(); - } - - static int PyUICaption_set_color_member(PyUICaptionObject* self, PyObject* value, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse - int r, g, b, a; - if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) - { - // get value from mcrfpy.Color instance - /* - PyColorObject* color = reinterpret_cast(value); - r = color->data->r; - g = color->data->g; - b = color->data->b; - a = color->data->a; - */ - //std::cout << "Build LinkedColor" << std::endl; - //auto lc = PyLinkedColor::fromPy(value); - auto c = ((PyColorObject*)value)->data; - //std::cout << "Fetch value" << std::endl; - //auto c = lc.get(); - r = c.r; g = c.g; b = c.b; a = c.a; - std::cout << "got " << int(r) << ", " << int(g) << ", " << int(b) << ", " << int(a) << std::endl; - } - else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) - { - // reject non-Color, non-tuple value - PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); - return -1; - } - else // get value from tuples - { - r = PyLong_AsLong(PyTuple_GetItem(value, 0)); - g = PyLong_AsLong(PyTuple_GetItem(value, 1)); - b = PyLong_AsLong(PyTuple_GetItem(value, 2)); - a = 255; - - if (PyTuple_Size(value) == 4) - { - a = PyLong_AsLong(PyTuple_GetItem(value, 3)); - } - } - - if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) - { - PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); - return -1; - } - - if (member_ptr == 0) - { - self->data->text.setFillColor(sf::Color(r, g, b, a)); - } - else if (member_ptr == 1) - { - self->data->text.setOutlineColor(sf::Color(r, g, b, a)); - } - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return -1; - } - - return 0; - } - - static PyObject* PyUICaption_get_text(PyUICaptionObject* self, void* closure) - { - Resources::caption_buffer = self->data->text.getString(); - return PyUnicode_FromString(Resources::caption_buffer.c_str()); - } - - static int PyUICaption_set_text(PyUICaptionObject* self, PyObject* value, void* closure) - { - PyObject* s = PyObject_Str(value); - PyObject * temp_bytes = PyUnicode_AsEncodedString(s, "UTF-8", "strict"); // Owned reference - if (temp_bytes != NULL) { - Resources::caption_buffer = PyBytes_AS_STRING(temp_bytes); // Borrowed pointer - Py_DECREF(temp_bytes); - } - self->data->text.setString(Resources::caption_buffer); - return 0; - } - - static PyGetSetDef PyUICaption_getsetters[] = { - {"x", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "X coordinate of top-left corner", (void*)0}, - {"y", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Y coordinate of top-left corner", (void*)1}, - {"pos", (getter)PyUICaption_get_vec_member, (setter)PyUICaption_set_vec_member, "(x, y) vector", (void*)0}, - //{"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, - //{"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, - {"outline", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Thickness of the border", (void*)4}, - {"fill_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Fill color of the text", (void*)0}, - {"outline_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Outline color of the text", (void*)1}, - //{"children", (getter)PyUIFrame_get_children, NULL, "UICollection of objects on top of this one", NULL}, - {"text", (getter)PyUICaption_get_text, (setter)PyUICaption_set_text, "The text displayed", NULL}, - {"size", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Text size (integer) in points", (void*)5}, - {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UICAPTION}, - {NULL} - }; - - static PyObject* PyUICaption_repr(PyUICaptionObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - auto text = self->data->text; - auto fc = text.getFillColor(); - auto oc = text.getOutlineColor(); - ss << ""; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - static int PyUICaption_init(PyUICaptionObject* self, PyObject* args, PyObject* kwds) - { - //std::cout << "Init called\n"; - static const char* keywords[] = { "x", "y", "text", "font", "fill_color", "outline_color", nullptr }; - float x = 0.0f, y = 0.0f; - char* text; - PyObject* font, fill_color, outline_color; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffzOOO", - const_cast(keywords), &x, &y, &text, &font, &fill_color, &outline_color)) - { - return -1; - } - - // check types for font, fill_color, outline_color - // - // Set Font - // - std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl; - if (font != NULL && !PyObject_IsInstance(font, (PyObject*)&PyFontType)){ - PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance"); - return -1; - } else if (font != NULL) - { - auto font_obj = (PyFontObject*)font; - self->data->text.setFont(font_obj->data->font); - self->font = font; - Py_INCREF(font); - } else - { - // default font - //self->data->text.setFont(Resources::game->getFont()); - } - - // - // Set Color - // - self->data->text.setPosition(sf::Vector2f(x, y)); - self->data->text.setString((std::string)text); - self->data->text.setFillColor(sf::Color(0,0,0,255)); - self->data->text.setOutlineColor(sf::Color(128,128,128,255)); - - return 0; - } - - static PyTypeObject PyUICaptionType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.Caption", - .tp_basicsize = sizeof(PyUICaptionObject), - .tp_itemsize = 0, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUICaptionObject* obj = (PyUICaptionObject*)self; - // release reference to font object - if (obj->font) Py_DECREF(obj->font); - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .tp_repr = (reprfunc)PyUICaption_repr, - //.tp_hash = NULL, - //.tp_iter - //.tp_iternext - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("docstring"), - //.tp_methods = PyUIFrame_methods, - //.tp_members = PyUIFrame_members, - .tp_getset = PyUICaption_getsetters, - //.tp_base = NULL, - .tp_init = (initproc)PyUICaption_init, - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - PyUICaptionObject* self = (PyUICaptionObject*)type->tp_alloc(type, 0); - if (self) self->data = std::make_shared(); - return (PyObject*)self; - } - }; - - /* - * - * End PyUICaptionType generation - * - */ - - - /* - * - * Begin template generation for PyUIFrameType - * - */ - - static PyObject* PyUIFrame_get_float_member(PyUIFrameObject* self, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - if (member_ptr == 0) - return PyFloat_FromDouble(self->data->box.getPosition().x); - else if (member_ptr == 1) - return PyFloat_FromDouble(self->data->box.getPosition().y); - else if (member_ptr == 2) - return PyFloat_FromDouble(self->data->box.getSize().x); - else if (member_ptr == 3) - return PyFloat_FromDouble(self->data->box.getSize().y); - else if (member_ptr == 4) - return PyFloat_FromDouble(self->data->box.getOutlineThickness()); - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - } - - static int PyUIFrame_set_float_member(PyUIFrameObject* 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 an integer."); - 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) //outline - self->data->box.setOutlineThickness(val); - return 0; - } - - static PyObject* PyUIFrame_get_color_member(PyUIFrameObject* self, void* closure) - { - // validate closure (should be impossible to be wrong, but it's thorough) - auto member_ptr = reinterpret_cast(closure); - if (member_ptr != 0 && member_ptr != 1) - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - PyTypeObject* colorType = &PyColorType; - PyObject* pyColor = colorType->tp_alloc(colorType, 0); - if (pyColor == NULL) - { - std::cout << "failure to allocate mcrfpy.Color / PyColorType" << std::endl; - return NULL; - } - PyColorObject* pyColorObj = reinterpret_cast(pyColor); - - // fetch correct member data - sf::Color color; - if (member_ptr == 0) - { - color = self->data->box.getFillColor(); - //return Py_BuildValue("(iii)", color.r, color.g, color.b); - } - else if (member_ptr == 1) - { - color = self->data->box.getOutlineColor(); - //return Py_BuildValue("(iii)", color.r, color.g, color.b); - } - - // initialize new mcrfpy.Color instance - //pyColorObj->data = std::make_shared(color); - //PyColor::fromPy(pyColorObj).set(color); - - // TODO - supposed to return Linked - return PyColor(color).pyObject(); - } - - static int PyUIFrame_set_color_member(PyUIFrameObject* self, PyObject* value, void* closure) - { - //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse - auto member_ptr = reinterpret_cast(closure); - int r, g, b, a; - if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) - { - // get value from mcrfpy.Color instance - /* - PyColorObject* color = reinterpret_cast(value); - r = color->data->r; - g = color->data->g; - b = color->data->b; - a = color->data->a; - std::cout << "using color: " << r << " " << g << " " << b << " " << a << std::endl; - */ - sf::Color c = ((PyColorObject*)value)->data; - r = c.r; g = c.g; b = c.b; a = c.a; - } - else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) - { - // reject non-Color, non-tuple value - PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); - return -1; - } - else // get value from tuples - { - r = PyLong_AsLong(PyTuple_GetItem(value, 0)); - g = PyLong_AsLong(PyTuple_GetItem(value, 1)); - b = PyLong_AsLong(PyTuple_GetItem(value, 2)); - a = 255; - - if (PyTuple_Size(value) == 4) - { - a = PyLong_AsLong(PyTuple_GetItem(value, 3)); - } - } - - if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) - { - PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); - return -1; - } - - if (member_ptr == 0) - { - self->data->box.setFillColor(sf::Color(r, g, b, a)); - } - else if (member_ptr == 1) - { - self->data->box.setOutlineColor(sf::Color(r, g, b, a)); - } - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return -1; - } - - return 0; - } - - static PyObject* PyUIFrame_get_children(PyUIFrameObject*, void*); - // implementation after the PyUICollectionType definition - - static PyGetSetDef PyUIFrame_getsetters[] = { - {"x", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "X coordinate of top-left corner", (void*)0}, - {"y", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Y coordinate of top-left corner", (void*)1}, - {"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, - {"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, - {"outline", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Thickness of the border", (void*)4}, - {"fill_color", (getter)PyUIFrame_get_color_member, (setter)PyUIFrame_set_color_member, "Fill color of the rectangle", (void*)0}, - {"outline_color", (getter)PyUIFrame_get_color_member, (setter)PyUIFrame_set_color_member, "Outline color of the rectangle", (void*)1}, - {"children", (getter)PyUIFrame_get_children, NULL, "UICollection of objects on top of this one", NULL}, - {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIFRAME}, - {NULL} - }; - - static PyObject* PyUIFrame_repr(PyUIFrameObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - auto box = self->data->box; - auto fc = box.getFillColor(); - auto oc = box.getOutlineColor(); - ss << "data->children->size() << " child objects" << - ")>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - static int PyUIFrame_init(PyUIFrameObject* self, PyObject* args, PyObject* kwds) - { - //std::cout << "Init called\n"; - static const char* keywords[] = { "x", "y", "w", "h", "fill_color", "outline_color", "outline", nullptr }; - float x = 0.0f, y = 0.0f, w = 0.0f, h=0.0f, outline=0.0f; - PyObject* fill_color = 0; - PyObject* outline_color = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffff|OOf", const_cast(keywords), &x, &y, &w, &h, &fill_color, &outline_color, &outline)) - { - return -1; - } - - //self->data->x = x; - //self->data->y = y; - self->data->box.setPosition(sf::Vector2f(x, y)); - self->data->box.setSize(sf::Vector2f(w, h)); - self->data->box.setOutlineThickness(outline); - // getsetter abuse because I haven't standardized Color object parsing (TODO) - int err_val = 0; - if (fill_color && fill_color != Py_None) err_val = PyUIFrame_set_color_member(self, fill_color, (void*)0); - else self->data->box.setFillColor(sf::Color(0,0,0,255)); - if (err_val) return err_val; - if (outline_color && outline_color != Py_None) err_val = PyUIFrame_set_color_member(self, outline_color, (void*)1); - else self->data->box.setOutlineColor(sf::Color(128,128,128,255)); - if (err_val) return err_val; - return 0; - } - - static PyTypeObject PyUIFrameType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.Frame", - .tp_basicsize = sizeof(PyUIFrameObject), - .tp_itemsize = 0, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUIFrameObject* obj = (PyUIFrameObject*)self; - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .tp_repr = (reprfunc)PyUIFrame_repr, - //.tp_hash = NULL, - //.tp_iter - //.tp_iternext - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("docstring"), - //.tp_methods = PyUIFrame_methods, - //.tp_members = PyUIFrame_members, - .tp_getset = PyUIFrame_getsetters, - //.tp_base = NULL, - .tp_init = (initproc)PyUIFrame_init, - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - PyUIFrameObject* self = (PyUIFrameObject*)type->tp_alloc(type, 0); - if (self) self->data = std::make_shared(); - return (PyObject*)self; - } - }; - - /* - * - * End auto-generated PyUIFrameType generation - * - */ - - /* - * - * Begin Python Class Instantiator (iterator helper) - * - */ - - /* // definition can't go in the header file - PyObject* py_instance(UIDrawable* obj) - { - - } - */ - - /* - * - * End Python Class Instantitator (iterator helper) - * - */ - - /* - * - * Begin template generation for PyUISpriteType - * - */ - - static PyObject* PyUISprite_get_float_member(PyUISpriteObject* self, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - if (member_ptr == 0) - return PyFloat_FromDouble(self->data->getPosition().x); - else if (member_ptr == 1) - return PyFloat_FromDouble(self->data->getPosition().y); - else if (member_ptr == 2) - return PyFloat_FromDouble(self->data->getScale().x); // scale X and Y are identical, presently - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - } - - - static int PyUISprite_set_float_member(PyUISpriteObject* 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->setPosition(sf::Vector2f(val, self->data->getPosition().y)); - else if (member_ptr == 1) //y - self->data->setPosition(sf::Vector2f(self->data->getPosition().x, val)); - else if (member_ptr == 2) // scale - self->data->setScale(sf::Vector2f(val, val)); - return 0; - } - - static PyObject* PyUISprite_get_int_member(PyUISpriteObject* self, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - if (true) {} - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - - return PyLong_FromDouble(self->data->getSpriteIndex()); - } - - - static int PyUISprite_set_int_member(PyUISpriteObject* self, PyObject* value, void* closure) - { - int val; - auto member_ptr = reinterpret_cast(closure); - if (PyLong_Check(value)) - { - val = PyLong_AsLong(value); - } - else - { - PyErr_SetString(PyExc_TypeError, "Value must be an integer."); - return -1; - } - //self->data->sprite_index = val; - //self->data->sprite.setTextureRect(self->data->itex->spriteCoordinates(val)); - self->data->setSpriteIndex(val); - return 0; - } - - static PyObject* PyUISprite_get_texture(PyUISpriteObject* self, void* closure) - { - return self->data->getTexture()->pyObject(); - } - - static int PyUISprite_set_texture(PyUISpriteObject* self, PyObject* value, void* closure) - { - return -1; - } - - static PyGetSetDef PyUISprite_getsetters[] = { - {"x", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "X coordinate of top-left corner", (void*)0}, - {"y", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "Y coordinate of top-left corner", (void*)1}, - {"scale", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "Size factor", (void*)2}, - {"sprite_number", (getter)PyUISprite_get_int_member, (setter)PyUISprite_set_int_member, "Which sprite on the texture is shown", NULL}, - {"texture", (getter)PyUISprite_get_texture, (setter)PyUISprite_set_texture, "Texture object", NULL}, - {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UISPRITE}, - {NULL} - }; - - static PyObject* PyUISprite_repr(PyUISpriteObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - //auto sprite = self->data->sprite; - ss << ""; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - static int PyUISprite_init(PyUISpriteObject* self, PyObject* args, PyObject* kwds) - { - //std::cout << "Init called\n"; - static const char* keywords[] = { "x", "y", "texture", "sprite_index", "scale", nullptr }; - float x = 0.0f, y = 0.0f, scale = 1.0f; - int sprite_index; - PyObject* texture; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffOif", - const_cast(keywords), &x, &y, &texture, &sprite_index, &scale)) - { - 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) // to be removed: UIObjects don't manage texture references - { - self->texture = texture; - Py_INCREF(texture); - } else - { - // default tex? - }*/ - auto pytexture = (PyTextureObject*)texture; - self->data = std::make_shared(pytexture->data, sprite_index, sf::Vector2f(x, y), scale); - self->data->setPosition(sf::Vector2f(x, y)); - - return 0; - } - - static PyTypeObject PyUISpriteType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.Sprite", - .tp_basicsize = sizeof(PyUISpriteObject), - .tp_itemsize = 0, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUISpriteObject* obj = (PyUISpriteObject*)self; - // release reference to font object - //if (obj->texture) Py_DECREF(obj->texture); - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .tp_repr = (reprfunc)PyUISprite_repr, - //.tp_hash = NULL, - //.tp_iter - //.tp_iternext - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("docstring"), - //.tp_methods = PyUIFrame_methods, - //.tp_members = PyUIFrame_members, - .tp_getset = PyUISprite_getsetters, - //.tp_base = NULL, - .tp_init = (initproc)PyUISprite_init, - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - PyUISpriteObject* self = (PyUISpriteObject*)type->tp_alloc(type, 0); - //if (self) self->data = std::make_shared(); - return (PyObject*)self; - } - }; - /* - * - * End template for PyUISpriteType - * - */ - - - - - /* - * - * PyUIGridPoint defs - * - */ - +// +// /* +// * +// * Begin template generation for PyUICaptionType +// * +// */ +// +// static PyObject* PyUICaption_get_float_member(PyUICaptionObject* self, void* closure) +// { +// auto member_ptr = reinterpret_cast(closure); +// if (member_ptr == 0) +// return PyFloat_FromDouble(self->data->text.getPosition().x); +// else if (member_ptr == 1) +// return PyFloat_FromDouble(self->data->text.getPosition().y); +// else if (member_ptr == 4) +// return PyFloat_FromDouble(self->data->text.getOutlineThickness()); +// else if (member_ptr == 5) +// return PyLong_FromLong(self->data->text.getCharacterSize()); +// else +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return nullptr; +// } +// } +// +// static int PyUICaption_set_float_member(PyUICaptionObject* 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 an integer."); +// return -1; +// } +// if (member_ptr == 0) //x +// self->data->text.setPosition(val, self->data->text.getPosition().y); +// else if (member_ptr == 1) //y +// self->data->text.setPosition(self->data->text.getPosition().x, val); +// else if (member_ptr == 4) //outline +// self->data->text.setOutlineThickness(val); +// else if (member_ptr == 5) // character size +// self->data->text.setCharacterSize(val); +// return 0; +// } +// +// static PyObject* PyUICaption_get_vec_member(PyUICaptionObject* self, void* closure) +// { +// return PyVector(self->data->text.getPosition()).pyObject(); +// } +// +// static int PyUICaption_set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure) +// { +// self->data->text.setPosition(PyVector::fromPy(value)); +// return 0; +// } +// +// static PyObject* PyUICaption_get_color_member(PyUICaptionObject* self, void* closure) +// { +// // validate closure (should be impossible to be wrong, but it's thorough) +// auto member_ptr = reinterpret_cast(closure); +// if (member_ptr != 0 && member_ptr != 1) +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return nullptr; +// } +// +// // TODO: manually calling tp_alloc to create a PyColorObject seems like an antipattern +// /* +// PyTypeObject* colorType = &PyLinkedColorType; +// PyObject* pyColor = colorType->tp_alloc(colorLinkedType, 0); +// if (pyColor == NULL) +// { +// std::cout << "failure to allocate mcrfpy.LinkedColor / PyLinkedColorType" << std::endl; +// return NULL; +// } +// PyColorObject* pyColorObj = reinterpret_cast(pyColor); +// */ +// +// // fetch correct member data +// sf::Color color; +// //sf::Color (*cgetter)(); +// //void (*csetter)(sf::Color); +// //std::function csetter; +// //std::function cgetter; +// if (member_ptr == 0) +// { +// color = self->data->text.getFillColor(); +// //return Py_BuildValue("(iii)", color.r, color.g, color.b); +// //csetter = &self->data->text.setFillColor; +// //cgetter = &self->data->text.getFillColor; +// //csetter = [s = self->data](sf::Color c){s->text.setFillColor(c);}; +// //cgetter = [s = self->data](){return s->text.getFillColor();}; +// } +// else if (member_ptr == 1) +// { +// color = self->data->text.getOutlineColor(); +// //return Py_BuildValue("(iii)", color.r, color.g, color.b); +// //csetter = &self->data->text.setOutlineColor; +// //cgetter = &self->data->text.getOutlineColor; +// //csetter = [s = self->data](sf::Color c){s->text.setOutlineColor(c);}; +// //cgetter = [s = self->data](){return s->text.getOutlineColor();}; +// } +// +// // initialize new mcrfpy.Color instance +// //pyColorObj->data = std::make_shared(color); +// //PyLinkedColor::fromPy(pyColorObj).set(color); +// //auto linkedcolor = PyLinkedColor(csetter, cgetter, self->data, member_ptr); +// //linkedcolor.set(color); // don't need to set a linked color! +// +// //return pyColor; +// return PyColor(color).pyObject(); +// } +// +// static int PyUICaption_set_color_member(PyUICaptionObject* self, PyObject* value, void* closure) +// { +// auto member_ptr = reinterpret_cast(closure); +// //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse +// int r, g, b, a; +// if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) +// { +// // get value from mcrfpy.Color instance +// /* +// PyColorObject* color = reinterpret_cast(value); +// r = color->data->r; +// g = color->data->g; +// b = color->data->b; +// a = color->data->a; +// */ +// //std::cout << "Build LinkedColor" << std::endl; +// //auto lc = PyLinkedColor::fromPy(value); +// auto c = ((PyColorObject*)value)->data; +// //std::cout << "Fetch value" << std::endl; +// //auto c = lc.get(); +// r = c.r; g = c.g; b = c.b; a = c.a; +// std::cout << "got " << int(r) << ", " << int(g) << ", " << int(b) << ", " << int(a) << std::endl; +// } +// else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) +// { +// // reject non-Color, non-tuple value +// PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); +// return -1; +// } +// else // get value from tuples +// { +// r = PyLong_AsLong(PyTuple_GetItem(value, 0)); +// g = PyLong_AsLong(PyTuple_GetItem(value, 1)); +// b = PyLong_AsLong(PyTuple_GetItem(value, 2)); +// a = 255; +// +// if (PyTuple_Size(value) == 4) +// { +// a = PyLong_AsLong(PyTuple_GetItem(value, 3)); +// } +// } +// +// if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) +// { +// PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); +// return -1; +// } +// +// if (member_ptr == 0) +// { +// self->data->text.setFillColor(sf::Color(r, g, b, a)); +// } +// else if (member_ptr == 1) +// { +// self->data->text.setOutlineColor(sf::Color(r, g, b, a)); +// } +// else +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return -1; +// } +// +// return 0; +// } +// +// static PyObject* PyUICaption_get_text(PyUICaptionObject* self, void* closure) +// { +// Resources::caption_buffer = self->data->text.getString(); +// return PyUnicode_FromString(Resources::caption_buffer.c_str()); +// } +// +// static int PyUICaption_set_text(PyUICaptionObject* self, PyObject* value, void* closure) +// { +// PyObject* s = PyObject_Str(value); +// PyObject * temp_bytes = PyUnicode_AsEncodedString(s, "UTF-8", "strict"); // Owned reference +// if (temp_bytes != NULL) { +// Resources::caption_buffer = PyBytes_AS_STRING(temp_bytes); // Borrowed pointer +// Py_DECREF(temp_bytes); +// } +// self->data->text.setString(Resources::caption_buffer); +// return 0; +// } +// +// static PyGetSetDef PyUICaption_getsetters[] = { +// {"x", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "X coordinate of top-left corner", (void*)0}, +// {"y", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Y coordinate of top-left corner", (void*)1}, +// {"pos", (getter)PyUICaption_get_vec_member, (setter)PyUICaption_set_vec_member, "(x, y) vector", (void*)0}, +// //{"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, +// //{"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, +// {"outline", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Thickness of the border", (void*)4}, +// {"fill_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Fill color of the text", (void*)0}, +// {"outline_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Outline color of the text", (void*)1}, +// //{"children", (getter)PyUIFrame_get_children, NULL, "UICollection of objects on top of this one", NULL}, +// {"text", (getter)PyUICaption_get_text, (setter)PyUICaption_set_text, "The text displayed", NULL}, +// {"size", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Text size (integer) in points", (void*)5}, +// {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UICAPTION}, +// {NULL} +// }; +// +// static PyObject* PyUICaption_repr(PyUICaptionObject* self) +// { +// std::ostringstream ss; +// if (!self->data) ss << ""; +// else { +// auto text = self->data->text; +// auto fc = text.getFillColor(); +// auto oc = text.getOutlineColor(); +// ss << ""; +// } +// std::string repr_str = ss.str(); +// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +// } +// +// static int PyUICaption_init(PyUICaptionObject* self, PyObject* args, PyObject* kwds) +// { +// //std::cout << "Init called\n"; +// static const char* keywords[] = { "x", "y", "text", "font", "fill_color", "outline_color", nullptr }; +// float x = 0.0f, y = 0.0f; +// char* text; +// PyObject* font, fill_color, outline_color; +// +// if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffzOOO", +// const_cast(keywords), &x, &y, &text, &font, &fill_color, &outline_color)) +// { +// return -1; +// } +// +// // check types for font, fill_color, outline_color +// // +// // Set Font +// // +// std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl; +// if (font != NULL && !PyObject_IsInstance(font, (PyObject*)&PyFontType)){ +// PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance"); +// return -1; +// } else if (font != NULL) +// { +// auto font_obj = (PyFontObject*)font; +// self->data->text.setFont(font_obj->data->font); +// self->font = font; +// Py_INCREF(font); +// } else +// { +// // default font +// //self->data->text.setFont(Resources::game->getFont()); +// } +// +// // +// // Set Color +// // +// self->data->text.setPosition(sf::Vector2f(x, y)); +// self->data->text.setString((std::string)text); +// self->data->text.setFillColor(sf::Color(0,0,0,255)); +// self->data->text.setOutlineColor(sf::Color(128,128,128,255)); +// +// return 0; +// } +// +// static PyTypeObject PyUICaptionType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.Caption", +// .tp_basicsize = sizeof(PyUICaptionObject), +// .tp_itemsize = 0, +// .tp_dealloc = (destructor)[](PyObject* self) +// { +// PyUICaptionObject* obj = (PyUICaptionObject*)self; +// // release reference to font object +// if (obj->font) Py_DECREF(obj->font); +// obj->data.reset(); +// Py_TYPE(self)->tp_free(self); +// }, +// .tp_repr = (reprfunc)PyUICaption_repr, +// //.tp_hash = NULL, +// //.tp_iter +// //.tp_iternext +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("docstring"), +// //.tp_methods = PyUIFrame_methods, +// //.tp_members = PyUIFrame_members, +// .tp_getset = PyUICaption_getsetters, +// //.tp_base = NULL, +// .tp_init = (initproc)PyUICaption_init, +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// PyUICaptionObject* self = (PyUICaptionObject*)type->tp_alloc(type, 0); +// if (self) self->data = std::make_shared(); +// return (PyObject*)self; +// } +// }; +// +// /* +// * +// * End PyUICaptionType generation +// * +// */ +// +// +// /* +// * +// * Begin template generation for PyUIFrameType +// * +// */ +// +// static PyObject* PyUIFrame_get_float_member(PyUIFrameObject* self, void* closure) +// { +// auto member_ptr = reinterpret_cast(closure); +// if (member_ptr == 0) +// return PyFloat_FromDouble(self->data->box.getPosition().x); +// else if (member_ptr == 1) +// return PyFloat_FromDouble(self->data->box.getPosition().y); +// else if (member_ptr == 2) +// return PyFloat_FromDouble(self->data->box.getSize().x); +// else if (member_ptr == 3) +// return PyFloat_FromDouble(self->data->box.getSize().y); +// else if (member_ptr == 4) +// return PyFloat_FromDouble(self->data->box.getOutlineThickness()); +// else +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return nullptr; +// } +// } +// +// static int PyUIFrame_set_float_member(PyUIFrameObject* 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 an integer."); +// 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) //outline +// self->data->box.setOutlineThickness(val); +// return 0; +// } +// +// static PyObject* PyUIFrame_get_color_member(PyUIFrameObject* self, void* closure) +// { +// // validate closure (should be impossible to be wrong, but it's thorough) +// auto member_ptr = reinterpret_cast(closure); +// if (member_ptr != 0 && member_ptr != 1) +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return nullptr; +// } +// PyTypeObject* colorType = &PyColorType; +// PyObject* pyColor = colorType->tp_alloc(colorType, 0); +// if (pyColor == NULL) +// { +// std::cout << "failure to allocate mcrfpy.Color / PyColorType" << std::endl; +// return NULL; +// } +// PyColorObject* pyColorObj = reinterpret_cast(pyColor); +// +// // fetch correct member data +// sf::Color color; +// if (member_ptr == 0) +// { +// color = self->data->box.getFillColor(); +// //return Py_BuildValue("(iii)", color.r, color.g, color.b); +// } +// else if (member_ptr == 1) +// { +// color = self->data->box.getOutlineColor(); +// //return Py_BuildValue("(iii)", color.r, color.g, color.b); +// } +// +// // initialize new mcrfpy.Color instance +// //pyColorObj->data = std::make_shared(color); +// //PyColor::fromPy(pyColorObj).set(color); +// +// // TODO - supposed to return Linked +// return PyColor(color).pyObject(); +// } +// +// static int PyUIFrame_set_color_member(PyUIFrameObject* self, PyObject* value, void* closure) +// { +// //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse +// auto member_ptr = reinterpret_cast(closure); +// int r, g, b, a; +// if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) +// { +// // get value from mcrfpy.Color instance +// /* +// PyColorObject* color = reinterpret_cast(value); +// r = color->data->r; +// g = color->data->g; +// b = color->data->b; +// a = color->data->a; +// std::cout << "using color: " << r << " " << g << " " << b << " " << a << std::endl; +// */ +// sf::Color c = ((PyColorObject*)value)->data; +// r = c.r; g = c.g; b = c.b; a = c.a; +// } +// else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) +// { +// // reject non-Color, non-tuple value +// PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); +// return -1; +// } +// else // get value from tuples +// { +// r = PyLong_AsLong(PyTuple_GetItem(value, 0)); +// g = PyLong_AsLong(PyTuple_GetItem(value, 1)); +// b = PyLong_AsLong(PyTuple_GetItem(value, 2)); +// a = 255; +// +// if (PyTuple_Size(value) == 4) +// { +// a = PyLong_AsLong(PyTuple_GetItem(value, 3)); +// } +// } +// +// if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) +// { +// PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); +// return -1; +// } +// +// if (member_ptr == 0) +// { +// self->data->box.setFillColor(sf::Color(r, g, b, a)); +// } +// else if (member_ptr == 1) +// { +// self->data->box.setOutlineColor(sf::Color(r, g, b, a)); +// } +// else +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return -1; +// } +// +// return 0; +// } +// +// static PyObject* PyUIFrame_get_children(PyUIFrameObject*, void*); +// // implementation after the PyUICollectionType definition +// +// static PyGetSetDef PyUIFrame_getsetters[] = { +// {"x", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "X coordinate of top-left corner", (void*)0}, +// {"y", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Y coordinate of top-left corner", (void*)1}, +// {"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, +// {"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, +// {"outline", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Thickness of the border", (void*)4}, +// {"fill_color", (getter)PyUIFrame_get_color_member, (setter)PyUIFrame_set_color_member, "Fill color of the rectangle", (void*)0}, +// {"outline_color", (getter)PyUIFrame_get_color_member, (setter)PyUIFrame_set_color_member, "Outline color of the rectangle", (void*)1}, +// {"children", (getter)PyUIFrame_get_children, NULL, "UICollection of objects on top of this one", NULL}, +// {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIFRAME}, +// {NULL} +// }; +// +// static PyObject* PyUIFrame_repr(PyUIFrameObject* self) +// { +// std::ostringstream ss; +// if (!self->data) ss << ""; +// else { +// auto box = self->data->box; +// auto fc = box.getFillColor(); +// auto oc = box.getOutlineColor(); +// ss << "data->children->size() << " child objects" << +// ")>"; +// } +// std::string repr_str = ss.str(); +// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +// } +// +// static int PyUIFrame_init(PyUIFrameObject* self, PyObject* args, PyObject* kwds) +// { +// //std::cout << "Init called\n"; +// static const char* keywords[] = { "x", "y", "w", "h", "fill_color", "outline_color", "outline", nullptr }; +// float x = 0.0f, y = 0.0f, w = 0.0f, h=0.0f, outline=0.0f; +// PyObject* fill_color = 0; +// PyObject* outline_color = 0; +// +// if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffff|OOf", const_cast(keywords), &x, &y, &w, &h, &fill_color, &outline_color, &outline)) +// { +// return -1; +// } +// +// //self->data->x = x; +// //self->data->y = y; +// self->data->box.setPosition(sf::Vector2f(x, y)); +// self->data->box.setSize(sf::Vector2f(w, h)); +// self->data->box.setOutlineThickness(outline); +// // getsetter abuse because I haven't standardized Color object parsing (TODO) +// int err_val = 0; +// if (fill_color && fill_color != Py_None) err_val = PyUIFrame_set_color_member(self, fill_color, (void*)0); +// else self->data->box.setFillColor(sf::Color(0,0,0,255)); +// if (err_val) return err_val; +// if (outline_color && outline_color != Py_None) err_val = PyUIFrame_set_color_member(self, outline_color, (void*)1); +// else self->data->box.setOutlineColor(sf::Color(128,128,128,255)); +// if (err_val) return err_val; +// return 0; +// } +// +// static PyTypeObject PyUIFrameType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.Frame", +// .tp_basicsize = sizeof(PyUIFrameObject), +// .tp_itemsize = 0, +// .tp_dealloc = (destructor)[](PyObject* self) +// { +// PyUIFrameObject* obj = (PyUIFrameObject*)self; +// obj->data.reset(); +// Py_TYPE(self)->tp_free(self); +// }, +// .tp_repr = (reprfunc)PyUIFrame_repr, +// //.tp_hash = NULL, +// //.tp_iter +// //.tp_iternext +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("docstring"), +// //.tp_methods = PyUIFrame_methods, +// //.tp_members = PyUIFrame_members, +// .tp_getset = PyUIFrame_getsetters, +// //.tp_base = NULL, +// .tp_init = (initproc)PyUIFrame_init, +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// PyUIFrameObject* self = (PyUIFrameObject*)type->tp_alloc(type, 0); +// if (self) self->data = std::make_shared(); +// return (PyObject*)self; +// } +// }; +// +// /* +// * +// * End auto-generated PyUIFrameType generation +// * +// */ +// +// /* +// * +// * Begin Python Class Instantiator (iterator helper) +// * +// */ +// +// /* // definition can't go in the header file +// PyObject* py_instance(UIDrawable* obj) +// { +// +// } +// */ +// +// /* +// * +// * End Python Class Instantitator (iterator helper) +// * +// */ +// +// /* +// * +// * Begin template generation for PyUISpriteType +// * +// */ +// +// static PyObject* PyUISprite_get_float_member(PyUISpriteObject* self, void* closure) +// { +// auto member_ptr = reinterpret_cast(closure); +// if (member_ptr == 0) +// return PyFloat_FromDouble(self->data->getPosition().x); +// else if (member_ptr == 1) +// return PyFloat_FromDouble(self->data->getPosition().y); +// else if (member_ptr == 2) +// return PyFloat_FromDouble(self->data->getScale().x); // scale X and Y are identical, presently +// else +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return nullptr; +// } +// } +// +// +// static int PyUISprite_set_float_member(PyUISpriteObject* 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->setPosition(sf::Vector2f(val, self->data->getPosition().y)); +// else if (member_ptr == 1) //y +// self->data->setPosition(sf::Vector2f(self->data->getPosition().x, val)); +// else if (member_ptr == 2) // scale +// self->data->setScale(sf::Vector2f(val, val)); +// return 0; +// } +// +// static PyObject* PyUISprite_get_int_member(PyUISpriteObject* self, void* closure) +// { +// auto member_ptr = reinterpret_cast(closure); +// if (true) {} +// else +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return nullptr; +// } +// +// return PyLong_FromDouble(self->data->getSpriteIndex()); +// } +// +// +// static int PyUISprite_set_int_member(PyUISpriteObject* self, PyObject* value, void* closure) +// { +// int val; +// auto member_ptr = reinterpret_cast(closure); +// if (PyLong_Check(value)) +// { +// val = PyLong_AsLong(value); +// } +// else +// { +// PyErr_SetString(PyExc_TypeError, "Value must be an integer."); +// return -1; +// } +// //self->data->sprite_index = val; +// //self->data->sprite.setTextureRect(self->data->itex->spriteCoordinates(val)); +// self->data->setSpriteIndex(val); +// return 0; +// } +// +// static PyObject* PyUISprite_get_texture(PyUISpriteObject* self, void* closure) +// { +// return self->data->getTexture()->pyObject(); +// } +// +// static int PyUISprite_set_texture(PyUISpriteObject* self, PyObject* value, void* closure) +// { +// return -1; +// } +// +// static PyGetSetDef PyUISprite_getsetters[] = { +// {"x", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "X coordinate of top-left corner", (void*)0}, +// {"y", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "Y coordinate of top-left corner", (void*)1}, +// {"scale", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "Size factor", (void*)2}, +// {"sprite_number", (getter)PyUISprite_get_int_member, (setter)PyUISprite_set_int_member, "Which sprite on the texture is shown", NULL}, +// {"texture", (getter)PyUISprite_get_texture, (setter)PyUISprite_set_texture, "Texture object", NULL}, +// {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UISPRITE}, +// {NULL} +// }; +// +// static PyObject* PyUISprite_repr(PyUISpriteObject* self) +// { +// std::ostringstream ss; +// if (!self->data) ss << ""; +// else { +// //auto sprite = self->data->sprite; +// ss << ""; +// } +// std::string repr_str = ss.str(); +// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +// } +// +// static int PyUISprite_init(PyUISpriteObject* self, PyObject* args, PyObject* kwds) +// { +// //std::cout << "Init called\n"; +// static const char* keywords[] = { "x", "y", "texture", "sprite_index", "scale", nullptr }; +// float x = 0.0f, y = 0.0f, scale = 1.0f; +// int sprite_index; +// PyObject* texture; +// +// if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffOif", +// const_cast(keywords), &x, &y, &texture, &sprite_index, &scale)) +// { +// 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) // to be removed: UIObjects don't manage texture references +// { +// self->texture = texture; +// Py_INCREF(texture); +// } else +// { +// // default tex? +// }*/ +// auto pytexture = (PyTextureObject*)texture; +// self->data = std::make_shared(pytexture->data, sprite_index, sf::Vector2f(x, y), scale); +// self->data->setPosition(sf::Vector2f(x, y)); +// +// return 0; +// } +// +// static PyTypeObject PyUISpriteType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.Sprite", +// .tp_basicsize = sizeof(PyUISpriteObject), +// .tp_itemsize = 0, +// .tp_dealloc = (destructor)[](PyObject* self) +// { +// PyUISpriteObject* obj = (PyUISpriteObject*)self; +// // release reference to font object +// //if (obj->texture) Py_DECREF(obj->texture); +// obj->data.reset(); +// Py_TYPE(self)->tp_free(self); +// }, +// .tp_repr = (reprfunc)PyUISprite_repr, +// //.tp_hash = NULL, +// //.tp_iter +// //.tp_iternext +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("docstring"), +// //.tp_methods = PyUIFrame_methods, +// //.tp_members = PyUIFrame_members, +// .tp_getset = PyUISprite_getsetters, +// //.tp_base = NULL, +// .tp_init = (initproc)PyUISprite_init, +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// PyUISpriteObject* self = (PyUISpriteObject*)type->tp_alloc(type, 0); +// //if (self) self->data = std::make_shared(); +// return (PyObject*)self; +// } +// }; +// /* +// * +// * End template for PyUISpriteType +// * +// */ +// +// +// +// +// /* +// * +// * PyUIGridPoint defs +// * +// */ +// // TODO: question: are sfColor_to_PyObject and PyObject_to_sfColor duplicitive? How does UIFrame get/set colors? - +// // Utility function to convert sf::Color to PyObject* -static PyObject* sfColor_to_PyObject(sf::Color color) { - return Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a); -} - +//static PyObject* sfColor_to_PyObject(sf::Color color) { +// return Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a); +//} +// // Utility function to convert PyObject* to sf::Color -static sf::Color PyObject_to_sfColor(PyObject* obj) { - int r, g, b, a = 255; // Default alpha to fully opaque if not specified - if (!PyArg_ParseTuple(obj, "iii|i", &r, &g, &b, &a)) { - return sf::Color(); // Return default color on parse error - } - return sf::Color(r, g, b, a); -} - -static PyObject* PyUIGridPoint_get_color(PyUIGridPointObject* self, void* closure) { - if (reinterpret_cast(closure) == 0) { // color - return sfColor_to_PyObject(self->data->color); - } else { // color_overlay - return sfColor_to_PyObject(self->data->color_overlay); - } -} - -static int PyUIGridPoint_set_color(PyUIGridPointObject* self, PyObject* value, void* closure) { - sf::Color color = PyObject_to_sfColor(value); - if (reinterpret_cast(closure) == 0) { // color - self->data->color = color; - } else { // color_overlay - self->data->color_overlay = color; - } - return 0; -} - -static PyObject* PyUIGridPoint_get_bool_member(PyUIGridPointObject* self, void* closure) { - if (reinterpret_cast(closure) == 0) { // walkable - return PyBool_FromLong(self->data->walkable); - } else { // transparent - return PyBool_FromLong(self->data->transparent); - } -} - -static int PyUIGridPoint_set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure) { - if (value == Py_True) { - if (reinterpret_cast(closure) == 0) { // walkable - self->data->walkable = true; - } else { // transparent - self->data->transparent = true; - } - } else if (value == Py_False) { - if (reinterpret_cast(closure) == 0) { // walkable - self->data->walkable = false; - } else { // transparent - self->data->transparent = false; - } - } else { - PyErr_SetString(PyExc_ValueError, "Expected a boolean value"); - return -1; - } - return 0; -} - -static PyObject* PyUIGridPoint_get_int_member(PyUIGridPointObject* self, void* closure) { - switch(reinterpret_cast(closure)) { - case 0: return PyLong_FromLong(self->data->tilesprite); - case 1: return PyLong_FromLong(self->data->tile_overlay); - case 2: return PyLong_FromLong(self->data->uisprite); - default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return nullptr; - } -} - -static int PyUIGridPoint_set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure) { - long val = PyLong_AsLong(value); - if (PyErr_Occurred()) return -1; - - switch(reinterpret_cast(closure)) { - case 0: self->data->tilesprite = val; break; - case 1: self->data->tile_overlay = val; break; - case 2: self->data->uisprite = val; break; - default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return -1; - } - return 0; -} - -static PyGetSetDef PyUIGridPoint_getsetters[] = { - {"color", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color", (void*)0}, - {"color_overlay", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color overlay", (void*)1}, - {"walkable", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint walkable", (void*)0}, - {"transparent", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint transparent", (void*)1}, - {"tilesprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile sprite index", (void*)0}, - {"tile_overlay", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile overlay sprite index", (void*)1}, - {"uisprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "UI sprite index", (void*)2}, - {NULL} /* Sentinel */ -}; - -static PyTypeObject PyUIGridPointType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.GridPoint", - .tp_basicsize = sizeof(PyUIGridPointObject), - .tp_itemsize = 0, - // Methods omitted for brevity - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = "UIGridPoint objects", - .tp_getset = PyUIGridPoint_getsetters, - //.tp_init = (initproc)PyUIGridPoint_init, // TODO Define the init function - .tp_new = PyType_GenericNew, -}; - - /* - * - * end PyUIGridPoint defs - * - */ - - - - /* - * - * PyUIGridPointState defs - * - */ - -static PyObject* PyUIGridPointState_get_bool_member(PyUIGridPointStateObject* self, void* closure) { - if (reinterpret_cast(closure) == 0) { // visible - return PyBool_FromLong(self->data->visible); - } else { // discovered - return PyBool_FromLong(self->data->discovered); - } -} - -static int PyUIGridPointState_set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure) { - if (!PyBool_Check(value)) { - PyErr_SetString(PyExc_TypeError, "Value must be a boolean"); - return -1; - } - - int truthValue = PyObject_IsTrue(value); - if (truthValue < 0) { - return -1; // PyObject_IsTrue returns -1 on error - } - - if (reinterpret_cast(closure) == 0) { // visible - self->data->visible = truthValue; - } else { // discovered - self->data->discovered = truthValue; - } - - return 0; -} - -static PyGetSetDef PyUIGridPointState_getsetters[] = { - {"visible", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Is the GridPointState visible", (void*)0}, - {"discovered", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Has the GridPointState been discovered", (void*)1}, - {NULL} /* Sentinel */ -}; - -static PyTypeObject PyUIGridPointStateType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.GridPointState", - .tp_basicsize = sizeof(PyUIGridPointStateObject), - .tp_itemsize = 0, - // Methods omitted for brevity - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = "UIGridPointState objects", // TODO: Add PyUIGridPointState tp_init - .tp_getset = PyUIGridPointState_getsetters, - .tp_new = PyType_GenericNew, -}; - - - /* - * - * end PyUIGridPointState defs - * - */ - - /* - * - * PyUIEntity defs - * - */ - +//static sf::Color PyObject_to_sfColor(PyObject* obj) { +// int r, g, b, a = 255; // Default alpha to fully opaque if not specified +// if (!PyArg_ParseTuple(obj, "iii|i", &r, &g, &b, &a)) { +// return sf::Color(); // Return default color on parse error +// } +// return sf::Color(r, g, b, a); +//} +// +//static PyObject* PyUIGridPoint_get_color(PyUIGridPointObject* self, void* closure) { +// if (reinterpret_cast(closure) == 0) { // color +// return sfColor_to_PyObject(self->data->color); +// } else { // color_overlay +// return sfColor_to_PyObject(self->data->color_overlay); +// } +//} +// +//static int PyUIGridPoint_set_color(PyUIGridPointObject* self, PyObject* value, void* closure) { +// sf::Color color = PyObject_to_sfColor(value); +// if (reinterpret_cast(closure) == 0) { // color +// self->data->color = color; +// } else { // color_overlay +// self->data->color_overlay = color; +// } +// return 0; +//} +// +//static PyObject* PyUIGridPoint_get_bool_member(PyUIGridPointObject* self, void* closure) { +// if (reinterpret_cast(closure) == 0) { // walkable +// return PyBool_FromLong(self->data->walkable); +// } else { // transparent +// return PyBool_FromLong(self->data->transparent); +// } +//} +// +//static int PyUIGridPoint_set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure) { +// if (value == Py_True) { +// if (reinterpret_cast(closure) == 0) { // walkable +// self->data->walkable = true; +// } else { // transparent +// self->data->transparent = true; +// } +// } else if (value == Py_False) { +// if (reinterpret_cast(closure) == 0) { // walkable +// self->data->walkable = false; +// } else { // transparent +// self->data->transparent = false; +// } +// } else { +// PyErr_SetString(PyExc_ValueError, "Expected a boolean value"); +// return -1; +// } +// return 0; +//} +// +//static PyObject* PyUIGridPoint_get_int_member(PyUIGridPointObject* self, void* closure) { +// switch(reinterpret_cast(closure)) { +// case 0: return PyLong_FromLong(self->data->tilesprite); +// case 1: return PyLong_FromLong(self->data->tile_overlay); +// case 2: return PyLong_FromLong(self->data->uisprite); +// default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return nullptr; +// } +//} +// +//static int PyUIGridPoint_set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure) { +// long val = PyLong_AsLong(value); +// if (PyErr_Occurred()) return -1; +// +// switch(reinterpret_cast(closure)) { +// case 0: self->data->tilesprite = val; break; +// case 1: self->data->tile_overlay = val; break; +// case 2: self->data->uisprite = val; break; +// default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return -1; +// } +// return 0; +//} +// +//static PyGetSetDef PyUIGridPoint_getsetters[] = { +// {"color", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color", (void*)0}, +// {"color_overlay", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color overlay", (void*)1}, +// {"walkable", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint walkable", (void*)0}, +// {"transparent", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint transparent", (void*)1}, +// {"tilesprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile sprite index", (void*)0}, +// {"tile_overlay", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile overlay sprite index", (void*)1}, +// {"uisprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "UI sprite index", (void*)2}, +// {NULL} /* Sentinel */ +//}; +// +//static PyTypeObject PyUIGridPointType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.GridPoint", +// .tp_basicsize = sizeof(PyUIGridPointObject), +// .tp_itemsize = 0, +// // Methods omitted for brevity +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = "UIGridPoint objects", +// .tp_getset = PyUIGridPoint_getsetters, +// //.tp_init = (initproc)PyUIGridPoint_init, // TODO Define the init function +// .tp_new = PyType_GenericNew, +//}; +// +// /* +// * +// * end PyUIGridPoint defs +// * +// */ +// +// +// +// /* +// * +// * PyUIGridPointState defs +// * +// */ +// +//static PyObject* PyUIGridPointState_get_bool_member(PyUIGridPointStateObject* self, void* closure) { +// if (reinterpret_cast(closure) == 0) { // visible +// return PyBool_FromLong(self->data->visible); +// } else { // discovered +// return PyBool_FromLong(self->data->discovered); +// } +//} +// +//static int PyUIGridPointState_set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure) { +// if (!PyBool_Check(value)) { +// PyErr_SetString(PyExc_TypeError, "Value must be a boolean"); +// return -1; +// } +// +// int truthValue = PyObject_IsTrue(value); +// if (truthValue < 0) { +// return -1; // PyObject_IsTrue returns -1 on error +// } +// +// if (reinterpret_cast(closure) == 0) { // visible +// self->data->visible = truthValue; +// } else { // discovered +// self->data->discovered = truthValue; +// } +// +// return 0; +//} +// +//static PyGetSetDef PyUIGridPointState_getsetters[] = { +// {"visible", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Is the GridPointState visible", (void*)0}, +// {"discovered", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Has the GridPointState been discovered", (void*)1}, +// {NULL} /* Sentinel */ +//}; +// +//static PyTypeObject PyUIGridPointStateType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.GridPointState", +// .tp_basicsize = sizeof(PyUIGridPointStateObject), +// .tp_itemsize = 0, +// // Methods omitted for brevity +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = "UIGridPointState objects", // TODO: Add PyUIGridPointState tp_init +// .tp_getset = PyUIGridPointState_getsetters, +// .tp_new = PyType_GenericNew, +//}; +// +// +// /* +// * +// * end PyUIGridPointState defs +// * +// */ +// +// /* +// * +// * PyUIEntity defs +// * +// */ +// // TODO: sf::Vector2f convenience functions here might benefit from a PyVectorObject much like PyColorObject // Utility function to convert sf::Vector2f to PyObject* -static PyObject* sfVector2f_to_PyObject(sf::Vector2f vector) { - return Py_BuildValue("(ff)", vector.x, vector.y); -} - +//static PyObject* sfVector2f_to_PyObject(sf::Vector2f vector) { +// return Py_BuildValue("(ff)", vector.x, vector.y); +//} +// // Utility function to convert PyObject* to sf::Vector2f -static sf::Vector2f PyObject_to_sfVector2f(PyObject* obj) { - float x, y; - if (!PyArg_ParseTuple(obj, "ff", &x, &y)) { - return sf::Vector2f(); // TODO / reconsider this default: Return default vector on parse error - } - return sf::Vector2f(x, y); -} - +//static sf::Vector2f PyObject_to_sfVector2f(PyObject* obj) { +// float x, y; +// if (!PyArg_ParseTuple(obj, "ff", &x, &y)) { +// return sf::Vector2f(); // TODO / reconsider this default: Return default vector on parse error +// } +// return sf::Vector2f(x, y); +//} +// // Utility function to convert UIGridPointState to PyObject* -static PyObject* UIGridPointState_to_PyObject(const UIGridPointState& state) { - PyObject* obj = PyObject_New(PyObject, &PyUIGridPointStateType); - if (!obj) return PyErr_NoMemory(); - - // Assuming PyUIGridPointStateObject structure has a UIGridPointState* member called 'data' - //((PyUIGridPointStateObject*)obj)->data = new UIGridPointState(state); // Copy constructor // wontimplement / feat - don't use new, get shared_ptr working - - return obj; -} - +//static PyObject* UIGridPointState_to_PyObject(const UIGridPointState& state) { +// PyObject* obj = PyObject_New(PyObject, &PyUIGridPointStateType); +// if (!obj) return PyErr_NoMemory(); +// +// // Assuming PyUIGridPointStateObject structure has a UIGridPointState* member called 'data' +// //((PyUIGridPointStateObject*)obj)->data = new UIGridPointState(state); // Copy constructor // wontimplement / feat - don't use new, get shared_ptr working +// +// return obj; +//} +// // Function to convert std::vector to a Python list TODO need a PyUICollection style iterable -static PyObject* UIGridPointStateVector_to_PyList(const std::vector& vec) { - PyObject* list = PyList_New(vec.size()); - if (!list) return PyErr_NoMemory(); - - for (size_t i = 0; i < vec.size(); ++i) { - PyObject* obj = UIGridPointState_to_PyObject(vec[i]); - if (!obj) { // Cleanup on failure - Py_DECREF(list); - return NULL; - } - PyList_SET_ITEM(list, i, obj); // This steals a reference to obj - } - - return list; -} - -static PyObject* PyUIEntity_get_position(PyUIEntityObject* self, void* closure) { - return sfVector2f_to_PyObject(self->data->position); -} - -static int PyUIEntity_set_position(PyUIEntityObject* self, PyObject* value, void* closure) { - self->data->position = PyObject_to_sfVector2f(value); - return 0; -} - -static PyObject* PyUIEntity_get_gridstate(PyUIEntityObject* self, void* closure) { - // Assuming a function to convert std::vector to PyObject* list - return UIGridPointStateVector_to_PyList(self->data->gridstate); -} - -static PyObject* PyUIEntity_get_spritenumber(PyUIEntityObject* self, void* closure) { - return PyLong_FromDouble(self->data->sprite.getSpriteIndex()); -} - -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.setSpriteIndex(val); // todone - 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} -}; - +//static PyObject* UIGridPointStateVector_to_PyList(const std::vector& vec) { +// PyObject* list = PyList_New(vec.size()); +// if (!list) return PyErr_NoMemory(); +// +// for (size_t i = 0; i < vec.size(); ++i) { +// PyObject* obj = UIGridPointState_to_PyObject(vec[i]); +// if (!obj) { // Cleanup on failure +// Py_DECREF(list); +// return NULL; +// } +// PyList_SET_ITEM(list, i, obj); // This steals a reference to obj +// } +// +// return list; +//} +// +//static PyObject* PyUIEntity_get_position(PyUIEntityObject* self, void* closure) { +// return sfVector2f_to_PyObject(self->data->position); +//} +// +//static int PyUIEntity_set_position(PyUIEntityObject* self, PyObject* value, void* closure) { +// self->data->position = PyObject_to_sfVector2f(value); +// return 0; +//} +// +//static PyObject* PyUIEntity_get_gridstate(PyUIEntityObject* self, void* closure) { +// // Assuming a function to convert std::vector to PyObject* list +// return UIGridPointStateVector_to_PyList(self->data->gridstate); +//} +// +//static PyObject* PyUIEntity_get_spritenumber(PyUIEntityObject* self, void* closure) { +// return PyLong_FromDouble(self->data->sprite.getSpriteIndex()); +//} +// +//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.setSpriteIndex(val); // todone - 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 - - - +//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 +// +// +// // Define the PyTypeObject for UIEntity -static PyTypeObject PyUIEntityType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.Entity", - .tp_basicsize = sizeof(PyUIEntityObject), - .tp_itemsize = 0, - // 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, -}; - - - /* - * - * end PyUIEntity defs - * - */ - - /* - * - * PyUIGrid defs - * - */ - -static int PyUIGrid_init(PyUIGridObject* self, PyObject* args, PyObject* kwds) { - int grid_x, grid_y; - PyObject* textureObj; - float box_x, box_y, box_w, box_h; - - if (!PyArg_ParseTuple(args, "iiOffff", &grid_x, &grid_y, &textureObj, &box_x, &box_y, &box_w, &box_h)) { - return -1; // If parsing fails, return an error - } - - // Convert PyObject texture to IndexTexture* - // This requires the texture object to have been initialized similar to UISprite's texture handling - if (!PyObject_IsInstance(textureObj, (PyObject*)&PyTextureType)) { - PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); - return -1; - } - PyTextureObject* pyTexture = reinterpret_cast(textureObj); - // TODO (7DRL day 2, item 4.) use shared_ptr / PyTextureObject on UIGrid - //IndexTexture* texture = pyTexture->data.get(); - - // Initialize UIGrid - //self->data = new UIGrid(grid_x, grid_y, texture, sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); - self->data = std::make_shared(grid_x, grid_y, pyTexture->data, - sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); - return 0; // Success -} - -static PyObject* PyUIGrid_get_grid_size(PyUIGridObject* self, void* closure) { - return Py_BuildValue("(ii)", self->data->grid_x, self->data->grid_y); -} - -static PyObject* PyUIGrid_get_position(PyUIGridObject* self, void* closure) { - auto& box = self->data->box; - return Py_BuildValue("(ff)", box.getPosition().x, box.getPosition().y); -} - -static int PyUIGrid_set_position(PyUIGridObject* self, PyObject* value, void* closure) { - float x, y; - if (!PyArg_ParseTuple(value, "ff", &x, &y)) { - PyErr_SetString(PyExc_ValueError, "Position must be a tuple of two floats"); - return -1; - } - self->data->box.setPosition(x, y); - return 0; -} - -static PyObject* PyUIGrid_get_size(PyUIGridObject* self, void* closure) { - auto& box = self->data->box; - return Py_BuildValue("(ff)", box.getSize().x, box.getSize().y); -} - -static int PyUIGrid_set_size(PyUIGridObject* self, PyObject* value, void* closure) { - float w, h; - if (!PyArg_ParseTuple(value, "ff", &w, &h)) { - PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); - return -1; - } - self->data->box.setSize(sf::Vector2f(w, h)); - 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; -} +//static PyTypeObject PyUIEntityType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.Entity", +// .tp_basicsize = sizeof(PyUIEntityObject), +// .tp_itemsize = 0, +// // 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, +//}; +// +// +// /* +// * +// * end PyUIEntity defs +// * +// */ +// +// /* +// * +// * PyUIGrid defs +// * +// */ +// +//static int PyUIGrid_init(PyUIGridObject* self, PyObject* args, PyObject* kwds) { +// int grid_x, grid_y; +// PyObject* textureObj; +// float box_x, box_y, box_w, box_h; +// +// if (!PyArg_ParseTuple(args, "iiOffff", &grid_x, &grid_y, &textureObj, &box_x, &box_y, &box_w, &box_h)) { +// return -1; // If parsing fails, return an error +// } +// +// // Convert PyObject texture to IndexTexture* +// // This requires the texture object to have been initialized similar to UISprite's texture handling +// if (!PyObject_IsInstance(textureObj, (PyObject*)&PyTextureType)) { +// PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); +// return -1; +// } +// PyTextureObject* pyTexture = reinterpret_cast(textureObj); +// // TODO (7DRL day 2, item 4.) use shared_ptr / PyTextureObject on UIGrid +// //IndexTexture* texture = pyTexture->data.get(); +// +// // Initialize UIGrid +// //self->data = new UIGrid(grid_x, grid_y, texture, sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); +// self->data = std::make_shared(grid_x, grid_y, pyTexture->data, +// sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); +// return 0; // Success +//} +// +//static PyObject* PyUIGrid_get_grid_size(PyUIGridObject* self, void* closure) { +// return Py_BuildValue("(ii)", self->data->grid_x, self->data->grid_y); +//} +// +//static PyObject* PyUIGrid_get_position(PyUIGridObject* self, void* closure) { +// auto& box = self->data->box; +// return Py_BuildValue("(ff)", box.getPosition().x, box.getPosition().y); +//} +// +//static int PyUIGrid_set_position(PyUIGridObject* self, PyObject* value, void* closure) { +// float x, y; +// if (!PyArg_ParseTuple(value, "ff", &x, &y)) { +// PyErr_SetString(PyExc_ValueError, "Position must be a tuple of two floats"); +// return -1; +// } +// self->data->box.setPosition(x, y); +// return 0; +//} +// +//static PyObject* PyUIGrid_get_size(PyUIGridObject* self, void* closure) { +// auto& box = self->data->box; +// return Py_BuildValue("(ff)", box.getSize().x, box.getSize().y); +//} +// +//static int PyUIGrid_set_size(PyUIGridObject* self, PyObject* value, void* closure) { +// float w, h; +// if (!PyArg_ParseTuple(value, "ff", &w, &h)) { +// PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); +// return -1; +// } +// self->data->box.setSize(sf::Vector2f(w, h)); +// 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) { - Py_INCREF(self->texture); - return self->texture; -} -*/ -static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { - //return self->data->getTexture()->pyObject(); - PyTextureObject* obj = (PyTextureObject*)((&PyTextureType)->tp_alloc(&PyTextureType, 0)); - obj->data = self->data->getTexture(); - return (PyObject*)obj; -} - -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; - } - if (x < 0 || x >= self->data->grid_x) { - PyErr_SetString(PyExc_ValueError, "x value out of range (0, Grid.grid_y)"); - return NULL; - } - if (y < 0 || y >= self->data->grid_y) { - PyErr_SetString(PyExc_ValueError, "y value out of range (0, Grid.grid_y)"); - return NULL; - } - - PyUIGridPointObject* obj = (PyUIGridPointObject*)((&PyUIGridPointType)->tp_alloc(&PyUIGridPointType, 0)); - //auto target = std::static_pointer_cast(target); - obj->data = &(self->data->points[x + self->data->grid_x * y]); - 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}, - - {"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}, - - {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIGRID}, - - {"texture", (getter)PyUIGrid_get_texture, NULL, "Texture of the grid", NULL}, //TODO 7DRL-day2-item5 - {NULL} /* Sentinel */ -}; - - -/* // TODO standard pointer would need deleted, but I opted for a shared pointer. tp_dealloc currently not even defined in the PyTypeObject -static void PyUIGrid_dealloc(PyUIGridObject* self) { - delete self->data; // Clean up the allocated UIGrid object - Py_TYPE(self)->tp_free((PyObject*)self); -} -*/ - - static PyTypeObject PyUIGridType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.Grid", - .tp_basicsize = sizeof(PyUIGridObject), - .tp_itemsize = 0, - //.tp_dealloc = (destructor)[](PyObject* self) - //{ - // PyUIGridObject* obj = (PyUIGridObject*)self; - // obj->data.reset(); - // Py_TYPE(self)->tp_free(self); - //}, - //TODO - PyUIGrid REPR def: - // .tp_repr = (reprfunc)PyUIGrid_repr, - //.tp_hash = NULL, - //.tp_iter - //.tp_iternext - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("docstring"), - .tp_methods = PyUIGrid_methods, - //.tp_members = PyUIGrid_members, - .tp_getset = PyUIGrid_getsetters, - //.tp_base = NULL, - .tp_init = (initproc)PyUIGrid_init, - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - PyUIGridObject* self = (PyUIGridObject*)type->tp_alloc(type, 0); - if (self) self->data = std::make_shared(); - return (PyObject*)self; - } - }; - - - /* - * - * end PyUIGrid defs - * - */ - +///* +//static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { +// Py_INCREF(self->texture); +// return self->texture; +//} +//*/ +//static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { +// //return self->data->getTexture()->pyObject(); +// PyTextureObject* obj = (PyTextureObject*)((&PyTextureType)->tp_alloc(&PyTextureType, 0)); +// obj->data = self->data->getTexture(); +// return (PyObject*)obj; +//} +// +//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; +// } +// if (x < 0 || x >= self->data->grid_x) { +// PyErr_SetString(PyExc_ValueError, "x value out of range (0, Grid.grid_y)"); +// return NULL; +// } +// if (y < 0 || y >= self->data->grid_y) { +// PyErr_SetString(PyExc_ValueError, "y value out of range (0, Grid.grid_y)"); +// return NULL; +// } +// +// PyUIGridPointObject* obj = (PyUIGridPointObject*)((&PyUIGridPointType)->tp_alloc(&PyUIGridPointType, 0)); +// //auto target = std::static_pointer_cast(target); +// obj->data = &(self->data->points[x + self->data->grid_x * y]); +// 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}, +// +// {"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}, +// +// {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIGRID}, +// +// {"texture", (getter)PyUIGrid_get_texture, NULL, "Texture of the grid", NULL}, //TODO 7DRL-day2-item5 +// {NULL} /* Sentinel */ +//}; +// +// +///* // TODO standard pointer would need deleted, but I opted for a shared pointer. tp_dealloc currently not even defined in the PyTypeObject +//static void PyUIGrid_dealloc(PyUIGridObject* self) { +// delete self->data; // Clean up the allocated UIGrid object +// Py_TYPE(self)->tp_free((PyObject*)self); +//} +//*/ +// +// static PyTypeObject PyUIGridType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.Grid", +// .tp_basicsize = sizeof(PyUIGridObject), +// .tp_itemsize = 0, +// //.tp_dealloc = (destructor)[](PyObject* self) +// //{ +// // PyUIGridObject* obj = (PyUIGridObject*)self; +// // obj->data.reset(); +// // Py_TYPE(self)->tp_free(self); +// //}, +// //TODO - PyUIGrid REPR def: +// // .tp_repr = (reprfunc)PyUIGrid_repr, +// //.tp_hash = NULL, +// //.tp_iter +// //.tp_iternext +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("docstring"), +// .tp_methods = PyUIGrid_methods, +// //.tp_members = PyUIGrid_members, +// .tp_getset = PyUIGrid_getsetters, +// //.tp_base = NULL, +// .tp_init = (initproc)PyUIGrid_init, +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// PyUIGridObject* self = (PyUIGridObject*)type->tp_alloc(type, 0); +// if (self) self->data = std::make_shared(); +// return (PyObject*)self; +// } +// }; +// +// +// /* +// * +// * end PyUIGrid defs +// * +// */ +// // 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) // this section needs to go; texture isn't optional and isn't managed by the UI objects anymore - { - 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, 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 - * - */ - typedef struct { - PyObject_HEAD - std::shared_ptr>> data; - int index; - int start_size; - } PyUIEntityCollectionIterObject; - - static int PyUIEntityCollectionIter_init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds) - { - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return -1; - } - - static PyObject* PyUIEntityCollectionIter_next(PyUIEntityCollectionIterObject* self) - { - if (self->data->size() != self->start_size) - { - PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); - return NULL; - } - - if (self->index > self->start_size - 1) - { - PyErr_SetNone(PyExc_StopIteration); - return NULL; - } - self->index++; - auto vec = self->data.get(); - if (!vec) - { - PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); - return NULL; - } - // Advance list iterator since Entities are not stored in a vector (if this code even worked) - // vectors only: //auto target = (*vec)[self->index-1]; - //auto l_front = (*vec).begin(); - //std::advance(l_front, self->index-1); - - // TODO build PyObject* of the correct UIDrawable subclass to return - //return py_instance(target); - return NULL; - } - - static PyObject* PyUIEntityCollectionIter_repr(PyUIEntityCollectionIterObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - ss << "data->size() << " child objects, @ index " << self->index << ")>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - static PyTypeObject PyUIEntityCollectionIterType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.UICollectionIter", - .tp_basicsize = sizeof(PyUIEntityCollectionIterObject), - .tp_itemsize = 0, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUIEntityCollectionIterObject* obj = (PyUIEntityCollectionIterObject*)self; - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .tp_repr = (reprfunc)PyUIEntityCollectionIter_repr, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), - .tp_iternext = (iternextfunc)PyUIEntityCollectionIter_next, - //.tp_getset = PyUIEntityCollection_getset, - .tp_init = (initproc)PyUIEntityCollectionIter_init, // just raise an exception - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return NULL; - } - }; - - /* - * - * End PyUIEntityCollectionIter defs - * - */ - -/* - * - * Begin PyUIEntityCollection defs - * - */ - typedef struct { - PyObject_HEAD - std::shared_ptr>> data; - std::shared_ptr grid; - } PyUIEntityCollectionObject; - - static Py_ssize_t PyUIEntityCollection_len(PyUIEntityCollectionObject* self) { - return self->data->size(); - } - - static PyObject* PyUIEntityCollection_getitem(PyUIEntityCollectionObject* self, Py_ssize_t index) { - // build a Python version of item at self->data[index] - // Copy pasted:: - auto vec = self->data.get(); - if (!vec) - { - PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); - return NULL; - } - while (index < 0) index += self->data->size(); - if (index > self->data->size() - 1) - { - PyErr_SetString(PyExc_IndexError, "EntityCollection index out of range"); - return NULL; - } - auto l_begin = (*vec).begin(); - std::advance(l_begin, index); - auto target = *l_begin; //auto target = (*vec)[index]; - //RET_PY_INSTANCE(target); - // construct and return an entity object that points directly into the UIGrid's entity vector - PyUIEntityObject* o = (PyUIEntityObject*)((&PyUIEntityType)->tp_alloc(&PyUIEntityType, 0)); - auto p = std::static_pointer_cast(target); - o->data = p; - return (PyObject*)o; - return NULL; - - - } - - static PySequenceMethods PyUIEntityCollection_sqmethods = { - .sq_length = (lenfunc)PyUIEntityCollection_len, - .sq_item = (ssizeargfunc)PyUIEntityCollection_getitem, - //.sq_item_by_index = PyUIEntityCollection_getitem - //.sq_slice - return a subset of the iterable - //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) - //.sq_ass_slice - cool; no thanks, for now - //.sq_contains - called when `x in o` is executed - //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) - }; - - static PyObject* PyUIEntityCollection_append(PyUIEntityCollectionObject* self, PyObject* o) - { - // if not UIDrawable subclass, reject it - // self->data->push_back( c++ object inside o ); - - // this would be a great use case for .tp_base - if (!PyObject_IsInstance(o, (PyObject*)&PyUIEntityType)) - { - PyErr_SetString(PyExc_TypeError, "Only Entity objects can be added to EntityCollection"); - return NULL; - } - PyUIEntityObject* entity = (PyUIEntityObject*)o; - self->data->push_back(entity->data); - entity->data->grid = self->grid; - - Py_INCREF(Py_None); - return Py_None; - } - static PyObject* PyUIEntityCollection_remove(PyUIEntityCollectionObject* self, PyObject* o) - { - if (!PyLong_Check(o)) - { - PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); - return NULL; - } - long index = PyLong_AsLong(o); - if (index >= self->data->size()) - { - PyErr_SetString(PyExc_ValueError, "Index out of range"); - return NULL; - } - else if (index < 0) - { - PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); - return NULL; - } - - // release the shared pointer at correct part of the list - self->data->erase(std::next(self->data->begin(), index)); - Py_INCREF(Py_None); - return Py_None; - } - - static PyMethodDef PyUIEntityCollection_methods[] = { - {"append", (PyCFunction)PyUIEntityCollection_append, METH_O}, - //{"extend", (PyCFunction)PyUIEntityCollection_extend, METH_O}, // TODO - {"remove", (PyCFunction)PyUIEntityCollection_remove, METH_O}, - {NULL, NULL, 0, NULL} - }; - - static PyObject* PyUIEntityCollection_repr(PyUIEntityCollectionObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - ss << "data->size() << " child objects)>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - static int PyUIEntityCollection_init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds) - { - PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); - return -1; - } - - static PyObject* PyUIEntityCollection_iter(PyUIEntityCollectionObject* self) - { - PyUIEntityCollectionIterObject* iterObj; - iterObj = (PyUIEntityCollectionIterObject*)PyUIEntityCollectionIterType.tp_alloc(&PyUIEntityCollectionIterType, 0); - if (iterObj == NULL) { - return NULL; // Failed to allocate memory for the iterator object - } - - iterObj->data = self->data; - iterObj->index = 0; - iterObj->start_size = self->data->size(); - - return (PyObject*)iterObj; - } - static PyTypeObject PyUIEntityCollectionType = { - //PyVarObject_/HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.EntityCollection", - .tp_basicsize = sizeof(PyUIEntityCollectionObject), - .tp_itemsize = 0, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUIEntityCollectionObject* obj = (PyUIEntityCollectionObject*)self; - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .tp_repr = (reprfunc)PyUIEntityCollection_repr, - .tp_as_sequence = &PyUIEntityCollection_sqmethods, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("Iterable, indexable collection of Entities"), - .tp_iter = (getiterfunc)PyUIEntityCollection_iter, - .tp_methods = PyUIEntityCollection_methods, // append, remove - //.tp_getset = PyUIEntityCollection_getset, - .tp_init = (initproc)PyUIEntityCollection_init, // just raise an exception - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - // Does PyUIEntityCollectionType need __new__ if it's not supposed to be instantiable by the user? - // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? - PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); - return NULL; - } - }; - - // Grid's get_children def must follow the EntityCollection def - static PyObject* PyUIGrid_get_children(PyUIGridObject* self, void* closure) - { - // create PyUICollection instance pointing to self->data->children - PyUIEntityCollectionObject* o = (PyUIEntityCollectionObject*)PyUIEntityCollectionType.tp_alloc(&PyUIEntityCollectionType, 0); - 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; - } - - /* - * - * End PyUIEntityCollection defs - * - */ - - - - /* - * - * Begin PyUICollectionIter defs - * - */ - typedef struct { - PyObject_HEAD - std::shared_ptr>> data; - int index; - int start_size; - } PyUICollectionIterObject; - - static int PyUICollectionIter_init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds) - { - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return -1; - } - - static PyObject* PyUICollectionIter_next(PyUICollectionIterObject* self) - { - if (self->data->size() != self->start_size) - { - PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); - return NULL; - } - - if (self->index > self->start_size - 1) - { - PyErr_SetNone(PyExc_StopIteration); - return NULL; - } - self->index++; - auto vec = self->data.get(); - if (!vec) - { - PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); - return NULL; - } - auto target = (*vec)[self->index-1]; - // TODO build PyObject* of the correct UIDrawable subclass to return - //return py_instance(target); - return NULL; - } - - static PyObject* PyUICollectionIter_repr(PyUICollectionIterObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - ss << "data->size() << " child objects, @ index " << self->index << ")>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - static PyTypeObject PyUICollectionIterType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.UICollectionIter", - .tp_basicsize = sizeof(PyUICollectionIterObject), - .tp_itemsize = 0, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUICollectionIterObject* obj = (PyUICollectionIterObject*)self; - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .tp_repr = (reprfunc)PyUICollectionIter_repr, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), - .tp_iternext = (iternextfunc)PyUICollectionIter_next, - //.tp_getset = PyUICollection_getset, - .tp_init = (initproc)PyUICollectionIter_init, // just raise an exception - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return NULL; - } - }; - - /* - * - * End PyUICollectionIter defs - * - */ - - - /* - * - * Begin PyUICollection defs - * - */ - - static Py_ssize_t PyUICollection_len(PyUICollectionObject* self) { - return self->data->size(); - } - - static PyObject* PyUICollection_getitem(PyUICollectionObject* self, Py_ssize_t index) { - // build a Python version of item at self->data[index] - // Copy pasted:: - auto vec = self->data.get(); - if (!vec) - { - PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); - return NULL; - } - while (index < 0) index += self->data->size(); - if (index > self->data->size() - 1) - { - PyErr_SetString(PyExc_IndexError, "UICollection index out of range"); - return NULL; - } - auto target = (*vec)[index]; - RET_PY_INSTANCE(target); - return NULL; - - - } - - static PySequenceMethods PyUICollection_sqmethods = { - .sq_length = (lenfunc)PyUICollection_len, - .sq_item = (ssizeargfunc)PyUICollection_getitem, - //.sq_item_by_index = PyUICollection_getitem - //.sq_slice - return a subset of the iterable - //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) - //.sq_ass_slice - cool; no thanks, for now - //.sq_contains - called when `x in o` is executed - //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) - }; - - static PyObject* PyUICollection_append(PyUICollectionObject* self, PyObject* o) - { - // if not UIDrawable subclass, reject it - // self->data->push_back( c++ object inside o ); - - // this would be a great use case for .tp_base - if (!PyObject_IsInstance(o, (PyObject*)&PyUIFrameType) && - !PyObject_IsInstance(o, (PyObject*)&PyUISpriteType) && - !PyObject_IsInstance(o, (PyObject*)&PyUICaptionType) && - !PyObject_IsInstance(o, (PyObject*)&PyUIGridType) - ) - { - PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, and Grid objects can be added to UICollection"); - return NULL; - } - - if (PyObject_IsInstance(o, (PyObject*)&PyUIFrameType)) - { - PyUIFrameObject* frame = (PyUIFrameObject*)o; - self->data->push_back(frame->data); - } - if (PyObject_IsInstance(o, (PyObject*)&PyUICaptionType)) - { - PyUICaptionObject* caption = (PyUICaptionObject*)o; - self->data->push_back(caption->data); - } - if (PyObject_IsInstance(o, (PyObject*)&PyUISpriteType)) - { - PyUISpriteObject* sprite = (PyUISpriteObject*)o; - self->data->push_back(sprite->data); - } - if (PyObject_IsInstance(o, (PyObject*)&PyUIGridType)) - { - PyUIGridObject* grid = (PyUIGridObject*)o; - self->data->push_back(grid->data); - } - - Py_INCREF(Py_None); - return Py_None; - } - - static PyObject* PyUICollection_remove(PyUICollectionObject* self, PyObject* o) - { - if (!PyLong_Check(o)) - { - PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); - return NULL; - } - long index = PyLong_AsLong(o); - if (index >= self->data->size()) - { - PyErr_SetString(PyExc_ValueError, "Index out of range"); - return NULL; - } - else if (index < 0) - { - PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); - return NULL; - } - - // release the shared pointer at self->data[index]; - self->data->erase(self->data->begin() + index); - Py_INCREF(Py_None); - return Py_None; - } - - static PyMethodDef PyUICollection_methods[] = { - {"append", (PyCFunction)PyUICollection_append, METH_O}, - //{"extend", (PyCFunction)PyUICollection_extend, METH_O}, // TODO - {"remove", (PyCFunction)PyUICollection_remove, METH_O}, - {NULL, NULL, 0, NULL} - }; - - static PyObject* PyUICollection_repr(PyUICollectionObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - ss << "data->size() << " child objects)>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - static int PyUICollection_init(PyUICollectionObject* self, PyObject* args, PyObject* kwds) - { - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return -1; - } - - static PyObject* PyUICollection_iter(PyUICollectionObject* self) - { - PyUICollectionIterObject* iterObj; - iterObj = (PyUICollectionIterObject*)PyUICollectionIterType.tp_alloc(&PyUICollectionIterType, 0); - if (iterObj == NULL) { - return NULL; // Failed to allocate memory for the iterator object - } - - iterObj->data = self->data; - iterObj->index = 0; - iterObj->start_size = self->data->size(); - - return (PyObject*)iterObj; - } - - /* - static PyGetSetDef PyUICollection_getsetters[] = { - {NULL} - }; - */ - - static PyTypeObject PyUICollectionType = { - //PyVarObject_/HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.UICollection", - .tp_basicsize = sizeof(PyUICollectionObject), - .tp_itemsize = 0, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUICollectionObject* obj = (PyUICollectionObject*)self; - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .tp_repr = (reprfunc)PyUICollection_repr, - .tp_as_sequence = &PyUICollection_sqmethods, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("Iterable, indexable collection of UI objects"), - .tp_iter = (getiterfunc)PyUICollection_iter, - .tp_methods = PyUICollection_methods, // append, remove - //.tp_getset = PyUICollection_getset, - .tp_init = (initproc)PyUICollection_init, // just raise an exception - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - // Does PyUICollectionType need __new__ if it's not supposed to be instantiable by the user? - // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return NULL; - } - }; - /* - * - * End PyUICollection defs - * - */ - - static PyObject* PyUIFrame_get_children(PyUIFrameObject* self, void* closure) - { - // create PyUICollection instance pointing to self->data->children - PyUICollectionObject* o = (PyUICollectionObject*)PyUICollectionType.tp_alloc(&PyUICollectionType, 0); - if (o) - o->data = self->data->children; - return (PyObject*)o; - } - - -} // namespace mcrfpydef +//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) // this section needs to go; texture isn't optional and isn't managed by the UI objects anymore +// { +// 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, 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 +// * +// */ +// typedef struct { +// PyObject_HEAD +// std::shared_ptr>> data; +// int index; +// int start_size; +// } PyUIEntityCollectionIterObject; +// +// static int PyUIEntityCollectionIter_init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds) +// { +// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); +// return -1; +// } +// +// static PyObject* PyUIEntityCollectionIter_next(PyUIEntityCollectionIterObject* self) +// { +// if (self->data->size() != self->start_size) +// { +// PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); +// return NULL; +// } +// +// if (self->index > self->start_size - 1) +// { +// PyErr_SetNone(PyExc_StopIteration); +// return NULL; +// } +// self->index++; +// auto vec = self->data.get(); +// if (!vec) +// { +// PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); +// return NULL; +// } +// // Advance list iterator since Entities are not stored in a vector (if this code even worked) +// // vectors only: //auto target = (*vec)[self->index-1]; +// //auto l_front = (*vec).begin(); +// //std::advance(l_front, self->index-1); +// +// // TODO build PyObject* of the correct UIDrawable subclass to return +// //return py_instance(target); +// return NULL; +// } +// +// static PyObject* PyUIEntityCollectionIter_repr(PyUIEntityCollectionIterObject* self) +// { +// std::ostringstream ss; +// if (!self->data) ss << ""; +// else { +// ss << "data->size() << " child objects, @ index " << self->index << ")>"; +// } +// std::string repr_str = ss.str(); +// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +// } +// static PyTypeObject PyUIEntityCollectionIterType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.UICollectionIter", +// .tp_basicsize = sizeof(PyUIEntityCollectionIterObject), +// .tp_itemsize = 0, +// .tp_dealloc = (destructor)[](PyObject* self) +// { +// PyUIEntityCollectionIterObject* obj = (PyUIEntityCollectionIterObject*)self; +// obj->data.reset(); +// Py_TYPE(self)->tp_free(self); +// }, +// .tp_repr = (reprfunc)PyUIEntityCollectionIter_repr, +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), +// .tp_iternext = (iternextfunc)PyUIEntityCollectionIter_next, +// //.tp_getset = PyUIEntityCollection_getset, +// .tp_init = (initproc)PyUIEntityCollectionIter_init, // just raise an exception +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); +// return NULL; +// } +// }; +// +// /* +// * +// * End PyUIEntityCollectionIter defs +// * +// */ +// +///* +// * +// * Begin PyUIEntityCollection defs +// * +// */ +// typedef struct { +// PyObject_HEAD +// std::shared_ptr>> data; +// std::shared_ptr grid; +// } PyUIEntityCollectionObject; +// +// static Py_ssize_t PyUIEntityCollection_len(PyUIEntityCollectionObject* self) { +// return self->data->size(); +// } +// +// static PyObject* PyUIEntityCollection_getitem(PyUIEntityCollectionObject* self, Py_ssize_t index) { +// // build a Python version of item at self->data[index] +// // Copy pasted:: +// auto vec = self->data.get(); +// if (!vec) +// { +// PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); +// return NULL; +// } +// while (index < 0) index += self->data->size(); +// if (index > self->data->size() - 1) +// { +// PyErr_SetString(PyExc_IndexError, "EntityCollection index out of range"); +// return NULL; +// } +// auto l_begin = (*vec).begin(); +// std::advance(l_begin, index); +// auto target = *l_begin; //auto target = (*vec)[index]; +// //RET_PY_INSTANCE(target); +// // construct and return an entity object that points directly into the UIGrid's entity vector +// PyUIEntityObject* o = (PyUIEntityObject*)((&PyUIEntityType)->tp_alloc(&PyUIEntityType, 0)); +// auto p = std::static_pointer_cast(target); +// o->data = p; +// return (PyObject*)o; +// return NULL; +// +// +// } +// +// static PySequenceMethods PyUIEntityCollection_sqmethods = { +// .sq_length = (lenfunc)PyUIEntityCollection_len, +// .sq_item = (ssizeargfunc)PyUIEntityCollection_getitem, +// //.sq_item_by_index = PyUIEntityCollection_getitem +// //.sq_slice - return a subset of the iterable +// //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) +// //.sq_ass_slice - cool; no thanks, for now +// //.sq_contains - called when `x in o` is executed +// //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) +// }; +// +// static PyObject* PyUIEntityCollection_append(PyUIEntityCollectionObject* self, PyObject* o) +// { +// // if not UIDrawable subclass, reject it +// // self->data->push_back( c++ object inside o ); +// +// // this would be a great use case for .tp_base +// if (!PyObject_IsInstance(o, (PyObject*)&PyUIEntityType)) +// { +// PyErr_SetString(PyExc_TypeError, "Only Entity objects can be added to EntityCollection"); +// return NULL; +// } +// PyUIEntityObject* entity = (PyUIEntityObject*)o; +// self->data->push_back(entity->data); +// entity->data->grid = self->grid; +// +// Py_INCREF(Py_None); +// return Py_None; +// } +// static PyObject* PyUIEntityCollection_remove(PyUIEntityCollectionObject* self, PyObject* o) +// { +// if (!PyLong_Check(o)) +// { +// PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); +// return NULL; +// } +// long index = PyLong_AsLong(o); +// if (index >= self->data->size()) +// { +// PyErr_SetString(PyExc_ValueError, "Index out of range"); +// return NULL; +// } +// else if (index < 0) +// { +// PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); +// return NULL; +// } +// +// // release the shared pointer at correct part of the list +// self->data->erase(std::next(self->data->begin(), index)); +// Py_INCREF(Py_None); +// return Py_None; +// } +// +// static PyMethodDef PyUIEntityCollection_methods[] = { +// {"append", (PyCFunction)PyUIEntityCollection_append, METH_O}, +// //{"extend", (PyCFunction)PyUIEntityCollection_extend, METH_O}, // TODO +// {"remove", (PyCFunction)PyUIEntityCollection_remove, METH_O}, +// {NULL, NULL, 0, NULL} +// }; +// +// static PyObject* PyUIEntityCollection_repr(PyUIEntityCollectionObject* self) +// { +// std::ostringstream ss; +// if (!self->data) ss << ""; +// else { +// ss << "data->size() << " child objects)>"; +// } +// std::string repr_str = ss.str(); +// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +// } +// +// static int PyUIEntityCollection_init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds) +// { +// PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); +// return -1; +// } +// +// static PyObject* PyUIEntityCollection_iter(PyUIEntityCollectionObject* self) +// { +// PyUIEntityCollectionIterObject* iterObj; +// iterObj = (PyUIEntityCollectionIterObject*)PyUIEntityCollectionIterType.tp_alloc(&PyUIEntityCollectionIterType, 0); +// if (iterObj == NULL) { +// return NULL; // Failed to allocate memory for the iterator object +// } +// +// iterObj->data = self->data; +// iterObj->index = 0; +// iterObj->start_size = self->data->size(); +// +// return (PyObject*)iterObj; +// } +// static PyTypeObject PyUIEntityCollectionType = { +// //PyVarObject_/HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.EntityCollection", +// .tp_basicsize = sizeof(PyUIEntityCollectionObject), +// .tp_itemsize = 0, +// .tp_dealloc = (destructor)[](PyObject* self) +// { +// PyUIEntityCollectionObject* obj = (PyUIEntityCollectionObject*)self; +// obj->data.reset(); +// Py_TYPE(self)->tp_free(self); +// }, +// .tp_repr = (reprfunc)PyUIEntityCollection_repr, +// .tp_as_sequence = &PyUIEntityCollection_sqmethods, +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("Iterable, indexable collection of Entities"), +// .tp_iter = (getiterfunc)PyUIEntityCollection_iter, +// .tp_methods = PyUIEntityCollection_methods, // append, remove +// //.tp_getset = PyUIEntityCollection_getset, +// .tp_init = (initproc)PyUIEntityCollection_init, // just raise an exception +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// // Does PyUIEntityCollectionType need __new__ if it's not supposed to be instantiable by the user? +// // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? +// PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); +// return NULL; +// } +// }; +// +// // Grid's get_children def must follow the EntityCollection def +// static PyObject* PyUIGrid_get_children(PyUIGridObject* self, void* closure) +// { +// // create PyUICollection instance pointing to self->data->children +// PyUIEntityCollectionObject* o = (PyUIEntityCollectionObject*)PyUIEntityCollectionType.tp_alloc(&PyUIEntityCollectionType, 0); +// 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; +// } +// +// /* +// * +// * End PyUIEntityCollection defs +// * +// */ +// +// +// +// /* +// * +// * Begin PyUICollectionIter defs +// * +// */ +// typedef struct { +// PyObject_HEAD +// std::shared_ptr>> data; +// int index; +// int start_size; +// } PyUICollectionIterObject; +// +// static int PyUICollectionIter_init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds) +// { +// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); +// return -1; +// } +// +// static PyObject* PyUICollectionIter_next(PyUICollectionIterObject* self) +// { +// if (self->data->size() != self->start_size) +// { +// PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); +// return NULL; +// } +// +// if (self->index > self->start_size - 1) +// { +// PyErr_SetNone(PyExc_StopIteration); +// return NULL; +// } +// self->index++; +// auto vec = self->data.get(); +// if (!vec) +// { +// PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); +// return NULL; +// } +// auto target = (*vec)[self->index-1]; +// // TODO build PyObject* of the correct UIDrawable subclass to return +// //return py_instance(target); +// return NULL; +// } +// +// static PyObject* PyUICollectionIter_repr(PyUICollectionIterObject* self) +// { +// std::ostringstream ss; +// if (!self->data) ss << ""; +// else { +// ss << "data->size() << " child objects, @ index " << self->index << ")>"; +// } +// std::string repr_str = ss.str(); +// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +// } +// +// static PyTypeObject PyUICollectionIterType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.UICollectionIter", +// .tp_basicsize = sizeof(PyUICollectionIterObject), +// .tp_itemsize = 0, +// .tp_dealloc = (destructor)[](PyObject* self) +// { +// PyUICollectionIterObject* obj = (PyUICollectionIterObject*)self; +// obj->data.reset(); +// Py_TYPE(self)->tp_free(self); +// }, +// .tp_repr = (reprfunc)PyUICollectionIter_repr, +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), +// .tp_iternext = (iternextfunc)PyUICollectionIter_next, +// //.tp_getset = PyUICollection_getset, +// .tp_init = (initproc)PyUICollectionIter_init, // just raise an exception +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); +// return NULL; +// } +// }; +// +// /* +// * +// * End PyUICollectionIter defs +// * +// */ +// +// +// /* +// * +// * Begin PyUICollection defs +// * +// */ +// +// static Py_ssize_t PyUICollection_len(PyUICollectionObject* self) { +// return self->data->size(); +// } +// +// static PyObject* PyUICollection_getitem(PyUICollectionObject* self, Py_ssize_t index) { +// // build a Python version of item at self->data[index] +// // Copy pasted:: +// auto vec = self->data.get(); +// if (!vec) +// { +// PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); +// return NULL; +// } +// while (index < 0) index += self->data->size(); +// if (index > self->data->size() - 1) +// { +// PyErr_SetString(PyExc_IndexError, "UICollection index out of range"); +// return NULL; +// } +// auto target = (*vec)[index]; +// RET_PY_INSTANCE(target); +// return NULL; +// +// +// } +// +// static PySequenceMethods PyUICollection_sqmethods = { +// .sq_length = (lenfunc)PyUICollection_len, +// .sq_item = (ssizeargfunc)PyUICollection_getitem, +// //.sq_item_by_index = PyUICollection_getitem +// //.sq_slice - return a subset of the iterable +// //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) +// //.sq_ass_slice - cool; no thanks, for now +// //.sq_contains - called when `x in o` is executed +// //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) +// }; +// +// static PyObject* PyUICollection_append(PyUICollectionObject* self, PyObject* o) +// { +// // if not UIDrawable subclass, reject it +// // self->data->push_back( c++ object inside o ); +// +// // this would be a great use case for .tp_base +// if (!PyObject_IsInstance(o, (PyObject*)&PyUIFrameType) && +// !PyObject_IsInstance(o, (PyObject*)&PyUISpriteType) && +// !PyObject_IsInstance(o, (PyObject*)&PyUICaptionType) && +// !PyObject_IsInstance(o, (PyObject*)&PyUIGridType) +// ) +// { +// PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, and Grid objects can be added to UICollection"); +// return NULL; +// } +// +// if (PyObject_IsInstance(o, (PyObject*)&PyUIFrameType)) +// { +// PyUIFrameObject* frame = (PyUIFrameObject*)o; +// self->data->push_back(frame->data); +// } +// if (PyObject_IsInstance(o, (PyObject*)&PyUICaptionType)) +// { +// PyUICaptionObject* caption = (PyUICaptionObject*)o; +// self->data->push_back(caption->data); +// } +// if (PyObject_IsInstance(o, (PyObject*)&PyUISpriteType)) +// { +// PyUISpriteObject* sprite = (PyUISpriteObject*)o; +// self->data->push_back(sprite->data); +// } +// if (PyObject_IsInstance(o, (PyObject*)&PyUIGridType)) +// { +// PyUIGridObject* grid = (PyUIGridObject*)o; +// self->data->push_back(grid->data); +// } +// +// Py_INCREF(Py_None); +// return Py_None; +// } +// +// static PyObject* PyUICollection_remove(PyUICollectionObject* self, PyObject* o) +// { +// if (!PyLong_Check(o)) +// { +// PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); +// return NULL; +// } +// long index = PyLong_AsLong(o); +// if (index >= self->data->size()) +// { +// PyErr_SetString(PyExc_ValueError, "Index out of range"); +// return NULL; +// } +// else if (index < 0) +// { +// PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); +// return NULL; +// } +// +// // release the shared pointer at self->data[index]; +// self->data->erase(self->data->begin() + index); +// Py_INCREF(Py_None); +// return Py_None; +// } +// +// static PyMethodDef PyUICollection_methods[] = { +// {"append", (PyCFunction)PyUICollection_append, METH_O}, +// //{"extend", (PyCFunction)PyUICollection_extend, METH_O}, // TODO +// {"remove", (PyCFunction)PyUICollection_remove, METH_O}, +// {NULL, NULL, 0, NULL} +// }; +// +// static PyObject* PyUICollection_repr(PyUICollectionObject* self) +// { +// std::ostringstream ss; +// if (!self->data) ss << ""; +// else { +// ss << "data->size() << " child objects)>"; +// } +// std::string repr_str = ss.str(); +// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +// } +// +// static int PyUICollection_init(PyUICollectionObject* self, PyObject* args, PyObject* kwds) +// { +// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); +// return -1; +// } +// +// static PyObject* PyUICollection_iter(PyUICollectionObject* self) +// { +// PyUICollectionIterObject* iterObj; +// iterObj = (PyUICollectionIterObject*)PyUICollectionIterType.tp_alloc(&PyUICollectionIterType, 0); +// if (iterObj == NULL) { +// return NULL; // Failed to allocate memory for the iterator object +// } +// +// iterObj->data = self->data; +// iterObj->index = 0; +// iterObj->start_size = self->data->size(); +// +// return (PyObject*)iterObj; +// } +// +// /* +// static PyGetSetDef PyUICollection_getsetters[] = { +// {NULL} +// }; +// */ +// +// static PyTypeObject PyUICollectionType = { +// //PyVarObject_/HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.UICollection", +// .tp_basicsize = sizeof(PyUICollectionObject), +// .tp_itemsize = 0, +// .tp_dealloc = (destructor)[](PyObject* self) +// { +// PyUICollectionObject* obj = (PyUICollectionObject*)self; +// obj->data.reset(); +// Py_TYPE(self)->tp_free(self); +// }, +// .tp_repr = (reprfunc)PyUICollection_repr, +// .tp_as_sequence = &PyUICollection_sqmethods, +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("Iterable, indexable collection of UI objects"), +// .tp_iter = (getiterfunc)PyUICollection_iter, +// .tp_methods = PyUICollection_methods, // append, remove +// //.tp_getset = PyUICollection_getset, +// .tp_init = (initproc)PyUICollection_init, // just raise an exception +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// // Does PyUICollectionType need __new__ if it's not supposed to be instantiable by the user? +// // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? +// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); +// return NULL; +// } +// }; +// /* +// * +// * End PyUICollection defs +// * +// */ +// +// static PyObject* PyUIFrame_get_children(PyUIFrameObject* self, void* closure) +// { +// // create PyUICollection instance pointing to self->data->children +// PyUICollectionObject* o = (PyUICollectionObject*)PyUICollectionType.tp_alloc(&PyUICollectionType, 0); +// if (o) +// o->data = self->data->children; +// return (PyObject*)o; +// } +// +// +//} // namespace mcrfpydef diff --git a/src/UICaption.cpp b/src/UICaption.cpp new file mode 100644 index 0000000..aa53a8a --- /dev/null +++ b/src/UICaption.cpp @@ -0,0 +1,22 @@ +#include "UICaption.h" + +UIDrawable* UICaption::click_at(sf::Vector2f point) +{ + if (click_callable) + { + if (text.getGlobalBounds().contains(point)) return this; + } + return NULL; +} + +void UICaption::render(sf::Vector2f offset) +{ + text.move(offset); + Resources::game->getWindow().draw(text); + text.move(-offset); +} + +PyObjectsEnum UICaption::derived_type() +{ + return PyObjectsEnum::UICAPTION; +} diff --git a/src/UICaption.h b/src/UICaption.h new file mode 100644 index 0000000..ee37e1c --- /dev/null +++ b/src/UICaption.h @@ -0,0 +1,304 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyTexture.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" +#include "UIDrawable.h" + +class UICaption: public UIDrawable +{ +public: + sf::Text text; + void render(sf::Vector2f) override final; + PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UICaption; }; + virtual UIDrawable* click_at(sf::Vector2f point) override final; +}; + +typedef struct { + PyObject_HEAD + std::shared_ptr data; + PyObject* font; +} PyUICaptionObject; + +namespace mcrfpydef { + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICaption_get_float_member(PyUICaptionObject* self, void* closure) + { + auto member_ptr = reinterpret_cast(closure); + if (member_ptr == 0) + return PyFloat_FromDouble(self->data->text.getPosition().x); + else if (member_ptr == 1) + return PyFloat_FromDouble(self->data->text.getPosition().y); + else if (member_ptr == 4) + return PyFloat_FromDouble(self->data->text.getOutlineThickness()); + else if (member_ptr == 5) + return PyLong_FromLong(self->data->text.getCharacterSize()); + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICaption_set_float_member(PyUICaptionObject* 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 an integer."); + return -1; + } + if (member_ptr == 0) //x + self->data->text.setPosition(val, self->data->text.getPosition().y); + else if (member_ptr == 1) //y + self->data->text.setPosition(self->data->text.getPosition().x, val); + else if (member_ptr == 4) //outline + self->data->text.setOutlineThickness(val); + else if (member_ptr == 5) // character size + self->data->text.setCharacterSize(val); + return 0; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICaption_get_vec_member(PyUICaptionObject* self, void* closure) + { + return PyVector(self->data->text.getPosition()).pyObject(); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICaption_set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure) + { + self->data->text.setPosition(PyVector::fromPy(value)); + return 0; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICaption_get_color_member(PyUICaptionObject* self, void* closure) + { + // validate closure (should be impossible to be wrong, but it's thorough) + auto member_ptr = reinterpret_cast(closure); + if (member_ptr != 0 && member_ptr != 1) + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + + // TODO: manually calling tp_alloc to create a PyColorObject seems like an antipattern + // fetch correct member data + sf::Color color; + + if (member_ptr == 0) + { + color = self->data->text.getFillColor(); + } + else if (member_ptr == 1) + { + color = self->data->text.getOutlineColor(); + } + + return PyColor(color).pyObject(); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICaption_set_color_member(PyUICaptionObject* self, PyObject* value, void* closure) + { + auto member_ptr = reinterpret_cast(closure); + //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse + int r, g, b, a; + if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) + { + // get value from mcrfpy.Color instance + auto c = ((PyColorObject*)value)->data; + r = c.r; g = c.g; b = c.b; a = c.a; + std::cout << "got " << int(r) << ", " << int(g) << ", " << int(b) << ", " << int(a) << std::endl; + } + else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) + { + // reject non-Color, non-tuple value + PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); + return -1; + } + else // get value from tuples + { + r = PyLong_AsLong(PyTuple_GetItem(value, 0)); + g = PyLong_AsLong(PyTuple_GetItem(value, 1)); + b = PyLong_AsLong(PyTuple_GetItem(value, 2)); + a = 255; + + if (PyTuple_Size(value) == 4) + { + a = PyLong_AsLong(PyTuple_GetItem(value, 3)); + } + } + + if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) + { + PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); + return -1; + } + + if (member_ptr == 0) + { + self->data->text.setFillColor(sf::Color(r, g, b, a)); + } + else if (member_ptr == 1) + { + self->data->text.setOutlineColor(sf::Color(r, g, b, a)); + } + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return -1; + } + + return 0; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICaption_get_text(PyUICaptionObject* self, void* closure) + { + Resources::caption_buffer = self->data->text.getString(); + return PyUnicode_FromString(Resources::caption_buffer.c_str()); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICaption_set_text(PyUICaptionObject* self, PyObject* value, void* closure) + { + PyObject* s = PyObject_Str(value); + PyObject * temp_bytes = PyUnicode_AsEncodedString(s, "UTF-8", "strict"); // Owned reference + if (temp_bytes != NULL) { + Resources::caption_buffer = PyBytes_AS_STRING(temp_bytes); // Borrowed pointer + Py_DECREF(temp_bytes); + } + self->data->text.setString(Resources::caption_buffer); + return 0; + } + + //TODO: add this static array to class scope; move implementation to .cpp file + static PyGetSetDef PyUICaption_getsetters[] = { + {"x", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "X coordinate of top-left corner", (void*)0}, + {"y", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Y coordinate of top-left corner", (void*)1}, + {"pos", (getter)PyUICaption_get_vec_member, (setter)PyUICaption_set_vec_member, "(x, y) vector", (void*)0}, + //{"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, + //{"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, + {"outline", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Thickness of the border", (void*)4}, + {"fill_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Fill color of the text", (void*)0}, + {"outline_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Outline color of the text", (void*)1}, + //{"children", (getter)PyUIFrame_get_children, NULL, "UICollection of objects on top of this one", NULL}, + {"text", (getter)PyUICaption_get_text, (setter)PyUICaption_set_text, "The text displayed", NULL}, + {"size", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Text size (integer) in points", (void*)5}, + {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UICAPTION}, + {NULL} + }; + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICaption_repr(PyUICaptionObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + auto text = self->data->text; + auto fc = text.getFillColor(); + auto oc = text.getOutlineColor(); + ss << ""; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICaption_init(PyUICaptionObject* self, PyObject* args, PyObject* kwds) + { + //std::cout << "Init called\n"; + static const char* keywords[] = { "x", "y", "text", "font", "fill_color", "outline_color", nullptr }; + float x = 0.0f, y = 0.0f; + char* text; + PyObject* font, fill_color, outline_color; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffzOOO", + const_cast(keywords), &x, &y, &text, &font, &fill_color, &outline_color)) + { + return -1; + } + + // check types for font, fill_color, outline_color + + std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl; + if (font != NULL && !PyObject_IsInstance(font, (PyObject*)&PyFontType)){ + PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance"); + return -1; + } else if (font != NULL) + { + auto font_obj = (PyFontObject*)font; + self->data->text.setFont(font_obj->data->font); + self->font = font; + Py_INCREF(font); + } else + { + // default font + //self->data->text.setFont(Resources::game->getFont()); + } + + self->data->text.setPosition(sf::Vector2f(x, y)); + self->data->text.setString((std::string)text); + self->data->text.setFillColor(sf::Color(0,0,0,255)); + self->data->text.setOutlineColor(sf::Color(128,128,128,255)); + + return 0; + } + + static PyTypeObject PyUICaptionType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.Caption", + .tp_basicsize = sizeof(PyUICaptionObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUICaptionObject* obj = (PyUICaptionObject*)self; + // release reference to font object + if (obj->font) Py_DECREF(obj->font); + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .tp_repr = (reprfunc)PyUICaption_repr, + //.tp_hash = NULL, + //.tp_iter + //.tp_iternext + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("docstring"), + //.tp_methods = PyUIFrame_methods, + //.tp_members = PyUIFrame_members, + .tp_getset = PyUICaption_getsetters, + //.tp_base = NULL, + .tp_init = (initproc)PyUICaption_init, + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + PyUICaptionObject* self = (PyUICaptionObject*)type->tp_alloc(type, 0); + if (self) self->data = std::make_shared(); + return (PyObject*)self; + } + }; +} diff --git a/src/UICollection.cpp b/src/UICollection.cpp new file mode 100644 index 0000000..8133952 --- /dev/null +++ b/src/UICollection.cpp @@ -0,0 +1,2 @@ +#include "UICollection.h" + diff --git a/src/UICollection.h b/src/UICollection.h new file mode 100644 index 0000000..03136f0 --- /dev/null +++ b/src/UICollection.h @@ -0,0 +1,279 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyTexture.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" + +#include "UIDrawable.h" +#include "UICaption.h" +#include "UIFrame.h" +#include "UISprite.h" +#include "UIGrid.h" + +namespace mcrfpydef { + + typedef struct { + PyObject_HEAD + std::shared_ptr>> data; + int index; + int start_size; + } PyUICollectionIterObject; + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICollectionIter_init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds) + { + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return -1; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollectionIter_next(PyUICollectionIterObject* self) + { + if (self->data->size() != self->start_size) + { + PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); + return NULL; + } + + if (self->index > self->start_size - 1) + { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + self->index++; + auto vec = self->data.get(); + if (!vec) + { + PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); + return NULL; + } + auto target = (*vec)[self->index-1]; + // TODO build PyObject* of the correct UIDrawable subclass to return + //return py_instance(target); + return NULL; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollectionIter_repr(PyUICollectionIterObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + ss << "data->size() << " child objects, @ index " << self->index << ")>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + static PyTypeObject PyUICollectionIterType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.UICollectionIter", + .tp_basicsize = sizeof(PyUICollectionIterObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUICollectionIterObject* obj = (PyUICollectionIterObject*)self; + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .tp_repr = (reprfunc)PyUICollectionIter_repr, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), + .tp_iternext = (iternextfunc)PyUICollectionIter_next, + //.tp_getset = PyUICollection_getset, + .tp_init = (initproc)PyUICollectionIter_init, // just raise an exception + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return NULL; + } + }; + + //TODO: add this method to class scope; move implementation to .cpp file + static Py_ssize_t PyUICollection_len(PyUICollectionObject* self) { + return self->data->size(); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollection_getitem(PyUICollectionObject* self, Py_ssize_t index) { + // build a Python version of item at self->data[index] + // Copy pasted:: + auto vec = self->data.get(); + if (!vec) + { + PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); + return NULL; + } + while (index < 0) index += self->data->size(); + if (index > self->data->size() - 1) + { + PyErr_SetString(PyExc_IndexError, "UICollection index out of range"); + return NULL; + } + auto target = (*vec)[index]; + RET_PY_INSTANCE(target); + return NULL; + + + } + + //TODO: add this static array to class scope; move implementation to .cpp file + static PySequenceMethods PyUICollection_sqmethods = { + .sq_length = (lenfunc)PyUICollection_len, + .sq_item = (ssizeargfunc)PyUICollection_getitem, + //.sq_item_by_index = PyUICollection_getitem + //.sq_slice - return a subset of the iterable + //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) + //.sq_ass_slice - cool; no thanks, for now + //.sq_contains - called when `x in o` is executed + //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) + }; + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollection_append(PyUICollectionObject* self, PyObject* o) + { + // if not UIDrawable subclass, reject it + // self->data->push_back( c++ object inside o ); + + // this would be a great use case for .tp_base + if (!PyObject_IsInstance(o, (PyObject*)&PyUIFrameType) && + !PyObject_IsInstance(o, (PyObject*)&PyUISpriteType) && + !PyObject_IsInstance(o, (PyObject*)&PyUICaptionType) && + !PyObject_IsInstance(o, (PyObject*)&PyUIGridType) + ) + { + PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, and Grid objects can be added to UICollection"); + return NULL; + } + + if (PyObject_IsInstance(o, (PyObject*)&PyUIFrameType)) + { + PyUIFrameObject* frame = (PyUIFrameObject*)o; + self->data->push_back(frame->data); + } + if (PyObject_IsInstance(o, (PyObject*)&PyUICaptionType)) + { + PyUICaptionObject* caption = (PyUICaptionObject*)o; + self->data->push_back(caption->data); + } + if (PyObject_IsInstance(o, (PyObject*)&PyUISpriteType)) + { + PyUISpriteObject* sprite = (PyUISpriteObject*)o; + self->data->push_back(sprite->data); + } + if (PyObject_IsInstance(o, (PyObject*)&PyUIGridType)) + { + PyUIGridObject* grid = (PyUIGridObject*)o; + self->data->push_back(grid->data); + } + + Py_INCREF(Py_None); + return Py_None; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollection_remove(PyUICollectionObject* self, PyObject* o) + { + if (!PyLong_Check(o)) + { + PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); + return NULL; + } + long index = PyLong_AsLong(o); + if (index >= self->data->size()) + { + PyErr_SetString(PyExc_ValueError, "Index out of range"); + return NULL; + } + else if (index < 0) + { + PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); + return NULL; + } + + // release the shared pointer at self->data[index]; + self->data->erase(self->data->begin() + index); + Py_INCREF(Py_None); + return Py_None; + } + + //TODO: add this static array to class scope; move implementation to .cpp file + static PyMethodDef PyUICollection_methods[] = { + {"append", (PyCFunction)PyUICollection_append, METH_O}, + //{"extend", (PyCFunction)PyUICollection_extend, METH_O}, // TODO + {"remove", (PyCFunction)PyUICollection_remove, METH_O}, + {NULL, NULL, 0, NULL} + }; + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollection_repr(PyUICollectionObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + ss << "data->size() << " child objects)>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICollection_init(PyUICollectionObject* self, PyObject* args, PyObject* kwds) + { + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return -1; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollection_iter(PyUICollectionObject* self) + { + PyUICollectionIterObject* iterObj; + iterObj = (PyUICollectionIterObject*)PyUICollectionIterType.tp_alloc(&PyUICollectionIterType, 0); + if (iterObj == NULL) { + return NULL; // Failed to allocate memory for the iterator object + } + + iterObj->data = self->data; + iterObj->index = 0; + iterObj->start_size = self->data->size(); + + return (PyObject*)iterObj; + } + + static PyTypeObject PyUICollectionType = { + //PyVarObject_/HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.UICollection", + .tp_basicsize = sizeof(PyUICollectionObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUICollectionObject* obj = (PyUICollectionObject*)self; + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .tp_repr = (reprfunc)PyUICollection_repr, + .tp_as_sequence = &PyUICollection_sqmethods, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("Iterable, indexable collection of UI objects"), + .tp_iter = (getiterfunc)PyUICollection_iter, + .tp_methods = PyUICollection_methods, // append, remove + //.tp_getset = PyUICollection_getset, + .tp_init = (initproc)PyUICollection_init, // just raise an exception + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + // Does PyUICollectionType need __new__ if it's not supposed to be instantiable by the user? + // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return NULL; + } + }; + +} diff --git a/src/UIDrawable.cpp b/src/UIDrawable.cpp new file mode 100644 index 0000000..7cd1178 --- /dev/null +++ b/src/UIDrawable.cpp @@ -0,0 +1,13 @@ +#include "UIDrawable.h" + +UIDrawable::UIDrawable() { click_callable = NULL; } + +void UIDrawable::click_unregister() +{ + click_callable.reset(); +} + +void UIDrawable::render() +{ + render(sf::Vector2f()); +} diff --git a/src/UIDrawable.h b/src/UIDrawable.h new file mode 100644 index 0000000..5fb01e1 --- /dev/null +++ b/src/UIDrawable.h @@ -0,0 +1,162 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyTexture.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" + +class UIFrame; class UICaption; class UISprite; class UIEntity; class UIGrid; + +enum PyObjectsEnum : int +{ + UIFRAME = 1, + UICAPTION, + UISPRITE, + UIGRID +}; + +class UIDrawable +{ +public: + void render(); + virtual void render(sf::Vector2f) = 0; + virtual PyObjectsEnum derived_type() = 0; + + // Mouse input handling - callable object, methods to find event's destination + std::unique_ptr click_callable; + virtual UIDrawable* click_at(sf::Vector2f point) = 0; + void click_register(PyObject*); + void click_unregister(); + + UIDrawable(); +}; + +typedef struct { + PyObject_HEAD + std::shared_ptr>> data; +} PyUICollectionObject; + +namespace mcrfpydef { + //PyObject* py_instance(std::shared_ptr source); + // This function segfaults on tp_alloc for an unknown reason, but works inline with mcrfpydef:: methods. + +#define RET_PY_INSTANCE(target) { \ +switch (target->derived_type()) \ +{ \ + case PyObjectsEnum::UIFRAME: \ + { \ + PyUIFrameObject* o = (PyUIFrameObject*)((&PyUIFrameType)->tp_alloc(&PyUIFrameType, 0)); \ + if (o) \ + { \ + auto p = std::static_pointer_cast(target); \ + o->data = p; \ + auto utarget = o->data; \ + } \ + return (PyObject*)o; \ + } \ + case PyObjectsEnum::UICAPTION: \ + { \ + PyUICaptionObject* o = (PyUICaptionObject*)((&PyUICaptionType)->tp_alloc(&PyUICaptionType, 0)); \ + if (o) \ + { \ + auto p = std::static_pointer_cast(target); \ + o->data = p; \ + auto utarget = o->data; \ + } \ + return (PyObject*)o; \ + } \ + case PyObjectsEnum::UISPRITE: \ + { \ + PyUISpriteObject* o = (PyUISpriteObject*)((&PyUISpriteType)->tp_alloc(&PyUISpriteType, 0)); \ + if (o) \ + { \ + auto p = std::static_pointer_cast(target); \ + o->data = p; \ + auto utarget = o->data; \ + } \ + return (PyObject*)o; \ + } \ + case PyObjectsEnum::UIGRID: \ + { \ + PyUIGridObject* o = (PyUIGridObject*)((&PyUIGridType)->tp_alloc(&PyUIGridType, 0)); \ + if (o) \ + { \ + auto p = std::static_pointer_cast(target); \ + o->data = p; \ + auto utarget = o->data; \ + } \ + return (PyObject*)o; \ + } \ +} \ +} +// end macro definition + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIDrawable_get_click(PyUIGridObject* self, void* closure) { + PyObjectsEnum objtype = static_cast(reinterpret_cast(closure)); // trust me bro, it's an Enum + PyObject* ptr; + + switch (objtype) + { + case PyObjectsEnum::UIFRAME: + ptr = ((PyUIFrameObject*)self)->data->click_callable->borrow(); + break; + case PyObjectsEnum::UICAPTION: + ptr = ((PyUICaptionObject*)self)->data->click_callable->borrow(); + break; + case PyObjectsEnum::UISPRITE: + ptr = ((PyUISpriteObject*)self)->data->click_callable->borrow(); + break; + case PyObjectsEnum::UIGRID: + ptr = ((PyUIGridObject*)self)->data->click_callable->borrow(); + break; + default: + PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _get_click"); + return NULL; + } + if (ptr && ptr != Py_None) + return ptr; + else + return Py_None; +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* closure) { + PyObjectsEnum objtype = static_cast(reinterpret_cast(closure)); // trust me bro, it's an Enum + UIDrawable* target; + switch (objtype) + { + case PyObjectsEnum::UIFRAME: + target = (((PyUIFrameObject*)self)->data.get()); + break; + case PyObjectsEnum::UICAPTION: + target = (((PyUICaptionObject*)self)->data.get()); + break; + case PyObjectsEnum::UISPRITE: + target = (((PyUISpriteObject*)self)->data.get()); + break; + case PyObjectsEnum::UIGRID: + target = (((PyUIGridObject*)self)->data.get()); + break; + default: + PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _set_click"); + return -1; + } + + if (value == Py_None) + { + target->click_unregister(); + } else { + target->click_register(value); + } + return 0; +} + +} diff --git a/src/UIEntity.cpp b/src/UIEntity.cpp new file mode 100644 index 0000000..96738d7 --- /dev/null +++ b/src/UIEntity.cpp @@ -0,0 +1,9 @@ +#include "UIEntity.h" + +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) +{ +} + diff --git a/src/UIEntity.h b/src/UIEntity.h new file mode 100644 index 0000000..a99369b --- /dev/null +++ b/src/UIEntity.h @@ -0,0 +1,40 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyTexture.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" + +#include "UIGridPoint.h" +#include "UIDrawable.h" + +class UIGrid; + +// TODO: make UIEntity a drawable +class UIEntity//: public UIDrawable +{ +public: + //PyObject* self; + std::shared_ptr grid; + std::vector gridstate; + UISprite sprite; + sf::Vector2f position; //(x,y) in grid coordinates; float for animation + void render(sf::Vector2f); //override final; + + UIEntity(); + UIEntity(UIGrid&); + +}; + +typedef struct { + PyObject_HEAD + std::shared_ptr data; + //PyObject* texture; +} PyUIEntityObject; diff --git a/src/UIFrame.cpp b/src/UIFrame.cpp new file mode 100644 index 0000000..6cebd9a --- /dev/null +++ b/src/UIFrame.cpp @@ -0,0 +1,69 @@ +#include "UIFrame.h" +#include "UICollection.h" + +UIDrawable* UIFrame::click_at(sf::Vector2f point) +{ + for (auto e: *children) + { + auto p = e->click_at(point + box.getPosition()); + if (p) + return p; + } + if (click_callable) + { + float x = box.getPosition().x, y = box.getPosition().y, w = box.getSize().x, h = box.getSize().y; + if (point.x > x && point.y > y && point.x < x+w && point.y < y+h) return this; + } + return NULL; +} + +UIFrame::UIFrame() +: outline(0) +{ + children = std::make_shared>>(); + box.setPosition(0, 0); + box.setSize(sf::Vector2f(0, 0)); +} + +UIFrame::UIFrame(float _x, float _y, float _w, float _h) +: outline(0) +{ + box.setPosition(_x, _y); + box.setSize(sf::Vector2f(_w, _h)); + children = std::make_shared>>(); +} + +UIFrame::~UIFrame() +{ + children.reset(); +} + +PyObjectsEnum UIFrame::derived_type() +{ + return PyObjectsEnum::UIFRAME; +} + +void UIFrame::render(sf::Vector2f offset) +{ + box.move(offset); + Resources::game->getWindow().draw(box); + box.move(-offset); + + for (auto drawable : *children) { + drawable->render(offset + box.getPosition()); + } +} + +namespace mcrfpydef { + + // TODO: move to class scope; but this should at least get us compiling + static PyObject* PyUIFrame_get_children(PyUIFrameObject* self, void* closure) + { + // create PyUICollection instance pointing to self->data->children + PyUICollectionObject* o = (PyUICollectionObject*)PyUICollectionType.tp_alloc(&PyUICollectionType, 0); + if (o) + o->data = self->data->children; + return (PyObject*)o; + } +} + diff --git a/src/UIFrame.h b/src/UIFrame.h new file mode 100644 index 0000000..c51f042 --- /dev/null +++ b/src/UIFrame.h @@ -0,0 +1,271 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyColor.h" +#include "PyVector.h" +#include "UIDrawable.h" + +class UIFrame: public UIDrawable +{ +public: + UIFrame(float, float, float, float); + UIFrame(); + ~UIFrame(); + sf::RectangleShape box; + float outline; + std::shared_ptr>> children; + void render(sf::Vector2f) override final; + void move(sf::Vector2f); + PyObjectsEnum derived_type() override final; + virtual UIDrawable* click_at(sf::Vector2f point) override final; +}; + +typedef struct { + PyObject_HEAD + std::shared_ptr data; +} PyUIFrameObject; + +namespace mcrfpydef { + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUIFrame_get_float_member(PyUIFrameObject* self, void* closure) + { + auto member_ptr = reinterpret_cast(closure); + if (member_ptr == 0) + return PyFloat_FromDouble(self->data->box.getPosition().x); + else if (member_ptr == 1) + return PyFloat_FromDouble(self->data->box.getPosition().y); + else if (member_ptr == 2) + return PyFloat_FromDouble(self->data->box.getSize().x); + else if (member_ptr == 3) + return PyFloat_FromDouble(self->data->box.getSize().y); + else if (member_ptr == 4) + return PyFloat_FromDouble(self->data->box.getOutlineThickness()); + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUIFrame_set_float_member(PyUIFrameObject* 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 an integer."); + 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) //outline + self->data->box.setOutlineThickness(val); + return 0; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUIFrame_get_color_member(PyUIFrameObject* self, void* closure) + { + // validate closure (should be impossible to be wrong, but it's thorough) + auto member_ptr = reinterpret_cast(closure); + if (member_ptr != 0 && member_ptr != 1) + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + PyTypeObject* colorType = &PyColorType; + PyObject* pyColor = colorType->tp_alloc(colorType, 0); + if (pyColor == NULL) + { + std::cout << "failure to allocate mcrfpy.Color / PyColorType" << std::endl; + return NULL; + } + PyColorObject* pyColorObj = reinterpret_cast(pyColor); + + // fetch correct member data + sf::Color color; + if (member_ptr == 0) + { + color = self->data->box.getFillColor(); + //return Py_BuildValue("(iii)", color.r, color.g, color.b); + } + else if (member_ptr == 1) + { + color = self->data->box.getOutlineColor(); + //return Py_BuildValue("(iii)", color.r, color.g, color.b); + } + + return PyColor(color).pyObject(); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUIFrame_set_color_member(PyUIFrameObject* self, PyObject* value, void* closure) + { + //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse + auto member_ptr = reinterpret_cast(closure); + int r, g, b, a; + if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) + { + sf::Color c = ((PyColorObject*)value)->data; + r = c.r; g = c.g; b = c.b; a = c.a; + } + else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) + { + // reject non-Color, non-tuple value + PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); + return -1; + } + else // get value from tuples + { + r = PyLong_AsLong(PyTuple_GetItem(value, 0)); + g = PyLong_AsLong(PyTuple_GetItem(value, 1)); + b = PyLong_AsLong(PyTuple_GetItem(value, 2)); + a = 255; + + if (PyTuple_Size(value) == 4) + { + a = PyLong_AsLong(PyTuple_GetItem(value, 3)); + } + } + + if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) + { + PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); + return -1; + } + + if (member_ptr == 0) + { + self->data->box.setFillColor(sf::Color(r, g, b, a)); + } + else if (member_ptr == 1) + { + self->data->box.setOutlineColor(sf::Color(r, g, b, a)); + } + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return -1; + } + + return 0; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUIFrame_get_children(PyUIFrameObject*, void*); // forward declaration until UICollection is defined + // implementation after the PyUICollectionType definition + + //TODO: add this static array to class scope; move implementation to .cpp file + static PyGetSetDef PyUIFrame_getsetters[] = { + {"x", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "X coordinate of top-left corner", (void*)0}, + {"y", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Y coordinate of top-left corner", (void*)1}, + {"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, + {"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, + {"outline", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Thickness of the border", (void*)4}, + {"fill_color", (getter)PyUIFrame_get_color_member, (setter)PyUIFrame_set_color_member, "Fill color of the rectangle", (void*)0}, + {"outline_color", (getter)PyUIFrame_get_color_member, (setter)PyUIFrame_set_color_member, "Outline color of the rectangle", (void*)1}, + {"children", (getter)PyUIFrame_get_children, NULL, "UICollection of objects on top of this one", NULL}, + {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIFRAME}, + {NULL} + }; + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUIFrame_repr(PyUIFrameObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + auto box = self->data->box; + auto fc = box.getFillColor(); + auto oc = box.getOutlineColor(); + ss << "data->children->size() << " child objects" << + ")>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUIFrame_init(PyUIFrameObject* self, PyObject* args, PyObject* kwds) + { + //std::cout << "Init called\n"; + static const char* keywords[] = { "x", "y", "w", "h", "fill_color", "outline_color", "outline", nullptr }; + float x = 0.0f, y = 0.0f, w = 0.0f, h=0.0f, outline=0.0f; + PyObject* fill_color = 0; + PyObject* outline_color = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffff|OOf", const_cast(keywords), &x, &y, &w, &h, &fill_color, &outline_color, &outline)) + { + return -1; + } + + self->data->box.setPosition(sf::Vector2f(x, y)); + self->data->box.setSize(sf::Vector2f(w, h)); + self->data->box.setOutlineThickness(outline); + // getsetter abuse because I haven't standardized Color object parsing (TODO) + int err_val = 0; + if (fill_color && fill_color != Py_None) err_val = PyUIFrame_set_color_member(self, fill_color, (void*)0); + else self->data->box.setFillColor(sf::Color(0,0,0,255)); + if (err_val) return err_val; + if (outline_color && outline_color != Py_None) err_val = PyUIFrame_set_color_member(self, outline_color, (void*)1); + else self->data->box.setOutlineColor(sf::Color(128,128,128,255)); + if (err_val) return err_val; + return 0; + } + + static PyTypeObject PyUIFrameType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.Frame", + .tp_basicsize = sizeof(PyUIFrameObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUIFrameObject* obj = (PyUIFrameObject*)self; + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .tp_repr = (reprfunc)PyUIFrame_repr, + //.tp_hash = NULL, + //.tp_iter + //.tp_iternext + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("docstring"), + //.tp_methods = PyUIFrame_methods, + //.tp_members = PyUIFrame_members, + .tp_getset = PyUIFrame_getsetters, + //.tp_base = NULL, + .tp_init = (initproc)PyUIFrame_init, + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + PyUIFrameObject* self = (PyUIFrameObject*)type->tp_alloc(type, 0); + if (self) self->data = std::make_shared(); + return (PyObject*)self; + } + }; +} diff --git a/src/UIGrid.cpp b/src/UIGrid.cpp new file mode 100644 index 0000000..207114b --- /dev/null +++ b/src/UIGrid.cpp @@ -0,0 +1,190 @@ +#include "UIGrid.h" + +UIGrid::UIGrid() {} + +UIGrid::UIGrid(int gx, int gy, std::shared_ptr _ptex, sf::Vector2f _xy, sf::Vector2f _wh) +: grid_x(gx), grid_y(gy), + zoom(1.0f), center_x((gx/2) * _ptex->sprite_width), center_y((gy/2) * _ptex->sprite_height), + ptex(_ptex), points(gx * gy) +{ + entities = std::make_shared>>(); + + box.setSize(_wh); + box.setPosition(_xy); + + box.setFillColor(sf::Color(0,0,0,0)); + // 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 = ptex->sprite(0); + + output.setTextureRect( + sf::IntRect(0, 0, + box.getSize().x, box.getSize().y)); + output.setPosition(box.getPosition()); + // textures are upside-down inside renderTexture + output.setTexture(renderTexture.getTexture()); + +} + +void UIGrid::update() {} + + +void UIGrid::render(sf::Vector2f) +{ + 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 / ptex->sprite_width; + float center_y_sq = center_y / ptex->sprite_height; + + float width_sq = box.getSize().x / (ptex->sprite_width * zoom); + float height_sq = box.getSize().y / (ptex->sprite_height * zoom); + float left_edge = center_x_sq - (width_sq / 2.0); + float top_edge = center_y_sq - (height_sq / 2.0); + + int left_spritepixels = center_x - (box.getSize().x / 2.0 / zoom); + int top_spritepixels = center_y - (box.getSize().y / 2.0 / zoom); + + //sprite.setScale(sf::Vector2f(zoom, zoom)); + sf::RectangleShape r; // for colors and overlays + r.setSize(sf::Vector2f(ptex->sprite_width * zoom, ptex->sprite_height * zoom)); + r.setOutlineThickness(0); + + int x_limit = left_edge + width_sq + 2; + if (x_limit > grid_x) x_limit = grid_x; + + int y_limit = top_edge + height_sq + 2; + if (y_limit > grid_y) y_limit = grid_y; + + // base layer - bottom color, tile sprite ("ground") + for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0); + x < x_limit; //x < view_width; + x+=1) + { + //for (float y = (top_edge >= 0 ? top_edge : 0); + for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0); + y < y_limit; //y < view_height; + y+=1) + { + auto pixel_pos = sf::Vector2f( + (x*ptex->sprite_width - left_spritepixels) * zoom, + (y*ptex->sprite_height - top_spritepixels) * zoom ); + + auto gridpoint = at(std::floor(x), std::floor(y)); + + //sprite.setPosition(pixel_pos); + + r.setPosition(pixel_pos); + r.setFillColor(gridpoint.color); + renderTexture.draw(r); + + // tilesprite + // if discovered but not visible, set opacity to 90% + // if not discovered... just don't draw it? + if (gridpoint.tilesprite != -1) { + sprite = ptex->sprite(gridpoint.tilesprite, pixel_pos, sf::Vector2f(zoom, zoom)); //setSprite(gridpoint.tilesprite);; + renderTexture.draw(sprite); + } + } + } + + // middle layer - entities + // disabling entity rendering until I can render their UISprite inside the rendertexture (not directly to window) + for (auto e : *entities) { + // TODO skip out-of-bounds entities (grid square not visible at all, check for partially on visible grid squares / floating point grid position) + //auto drawent = e->cGrid->indexsprite.drawable(); + auto& drawent = e->sprite; + //drawent.setScale(zoom, zoom); + drawent.setScale(sf::Vector2f(zoom, zoom)); + auto pixel_pos = sf::Vector2f( + (e->position.x*ptex->sprite_width - left_spritepixels) * zoom, + (e->position.y*ptex->sprite_height - top_spritepixels) * zoom ); + //drawent.setPosition(pixel_pos); + //renderTexture.draw(drawent); + drawent.render(pixel_pos, renderTexture); + } + + + // top layer - opacity for discovered / visible status (debug, basically) + /* // Disabled until I attach a "perspective" + for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0); + x < x_limit; //x < view_width; + x+=1) + { + //for (float y = (top_edge >= 0 ? top_edge : 0); + for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0); + y < y_limit; //y < view_height; + y+=1) + { + + auto pixel_pos = sf::Vector2f( + (x*itex->grid_size - left_spritepixels) * zoom, + (y*itex->grid_size - top_spritepixels) * zoom ); + + auto gridpoint = at(std::floor(x), std::floor(y)); + + sprite.setPosition(pixel_pos); + + r.setPosition(pixel_pos); + + // visible & discovered layers for testing purposes + if (!gridpoint.discovered) { + r.setFillColor(sf::Color(16, 16, 20, 192)); // 255 opacity for actual blackout + renderTexture.draw(r); + } else if (!gridpoint.visible) { + r.setFillColor(sf::Color(32, 32, 40, 128)); + renderTexture.draw(r); + } + + // overlay + + // uisprite + } + } + */ + + // grid lines for testing & validation + /* + sf::Vertex line[] = + { + sf::Vertex(sf::Vector2f(0, 0), sf::Color::Red), + sf::Vertex(box.getSize(), sf::Color::Red), + + }; + + renderTexture.draw(line, 2, sf::Lines); + sf::Vertex lineb[] = + { + sf::Vertex(sf::Vector2f(0, box.getSize().y), sf::Color::Blue), + sf::Vertex(sf::Vector2f(box.getSize().x, 0), sf::Color::Blue), + + }; + + renderTexture.draw(lineb, 2, sf::Lines); + */ + + // render to window + renderTexture.display(); + Resources::game->getWindow().draw(output); + +} + +UIGridPoint& UIGrid::at(int x, int y) +{ + return points[y * grid_x + x]; +} + +PyObjectsEnum UIGrid::derived_type() +{ + return PyObjectsEnum::UIGRID; +} + +std::shared_ptr UIGrid::getTexture() +{ + return ptex; +} diff --git a/src/UIGrid.h b/src/UIGrid.h new file mode 100644 index 0000000..e55026b --- /dev/null +++ b/src/UIGrid.h @@ -0,0 +1,570 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyTexture.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" + +#include "UIGridPoint.h" +#include "UIEntity.h" +#include "UIDrawable.h" + +class UIGrid: public UIDrawable +{ +private: + std::shared_ptr ptex; +public: + UIGrid(); + //UIGrid(int, int, IndexTexture*, float, float, float, float); + UIGrid(int, int, std::shared_ptr, sf::Vector2f, sf::Vector2f); + void update(); + void render(sf::Vector2f) override final; + UIGridPoint& at(int, int); + PyObjectsEnum derived_type() override final; + //void setSprite(int); + virtual UIDrawable* click_at(sf::Vector2f point) override final; + + int grid_x, grid_y; + //int grid_size; // grid sizes are implied by IndexTexture now + sf::RectangleShape box; + float center_x, center_y, zoom; + //IndexTexture* itex; + std::shared_ptr getTexture(); + sf::Sprite sprite, output; + sf::RenderTexture renderTexture; + std::vector points; + std::shared_ptr>> entities; +}; + +typedef struct { + PyObject_HEAD + std::shared_ptr data; + //PyObject* texture; +} PyUIGridObject; + +namespace mcrfpydef { +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGrid_init(PyUIGridObject* self, PyObject* args, PyObject* kwds) { + int grid_x, grid_y; + PyObject* textureObj; + float box_x, box_y, box_w, box_h; + + if (!PyArg_ParseTuple(args, "iiOffff", &grid_x, &grid_y, &textureObj, &box_x, &box_y, &box_w, &box_h)) { + return -1; // If parsing fails, return an error + } + + // Convert PyObject texture to IndexTexture* + // This requires the texture object to have been initialized similar to UISprite's texture handling + if (!PyObject_IsInstance(textureObj, (PyObject*)&PyTextureType)) { + PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); + return -1; + } + PyTextureObject* pyTexture = reinterpret_cast(textureObj); + // TODO (7DRL day 2, item 4.) use shared_ptr / PyTextureObject on UIGrid + //IndexTexture* texture = pyTexture->data.get(); + + // Initialize UIGrid + //self->data = new UIGrid(grid_x, grid_y, texture, sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); + self->data = std::make_shared(grid_x, grid_y, pyTexture->data, + sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); + return 0; // Success +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGrid_get_grid_size(PyUIGridObject* self, void* closure) { + return Py_BuildValue("(ii)", self->data->grid_x, self->data->grid_y); +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGrid_get_position(PyUIGridObject* self, void* closure) { + auto& box = self->data->box; + return Py_BuildValue("(ff)", box.getPosition().x, box.getPosition().y); +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGrid_set_position(PyUIGridObject* self, PyObject* value, void* closure) { + float x, y; + if (!PyArg_ParseTuple(value, "ff", &x, &y)) { + PyErr_SetString(PyExc_ValueError, "Position must be a tuple of two floats"); + return -1; + } + self->data->box.setPosition(x, y); + return 0; +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGrid_get_size(PyUIGridObject* self, void* closure) { + auto& box = self->data->box; + return Py_BuildValue("(ff)", box.getSize().x, box.getSize().y); +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGrid_set_size(PyUIGridObject* self, PyObject* value, void* closure) { + float w, h; + if (!PyArg_ParseTuple(value, "ff", &w, &h)) { + PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); + return -1; + } + self->data->box.setSize(sf::Vector2f(w, h)); + return 0; +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGrid_get_center(PyUIGridObject* self, void* closure) { + return Py_BuildValue("(ff)", self->data->center_x, self->data->center_y); +} + +//TODO: add this method to class scope; move implementation to .cpp file +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; +} + +//TODO: add this method to class scope; move implementation to .cpp file +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; + } +} + +//TODO: add this method to class scope; move implementation to .cpp file +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) { + Py_INCREF(self->texture); + return self->texture; +} +*/ +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { + //return self->data->getTexture()->pyObject(); + PyTextureObject* obj = (PyTextureObject*)((&PyTextureType)->tp_alloc(&PyTextureType, 0)); + obj->data = self->data->getTexture(); + return (PyObject*)obj; +} + +//TODO: add this method to class scope; move implementation to .cpp file +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; + } + if (x < 0 || x >= self->data->grid_x) { + PyErr_SetString(PyExc_ValueError, "x value out of range (0, Grid.grid_y)"); + return NULL; + } + if (y < 0 || y >= self->data->grid_y) { + PyErr_SetString(PyExc_ValueError, "y value out of range (0, Grid.grid_y)"); + return NULL; + } + + PyUIGridPointObject* obj = (PyUIGridPointObject*)((&PyUIGridPointType)->tp_alloc(&PyUIGridPointType, 0)); + //auto target = std::static_pointer_cast(target); + obj->data = &(self->data->points[x + self->data->grid_x * y]); + obj->grid = self->data; + return (PyObject*)obj; +} + +//TODO: add this static array to class scope; move implementation to .cpp file +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}, + + {"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}, + + {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIGRID}, + + {"texture", (getter)PyUIGrid_get_texture, NULL, "Texture of the grid", NULL}, //TODO 7DRL-day2-item5 + {NULL} /* Sentinel */ +}; + + +/* // TODO standard pointer would need deleted, but I opted for a shared pointer. tp_dealloc currently not even defined in the PyTypeObject +static void PyUIGrid_dealloc(PyUIGridObject* self) { + delete self->data; // Clean up the allocated UIGrid object + Py_TYPE(self)->tp_free((PyObject*)self); +} +*/ + + static PyTypeObject PyUIGridType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.Grid", + .tp_basicsize = sizeof(PyUIGridObject), + .tp_itemsize = 0, + //.tp_dealloc = (destructor)[](PyObject* self) + //{ + // PyUIGridObject* obj = (PyUIGridObject*)self; + // obj->data.reset(); + // Py_TYPE(self)->tp_free(self); + //}, + //TODO - PyUIGrid REPR def: + // .tp_repr = (reprfunc)PyUIGrid_repr, + //.tp_hash = NULL, + //.tp_iter + //.tp_iternext + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("docstring"), + .tp_methods = PyUIGrid_methods, + //.tp_members = PyUIGrid_members, + .tp_getset = PyUIGrid_getsetters, + //.tp_base = NULL, + .tp_init = (initproc)PyUIGrid_init, + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + PyUIGridObject* self = (PyUIGridObject*)type->tp_alloc(type, 0); + if (self) self->data = std::make_shared(); + return (PyObject*)self; + } + }; + + typedef struct { + PyObject_HEAD + std::shared_ptr>> data; + int index; + int start_size; + } PyUIEntityCollectionIterObject; + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static int PyUIEntityCollectionIter_init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds) + { + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return -1; + } + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyObject* PyUIEntityCollectionIter_next(PyUIEntityCollectionIterObject* self) + { + if (self->data->size() != self->start_size) + { + PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); + return NULL; + } + + if (self->index > self->start_size - 1) + { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + self->index++; + auto vec = self->data.get(); + if (!vec) + { + PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); + return NULL; + } + // Advance list iterator since Entities are not stored in a vector (if this code even worked) + // vectors only: //auto target = (*vec)[self->index-1]; + //auto l_front = (*vec).begin(); + //std::advance(l_front, self->index-1); + + // TODO build PyObject* of the correct UIDrawable subclass to return + //return py_instance(target); + return NULL; + } + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyObject* PyUIEntityCollectionIter_repr(PyUIEntityCollectionIterObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + ss << "data->size() << " child objects, @ index " << self->index << ")>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + static PyTypeObject PyUIEntityCollectionIterType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.UICollectionIter", + .tp_basicsize = sizeof(PyUIEntityCollectionIterObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUIEntityCollectionIterObject* obj = (PyUIEntityCollectionIterObject*)self; + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .tp_repr = (reprfunc)PyUIEntityCollectionIter_repr, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), + .tp_iternext = (iternextfunc)PyUIEntityCollectionIter_next, + //.tp_getset = PyUIEntityCollection_getset, + .tp_init = (initproc)PyUIEntityCollectionIter_init, // just raise an exception + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return NULL; + } + }; + + + typedef struct { + PyObject_HEAD + std::shared_ptr>> data; + std::shared_ptr grid; + } PyUIEntityCollectionObject; + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static Py_ssize_t PyUIEntityCollection_len(PyUIEntityCollectionObject* self) { + return self->data->size(); + } + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyObject* PyUIEntityCollection_getitem(PyUIEntityCollectionObject* self, Py_ssize_t index) { + // build a Python version of item at self->data[index] + // Copy pasted:: + auto vec = self->data.get(); + if (!vec) + { + PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); + return NULL; + } + while (index < 0) index += self->data->size(); + if (index > self->data->size() - 1) + { + PyErr_SetString(PyExc_IndexError, "EntityCollection index out of range"); + return NULL; + } + auto l_begin = (*vec).begin(); + std::advance(l_begin, index); + auto target = *l_begin; //auto target = (*vec)[index]; + //RET_PY_INSTANCE(target); + // construct and return an entity object that points directly into the UIGrid's entity vector + PyUIEntityObject* o = (PyUIEntityObject*)((&PyUIEntityType)->tp_alloc(&PyUIEntityType, 0)); + auto p = std::static_pointer_cast(target); + o->data = p; + return (PyObject*)o; + return NULL; + + + } + + //TODO: add this static array to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PySequenceMethods PyUIEntityCollection_sqmethods = { + .sq_length = (lenfunc)PyUIEntityCollection_len, + .sq_item = (ssizeargfunc)PyUIEntityCollection_getitem, + //.sq_item_by_index = PyUIEntityCollection_getitem + //.sq_slice - return a subset of the iterable + //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) + //.sq_ass_slice - cool; no thanks, for now + //.sq_contains - called when `x in o` is executed + //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) + }; + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyObject* PyUIEntityCollection_append(PyUIEntityCollectionObject* self, PyObject* o) + { + // if not UIDrawable subclass, reject it + // self->data->push_back( c++ object inside o ); + + // this would be a great use case for .tp_base + if (!PyObject_IsInstance(o, (PyObject*)&PyUIEntityType)) + { + PyErr_SetString(PyExc_TypeError, "Only Entity objects can be added to EntityCollection"); + return NULL; + } + PyUIEntityObject* entity = (PyUIEntityObject*)o; + self->data->push_back(entity->data); + entity->data->grid = self->grid; + + Py_INCREF(Py_None); + return Py_None; + } + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyObject* PyUIEntityCollection_remove(PyUIEntityCollectionObject* self, PyObject* o) + { + if (!PyLong_Check(o)) + { + PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); + return NULL; + } + long index = PyLong_AsLong(o); + if (index >= self->data->size()) + { + PyErr_SetString(PyExc_ValueError, "Index out of range"); + return NULL; + } + else if (index < 0) + { + PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); + return NULL; + } + + // release the shared pointer at correct part of the list + self->data->erase(std::next(self->data->begin(), index)); + Py_INCREF(Py_None); + return Py_None; + } + + //TODO: add this static array to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyMethodDef PyUIEntityCollection_methods[] = { + {"append", (PyCFunction)PyUIEntityCollection_append, METH_O}, + //{"extend", (PyCFunction)PyUIEntityCollection_extend, METH_O}, // TODO + {"remove", (PyCFunction)PyUIEntityCollection_remove, METH_O}, + {NULL, NULL, 0, NULL} + }; + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyObject* PyUIEntityCollection_repr(PyUIEntityCollectionObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + ss << "data->size() << " child objects)>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static int PyUIEntityCollection_init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds) + { + PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); + return -1; + } + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyObject* PyUIEntityCollection_iter(PyUIEntityCollectionObject* self) + { + PyUIEntityCollectionIterObject* iterObj; + iterObj = (PyUIEntityCollectionIterObject*)PyUIEntityCollectionIterType.tp_alloc(&PyUIEntityCollectionIterType, 0); + if (iterObj == NULL) { + return NULL; // Failed to allocate memory for the iterator object + } + + iterObj->data = self->data; + iterObj->index = 0; + iterObj->start_size = self->data->size(); + + return (PyObject*)iterObj; + } + static PyTypeObject PyUIEntityCollectionType = { + //PyVarObject_/HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.EntityCollection", + .tp_basicsize = sizeof(PyUIEntityCollectionObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUIEntityCollectionObject* obj = (PyUIEntityCollectionObject*)self; + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .tp_repr = (reprfunc)PyUIEntityCollection_repr, + .tp_as_sequence = &PyUIEntityCollection_sqmethods, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("Iterable, indexable collection of Entities"), + .tp_iter = (getiterfunc)PyUIEntityCollection_iter, + .tp_methods = PyUIEntityCollection_methods, // append, remove + //.tp_getset = PyUIEntityCollection_getset, + .tp_init = (initproc)PyUIEntityCollection_init, // just raise an exception + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + // Does PyUIEntityCollectionType need __new__ if it's not supposed to be instantiable by the user? + // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? + PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); + return NULL; + } + }; + + //TODO: add this method to class scope; move implementation to .cpp file + // Grid's get_children def must follow the EntityCollection def + static PyObject* PyUIGrid_get_children(PyUIGridObject* self, void* closure) + { + // create PyUICollection instance pointing to self->data->children + PyUIEntityCollectionObject* o = (PyUIEntityCollectionObject*)PyUIEntityCollectionType.tp_alloc(&PyUIEntityCollectionType, 0); + 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/UIGridPoint.cpp b/src/UIGridPoint.cpp new file mode 100644 index 0000000..18fffc7 --- /dev/null +++ b/src/UIGridPoint.cpp @@ -0,0 +1,6 @@ +#include "UIGridPoint.h" + +UIGridPoint::UIGridPoint() +: color(1.0f, 1.0f, 1.0f), color_overlay(0.0f, 0.0f, 0.0f), walkable(false), transparent(false), + tilesprite(-1), tile_overlay(-1), uisprite(-1) +{} diff --git a/src/UIGridPoint.h b/src/UIGridPoint.h new file mode 100644 index 0000000..e611ff7 --- /dev/null +++ b/src/UIGridPoint.h @@ -0,0 +1,212 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyTexture.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" + +// UIGridPoint - revised grid data for each point +class UIGridPoint +{ +public: + sf::Color color, color_overlay; + bool walkable, transparent; + int tilesprite, tile_overlay, uisprite; + UIGridPoint(); +}; + +// UIGridPointState - entity-specific info for each cell +class UIGridPointState +{ +public: + bool visible, discovered; +}; + +typedef struct { + PyObject_HEAD + UIGridPoint* data; + std::shared_ptr grid; +} PyUIGridPointObject; + +typedef struct { + PyObject_HEAD + UIGridPointState* data; + std::shared_ptr grid; + std::shared_ptr entity; +} PyUIGridPointStateObject; + +namespace mcrfpydef { +// TODO: question: are sfColor_to_PyObject and PyObject_to_sfColor duplicitive? How does UIFrame get/set colors? + +//TODO: add this method to class scope; move implementation to .cpp file; reconsider for moving to "UIBase.h/.cpp" +// Utility function to convert sf::Color to PyObject* +static PyObject* sfColor_to_PyObject(sf::Color color) { + return Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a); +} + +//TODO: add this method to class scope; move implementation to .cpp file; reconsider for moving to "UIBase.h/.cpp" +// Utility function to convert PyObject* to sf::Color +static sf::Color PyObject_to_sfColor(PyObject* obj) { + int r, g, b, a = 255; // Default alpha to fully opaque if not specified + if (!PyArg_ParseTuple(obj, "iii|i", &r, &g, &b, &a)) { + return sf::Color(); // Return default color on parse error + } + return sf::Color(r, g, b, a); +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGridPoint_get_color(PyUIGridPointObject* self, void* closure) { + if (reinterpret_cast(closure) == 0) { // color + return sfColor_to_PyObject(self->data->color); + } else { // color_overlay + return sfColor_to_PyObject(self->data->color_overlay); + } +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGridPoint_set_color(PyUIGridPointObject* self, PyObject* value, void* closure) { + sf::Color color = PyObject_to_sfColor(value); + if (reinterpret_cast(closure) == 0) { // color + self->data->color = color; + } else { // color_overlay + self->data->color_overlay = color; + } + return 0; +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGridPoint_get_bool_member(PyUIGridPointObject* self, void* closure) { + if (reinterpret_cast(closure) == 0) { // walkable + return PyBool_FromLong(self->data->walkable); + } else { // transparent + return PyBool_FromLong(self->data->transparent); + } +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGridPoint_set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure) { + if (value == Py_True) { + if (reinterpret_cast(closure) == 0) { // walkable + self->data->walkable = true; + } else { // transparent + self->data->transparent = true; + } + } else if (value == Py_False) { + if (reinterpret_cast(closure) == 0) { // walkable + self->data->walkable = false; + } else { // transparent + self->data->transparent = false; + } + } else { + PyErr_SetString(PyExc_ValueError, "Expected a boolean value"); + return -1; + } + return 0; +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGridPoint_get_int_member(PyUIGridPointObject* self, void* closure) { + switch(reinterpret_cast(closure)) { + case 0: return PyLong_FromLong(self->data->tilesprite); + case 1: return PyLong_FromLong(self->data->tile_overlay); + case 2: return PyLong_FromLong(self->data->uisprite); + default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return nullptr; + } +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGridPoint_set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure) { + long val = PyLong_AsLong(value); + if (PyErr_Occurred()) return -1; + + switch(reinterpret_cast(closure)) { + case 0: self->data->tilesprite = val; break; + case 1: self->data->tile_overlay = val; break; + case 2: self->data->uisprite = val; break; + default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return -1; + } + return 0; +} + +//TODO: add this static array to class scope; move implementation to .cpp file +static PyGetSetDef PyUIGridPoint_getsetters[] = { + {"color", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color", (void*)0}, + {"color_overlay", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color overlay", (void*)1}, + {"walkable", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint walkable", (void*)0}, + {"transparent", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint transparent", (void*)1}, + {"tilesprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile sprite index", (void*)0}, + {"tile_overlay", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile overlay sprite index", (void*)1}, + {"uisprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "UI sprite index", (void*)2}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject PyUIGridPointType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.GridPoint", + .tp_basicsize = sizeof(PyUIGridPointObject), + .tp_itemsize = 0, + // Methods omitted for brevity + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "UIGridPoint objects", + .tp_getset = PyUIGridPoint_getsetters, + //.tp_init = (initproc)PyUIGridPoint_init, // TODO Define the init function + .tp_new = PyType_GenericNew, +}; + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGridPointState_get_bool_member(PyUIGridPointStateObject* self, void* closure) { + if (reinterpret_cast(closure) == 0) { // visible + return PyBool_FromLong(self->data->visible); + } else { // discovered + return PyBool_FromLong(self->data->discovered); + } +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGridPointState_set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure) { + if (!PyBool_Check(value)) { + PyErr_SetString(PyExc_TypeError, "Value must be a boolean"); + return -1; + } + + int truthValue = PyObject_IsTrue(value); + if (truthValue < 0) { + return -1; // PyObject_IsTrue returns -1 on error + } + + if (reinterpret_cast(closure) == 0) { // visible + self->data->visible = truthValue; + } else { // discovered + self->data->discovered = truthValue; + } + + return 0; +} + +//TODO: add this static array to class scope; move implementation to .cpp file +static PyGetSetDef PyUIGridPointState_getsetters[] = { + {"visible", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Is the GridPointState visible", (void*)0}, + {"discovered", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Has the GridPointState been discovered", (void*)1}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject PyUIGridPointStateType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.GridPointState", + .tp_basicsize = sizeof(PyUIGridPointStateObject), + .tp_itemsize = 0, + // Methods omitted for brevity + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "UIGridPointState objects", // TODO: Add PyUIGridPointState tp_init + .tp_getset = PyUIGridPointState_getsetters, + .tp_new = PyType_GenericNew, +}; + +} diff --git a/src/UISprite.cpp b/src/UISprite.cpp new file mode 100644 index 0000000..1f06be1 --- /dev/null +++ b/src/UISprite.cpp @@ -0,0 +1,81 @@ +#include "UISprite.h" + +UIDrawable* UISprite::click_at(sf::Vector2f point) +{ + if (click_callable) + { + if(sprite.getGlobalBounds().contains(point)) return this; + } + return NULL; +} + +UISprite::UISprite() {} + +UISprite::UISprite(std::shared_ptr _ptex, int _sprite_index, sf::Vector2f _pos, float _scale) +: ptex(_ptex), sprite_index(_sprite_index) +{ + sprite = ptex->sprite(sprite_index, _pos, sf::Vector2f(_scale, _scale)); +} + +void UISprite::render(sf::Vector2f offset) +{ + sprite.move(offset); + Resources::game->getWindow().draw(sprite); + sprite.move(-offset); +} + +void UISprite::render(sf::Vector2f offset, sf::RenderTexture& target) +{ + sprite.move(offset); + target.draw(sprite); + sprite.move(-offset); +} + +void UISprite::setPosition(sf::Vector2f pos) +{ + sprite.setPosition(pos); +} + +void UISprite::setScale(sf::Vector2f s) +{ + sprite.setScale(s); +} + +void UISprite::setTexture(std::shared_ptr _ptex, int _sprite_index) +{ + ptex = _ptex; + if (_sprite_index != -1) // if you are changing textures, there's a good chance you need a new index too + sprite_index = _sprite_index; + sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale()); +} + +void UISprite::setSpriteIndex(int _sprite_index) +{ + sprite_index = _sprite_index; + sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale()); +} + +sf::Vector2f UISprite::getScale() +{ + return sprite.getScale(); +} + +sf::Vector2f UISprite::getPosition() +{ + return sprite.getPosition(); +} + +std::shared_ptr UISprite::getTexture() +{ + return ptex; +} + +int UISprite::getSpriteIndex() +{ + return sprite_index; +} + +PyObjectsEnum UISprite::derived_type() +{ + return PyObjectsEnum::UISPRITE; +} diff --git a/src/UISprite.h b/src/UISprite.h new file mode 100644 index 0000000..4397c2c --- /dev/null +++ b/src/UISprite.h @@ -0,0 +1,223 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyTexture.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" +#include "UIDrawable.h" + +class UISprite: public UIDrawable +{ +private: + int sprite_index; + sf::Sprite sprite; +protected: + std::shared_ptr ptex; +public: + UISprite(); + UISprite(std::shared_ptr, int, sf::Vector2f, float); + void update(); + void render(sf::Vector2f) override final; + virtual UIDrawable* click_at(sf::Vector2f point) override final; + + void render(sf::Vector2f, sf::RenderTexture&); + + void setPosition(sf::Vector2f); + sf::Vector2f getPosition(); + void setScale(sf::Vector2f); + sf::Vector2f getScale(); + void setSpriteIndex(int); + int getSpriteIndex(); + + void setTexture(std::shared_ptr _ptex, int _sprite_index=-1); + std::shared_ptr getTexture(); + + PyObjectsEnum derived_type() override final; +}; + +typedef struct { + PyObject_HEAD + std::shared_ptr data; +} PyUISpriteObject; + +namespace mcrfpydef { + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUISprite_get_float_member(PyUISpriteObject* self, void* closure) + { + auto member_ptr = reinterpret_cast(closure); + if (member_ptr == 0) + return PyFloat_FromDouble(self->data->getPosition().x); + else if (member_ptr == 1) + return PyFloat_FromDouble(self->data->getPosition().y); + else if (member_ptr == 2) + return PyFloat_FromDouble(self->data->getScale().x); // scale X and Y are identical, presently + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUISprite_set_float_member(PyUISpriteObject* 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->setPosition(sf::Vector2f(val, self->data->getPosition().y)); + else if (member_ptr == 1) //y + self->data->setPosition(sf::Vector2f(self->data->getPosition().x, val)); + else if (member_ptr == 2) // scale + self->data->setScale(sf::Vector2f(val, val)); + return 0; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUISprite_get_int_member(PyUISpriteObject* self, void* closure) + { + auto member_ptr = reinterpret_cast(closure); + if (true) {} + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + + return PyLong_FromDouble(self->data->getSpriteIndex()); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUISprite_set_int_member(PyUISpriteObject* self, PyObject* value, void* closure) + { + int val; + auto member_ptr = reinterpret_cast(closure); + if (PyLong_Check(value)) + { + val = PyLong_AsLong(value); + } + else + { + PyErr_SetString(PyExc_TypeError, "Value must be an integer."); + return -1; + } + self->data->setSpriteIndex(val); + return 0; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUISprite_get_texture(PyUISpriteObject* self, void* closure) + { + return self->data->getTexture()->pyObject(); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUISprite_set_texture(PyUISpriteObject* self, PyObject* value, void* closure) + { + return -1; + } + + //TODO: add this method to static array scope; move implementation to .cpp file + static PyGetSetDef PyUISprite_getsetters[] = { + {"x", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "X coordinate of top-left corner", (void*)0}, + {"y", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "Y coordinate of top-left corner", (void*)1}, + {"scale", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "Size factor", (void*)2}, + {"sprite_number", (getter)PyUISprite_get_int_member, (setter)PyUISprite_set_int_member, "Which sprite on the texture is shown", NULL}, + {"texture", (getter)PyUISprite_get_texture, (setter)PyUISprite_set_texture, "Texture object", NULL}, + {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UISPRITE}, + {NULL} + }; + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUISprite_repr(PyUISpriteObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + //auto sprite = self->data->sprite; + ss << ""; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUISprite_init(PyUISpriteObject* self, PyObject* args, PyObject* kwds) + { + //std::cout << "Init called\n"; + static const char* keywords[] = { "x", "y", "texture", "sprite_index", "scale", nullptr }; + float x = 0.0f, y = 0.0f, scale = 1.0f; + int sprite_index; + PyObject* texture; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffOif", + const_cast(keywords), &x, &y, &texture, &sprite_index, &scale)) + { + return -1; + } + + // check types for texture + if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&PyTextureType)){ + PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); + return -1; + } + auto pytexture = (PyTextureObject*)texture; + self->data = std::make_shared(pytexture->data, sprite_index, sf::Vector2f(x, y), scale); + self->data->setPosition(sf::Vector2f(x, y)); + + return 0; + } + + static PyTypeObject PyUISpriteType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.Sprite", + .tp_basicsize = sizeof(PyUISpriteObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUISpriteObject* obj = (PyUISpriteObject*)self; + // release reference to font object + //if (obj->texture) Py_DECREF(obj->texture); + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .tp_repr = (reprfunc)PyUISprite_repr, + //.tp_hash = NULL, + //.tp_iter + //.tp_iternext + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("docstring"), + //.tp_methods = PyUIFrame_methods, + //.tp_members = PyUIFrame_members, + .tp_getset = PyUISprite_getsetters, + //.tp_base = NULL, + .tp_init = (initproc)PyUISprite_init, + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + PyUISpriteObject* self = (PyUISpriteObject*)type->tp_alloc(type, 0); + //if (self) self->data = std::make_shared(); + return (PyObject*)self; + } + }; +}