Compare commits
	
		
			14 Commits
		
	
	
		
			master
			...
			break_up_u
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
									
								
								 | 
						6aa151aba3 | |
| 
							
							
								
									
								
								 | 
						ec0374ef50 | |
| 
							
							
								
									
								
								 | 
						2cb7339535 | |
| 
							
							
								
									
								
								 | 
						5d6af324bf | |
| 
							
							
								
									
								
								 | 
						567218cd7b | |
| 
							
							
								
									
								
								 | 
						76693acd28 | |
| 
							
							
								
									
								
								 | 
						9efe998a33 | |
| 
							
							
								
									
								
								 | 
						714965da45 | |
| 
							
							
								
									
								
								 | 
						8efa25878f | |
| 
							
							
								
									
								
								 | 
						c186d8c7f3 | |
| 
							
							
								
									
								
								 | 
						1b6e2a709b | |
| 
							
							
								
									
								
								 | 
						aa7553a818 | |
| 
							
							
								
									
								
								 | 
						c0201d989a | |
| 
							
							
								
									
								
								 | 
						83a63a3093 | 
| 
						 | 
				
			
			@ -28,36 +28,12 @@ sf::Sprite PyTexture::sprite(int index, sf::Vector2f pos,  sf::Vector2f s)
 | 
			
		|||
 | 
			
		||||
PyObject* PyTexture::pyObject()
 | 
			
		||||
{
 | 
			
		||||
    // method 1: works but with type weirdness
 | 
			
		||||
    //PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyTextureType, 0);
 | 
			
		||||
    //Py_SET_TYPE(obj, &mcrfpydef::PyTextureType);
 | 
			
		||||
    
 | 
			
		||||
    // method 2: does not work (segfault on use of the mcrfpy.Texture object)
 | 
			
		||||
    //PyObject* obj = PyTexture::pynew(&mcrfpydef::PyTextureType, Py_None, Py_None);
 | 
			
		||||
 | 
			
		||||
    // method 3: does not work (segfault on use of the mcrfpy.Texture object)
 | 
			
		||||
    std::cout << "Find type" << std::endl;
 | 
			
		||||
    auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture");
 | 
			
		||||
        //auto type = obj->ob_type;
 | 
			
		||||
        //auto type = &mcrfpydef::PyTextureType;
 | 
			
		||||
        //std::cout << "assigned value 0x" << std::hex << reinterpret_cast<long>(type) << std::endl;
 | 
			
		||||
        //std::cout << "Found PyTextureType: " << PyUnicode_AsUTF8(PyObject_Repr((PyObject*)type)) << std::endl;
 | 
			
		||||
        //std::cout << "PyTextureType metatype: " << PyUnicode_AsUTF8(PyObject_Repr((PyObject_Type((PyObject*)type)))) << std::endl;
 | 
			
		||||
        //std::cout << "tp_alloc: 0x" << std::hex << reinterpret_cast <long>(type->tp_alloc) << std::endl <<
 | 
			
		||||
        //             "tp_new: 0x" << std::hex << reinterpret_cast<long>(type->tp_new) << std::endl;
 | 
			
		||||
        //PyObject* obj = ((PyTypeObject*)type)->tp_new((PyTypeObject*)type, Py_None, Py_None);
 | 
			
		||||
        PyObject* obj = PyTexture::pynew(type, Py_None, Py_None);
 | 
			
		||||
        //Py_SET_TYPE(obj, type);
 | 
			
		||||
    PyObject* obj = PyTexture::pynew(type, Py_None, Py_None);
 | 
			
		||||
 | 
			
		||||
    // method 4: call the type object?
 | 
			
		||||
 | 
			
		||||
    std::cout << "Instantiated" << std::endl;
 | 
			
		||||
    //Py_SET_TYPE(obj, &mcrfpydef::PyTextureType);
 | 
			
		||||
 | 
			
		||||
    //PyObject_CallFunction(&mcrfpydef::PyTextureType, 
 | 
			
		||||
    try {
 | 
			
		||||
        ((PyTextureObject*)obj)->data = shared_from_this();
 | 
			
		||||
        std::cout << "Sideloaded texture: " << PyUnicode_AsUTF8(PyObject_Repr(obj)) << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
    catch (std::bad_weak_ptr& e)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										527
									
								
								src/UI.cpp
								
								
								
								
							
							
						
						
									
										527
									
								
								src/UI.cpp
								
								
								
								
							| 
						 | 
				
			
			@ -1,527 +0,0 @@
 | 
			
		|||
#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<PyClickCallable>(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<std::vector<std::shared_ptr<UIDrawable>>>();
 | 
			
		||||
    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<std::vector<std::shared_ptr<UIDrawable>>>();
 | 
			
		||||
    /*
 | 
			
		||||
    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<PyTexture> _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));
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
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<PyTexture> _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<PyTexture> 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)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UIGrid methods
 | 
			
		||||
 | 
			
		||||
UIGrid::UIGrid()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
UIGrid::UIGrid(int gx, int gy, std::shared_ptr<PyTexture> _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<std::list<std::shared_ptr<UIEntity>>>();
 | 
			
		||||
 | 
			
		||||
    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<PyTexture> _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<std::list<std::shared_ptr<UIEntity>>>();
 | 
			
		||||
 | 
			
		||||
    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<PyTexture> UIGrid::getTexture()
 | 
			
		||||
{
 | 
			
		||||
    return ptex;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
class UIEntity;
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PyObject_HEAD
 | 
			
		||||
    std::shared_ptr<UIEntity> data;
 | 
			
		||||
} PyUIEntityObject;
 | 
			
		||||
 | 
			
		||||
class UIFrame;
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PyObject_HEAD
 | 
			
		||||
    std::shared_ptr<UIFrame> data;
 | 
			
		||||
} PyUIFrameObject;
 | 
			
		||||
 | 
			
		||||
class UICaption;
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PyObject_HEAD
 | 
			
		||||
    std::shared_ptr<UICaption> data;
 | 
			
		||||
    PyObject* font;
 | 
			
		||||
} PyUICaptionObject;
 | 
			
		||||
 | 
			
		||||
class UIGrid;
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PyObject_HEAD
 | 
			
		||||
    std::shared_ptr<UIGrid> data;
 | 
			
		||||
} PyUIGridObject;
 | 
			
		||||
 | 
			
		||||
class UISprite;
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PyObject_HEAD
 | 
			
		||||
    std::shared_ptr<UISprite> data;
 | 
			
		||||
} PyUISpriteObject;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,261 @@
 | 
			
		|||
#include "UICaption.h"
 | 
			
		||||
#include "GameEngine.h"
 | 
			
		||||
#include "PyColor.h"
 | 
			
		||||
#include "PyVector.h"
 | 
			
		||||
#include "PyFont.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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UICaption::get_float_member(PyUICaptionObject* self, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    auto member_ptr = reinterpret_cast<long>(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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UICaption::set_float_member(PyUICaptionObject* self, PyObject* value, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    float val;
 | 
			
		||||
    auto member_ptr = reinterpret_cast<long>(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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UICaption::get_vec_member(PyUICaptionObject* self, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    return PyVector(self->data->text.getPosition()).pyObject();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UICaption::set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    self->data->text.setPosition(PyVector::fromPy(value));
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UICaption::get_color_member(PyUICaptionObject* self, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    // TODO: migrate this code to a switch statement - validate closure & return values in one tighter, more extensible structure
 | 
			
		||||
 | 
			
		||||
    // validate closure (should be impossible to be wrong, but it's thorough)
 | 
			
		||||
    auto member_ptr = reinterpret_cast<long>(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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UICaption::set_color_member(PyUICaptionObject* self, PyObject* value, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    auto member_ptr = reinterpret_cast<long>(closure);
 | 
			
		||||
    //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse
 | 
			
		||||
    int r, g, b, a;
 | 
			
		||||
    if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color")  /*(PyObject*)&mcrfpydef::PyColorType)*/))
 | 
			
		||||
    {
 | 
			
		||||
        // 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: evaluate use of Resources::caption_buffer... can't I do this with a std::string?
 | 
			
		||||
PyObject* UICaption::get_text(PyUICaptionObject* self, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    Resources::caption_buffer = self->data->text.getString();
 | 
			
		||||
    return PyUnicode_FromString(Resources::caption_buffer.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UICaption::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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyGetSetDef UICaption::getsetters[] = {
 | 
			
		||||
    {"x", (getter)UICaption::get_float_member, (setter)UICaption::set_float_member, "X coordinate of top-left corner",   (void*)0},
 | 
			
		||||
    {"y", (getter)UICaption::get_float_member, (setter)UICaption::set_float_member, "Y coordinate of top-left corner",   (void*)1},
 | 
			
		||||
    {"pos", (getter)UICaption::get_vec_member, (setter)UICaption::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)UICaption::get_float_member, (setter)UICaption::set_float_member, "Thickness of the border",   (void*)4},
 | 
			
		||||
    {"fill_color", (getter)UICaption::get_color_member, (setter)UICaption::set_color_member, "Fill color of the text", (void*)0},
 | 
			
		||||
    {"outline_color", (getter)UICaption::get_color_member, (setter)UICaption::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)UICaption::get_text, (setter)UICaption::set_text, "The text displayed", NULL},
 | 
			
		||||
    {"size", (getter)UICaption::get_float_member, (setter)UICaption::set_float_member, "Text size (integer) in points", (void*)5},
 | 
			
		||||
    {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UICAPTION},
 | 
			
		||||
    {NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PyObject* UICaption::repr(PyUICaptionObject* self)
 | 
			
		||||
{
 | 
			
		||||
    std::ostringstream ss;
 | 
			
		||||
    if (!self->data) ss << "<Caption (invalid internal object)>";
 | 
			
		||||
    else {
 | 
			
		||||
        auto text = self->data->text;
 | 
			
		||||
        auto fc = text.getFillColor();
 | 
			
		||||
        auto oc = text.getOutlineColor();
 | 
			
		||||
        ss << "<Caption (x=" << text.getPosition().x << ", y=" << text.getPosition().y << ", " <<
 | 
			
		||||
            "text='" << (std::string)text.getString() << "', " <<
 | 
			
		||||
            "outline=" << text.getOutlineThickness() << ", " <<
 | 
			
		||||
            "fill_color=(" << (int)fc.r << ", " << (int)fc.g << ", " << (int)fc.b << ", " << (int)fc.a <<"), " <<
 | 
			
		||||
            "outlinecolor=(" << (int)oc.r << ", " << (int)oc.g << ", " << (int)oc.b << ", " << (int)oc.a <<"), " <<
 | 
			
		||||
            ")>";
 | 
			
		||||
    }
 | 
			
		||||
    std::string repr_str = ss.str();
 | 
			
		||||
    return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UICaption::init(PyUICaptionObject* self, PyObject* args, PyObject* kwds)
 | 
			
		||||
{
 | 
			
		||||
    using namespace mcrfpydef;
 | 
			
		||||
    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<char**>(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_GetAttrString(McRFPy_API::mcrf_module, "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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,62 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
#include "Common.h"
 | 
			
		||||
#include "Python.h"
 | 
			
		||||
#include "UIDrawable.h"
 | 
			
		||||
 | 
			
		||||
class UICaption: public UIDrawable
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    sf::Text text;
 | 
			
		||||
    void render(sf::Vector2f) override final;
 | 
			
		||||
    PyObjectsEnum derived_type() override final;
 | 
			
		||||
    virtual UIDrawable* click_at(sf::Vector2f point) override final;
 | 
			
		||||
 | 
			
		||||
    static PyObject* get_float_member(PyUICaptionObject* self, void* closure);
 | 
			
		||||
    static int set_float_member(PyUICaptionObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyObject* get_vec_member(PyUICaptionObject* self, void* closure);
 | 
			
		||||
    static int set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyObject* get_color_member(PyUICaptionObject* self, void* closure);
 | 
			
		||||
    static int set_color_member(PyUICaptionObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyObject* get_text(PyUICaptionObject* self, void* closure);
 | 
			
		||||
    static int set_text(PyUICaptionObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyGetSetDef getsetters[];
 | 
			
		||||
    static PyObject* repr(PyUICaptionObject* self);
 | 
			
		||||
    static int init(PyUICaptionObject* self, PyObject* args, PyObject* kwds);
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace mcrfpydef {
 | 
			
		||||
    static PyTypeObject PyUICaptionType = {
 | 
			
		||||
        .tp_name = "mcrfpy.Caption",
 | 
			
		||||
        .tp_basicsize = sizeof(PyUICaptionObject),
 | 
			
		||||
        .tp_itemsize = 0,
 | 
			
		||||
        // TODO - move tp_dealloc to .cpp file as static function (UICaption::dealloc)
 | 
			
		||||
        .tp_dealloc = (destructor)[](PyObject* self)
 | 
			
		||||
        {
 | 
			
		||||
            PyUICaptionObject* obj = (PyUICaptionObject*)self;
 | 
			
		||||
            // TODO - reevaluate with PyFont usage; UICaption does not own the font
 | 
			
		||||
            // release reference to font object
 | 
			
		||||
            if (obj->font) Py_DECREF(obj->font);
 | 
			
		||||
            obj->data.reset();
 | 
			
		||||
            Py_TYPE(self)->tp_free(self);
 | 
			
		||||
        },
 | 
			
		||||
        .tp_repr = (reprfunc)UICaption::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 = UICaption::getsetters,
 | 
			
		||||
        //.tp_base = NULL,
 | 
			
		||||
        .tp_init = (initproc)UICaption::init,
 | 
			
		||||
        // TODO - move tp_new to .cpp file as a static function (UICaption::new)
 | 
			
		||||
        .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
 | 
			
		||||
        {
 | 
			
		||||
            PyUICaptionObject* self = (PyUICaptionObject*)type->tp_alloc(type, 0);
 | 
			
		||||
            if (self) self->data = std::make_shared<UICaption>();
 | 
			
		||||
            return (PyObject*)self;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,203 @@
 | 
			
		|||
#include "UICollection.h"
 | 
			
		||||
 | 
			
		||||
#include "UIFrame.h"
 | 
			
		||||
#include "UICaption.h"
 | 
			
		||||
#include "UISprite.h"
 | 
			
		||||
#include "UIGrid.h"
 | 
			
		||||
#include "McRFPy_API.h"
 | 
			
		||||
 | 
			
		||||
using namespace mcrfpydef;
 | 
			
		||||
 | 
			
		||||
int UICollectionIter::init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds)
 | 
			
		||||
{
 | 
			
		||||
    PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UICollectionIter::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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UICollectionIter::repr(PyUICollectionIterObject* self)
 | 
			
		||||
{
 | 
			
		||||
	std::ostringstream ss;
 | 
			
		||||
	if (!self->data) ss << "<UICollectionIter (invalid internal object)>";
 | 
			
		||||
	else {
 | 
			
		||||
	    ss << "<UICollectionIter (" << self->data->size() << " child objects, @ index " << self->index  << ")>";
 | 
			
		||||
	}
 | 
			
		||||
	std::string repr_str = ss.str();
 | 
			
		||||
	return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Py_ssize_t UICollection::len(PyUICollectionObject* self) {
 | 
			
		||||
	return self->data->size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UICollection::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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PySequenceMethods UICollection::sqmethods = {
 | 
			
		||||
	.sq_length = (lenfunc)UICollection::len,
 | 
			
		||||
	.sq_item = (ssizeargfunc)UICollection::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)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Idiomatic way to fetch complete types from the API rather than referencing their PyTypeObject struct
 | 
			
		||||
 | 
			
		||||
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture");
 | 
			
		||||
 | 
			
		||||
I never identified why `using namespace mcrfpydef;` doesn't solve the segfault issue.
 | 
			
		||||
The horrible macro in UIDrawable was originally a workaround for this, but as I interact with the types outside of the monster UI.h, a more general (and less icky) solution is required.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
PyObject* UICollection::append(PyUICollectionObject* self, PyObject* o)
 | 
			
		||||
{
 | 
			
		||||
	// if not UIDrawable subclass, reject it
 | 
			
		||||
	// self->data->push_back( c++ object inside o );
 | 
			
		||||
 | 
			
		||||
    // this would be a great use case for .tp_base
 | 
			
		||||
    if (!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) &&
 | 
			
		||||
        !PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) &&
 | 
			
		||||
        !PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) &&
 | 
			
		||||
        !PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, and Grid objects can be added to UICollection");
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")))
 | 
			
		||||
    {
 | 
			
		||||
        PyUIFrameObject* frame = (PyUIFrameObject*)o;
 | 
			
		||||
        self->data->push_back(frame->data);
 | 
			
		||||
    }
 | 
			
		||||
    if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")))
 | 
			
		||||
    {
 | 
			
		||||
        PyUICaptionObject* caption = (PyUICaptionObject*)o;
 | 
			
		||||
        self->data->push_back(caption->data);
 | 
			
		||||
    }
 | 
			
		||||
    if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")))
 | 
			
		||||
    {
 | 
			
		||||
        PyUISpriteObject* sprite = (PyUISpriteObject*)o;
 | 
			
		||||
        self->data->push_back(sprite->data);
 | 
			
		||||
    }
 | 
			
		||||
    if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid")))
 | 
			
		||||
    {
 | 
			
		||||
        PyUIGridObject* grid = (PyUIGridObject*)o;
 | 
			
		||||
        self->data->push_back(grid->data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Py_INCREF(Py_None);
 | 
			
		||||
    return Py_None;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UICollection::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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyMethodDef UICollection::methods[] = {
 | 
			
		||||
	{"append", (PyCFunction)UICollection::append, METH_O},
 | 
			
		||||
    //{"extend", (PyCFunction)PyUICollection_extend, METH_O}, // TODO
 | 
			
		||||
	{"remove", (PyCFunction)UICollection::remove, METH_O},
 | 
			
		||||
	{NULL, NULL, 0, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PyObject* UICollection::repr(PyUICollectionObject* self)
 | 
			
		||||
{
 | 
			
		||||
	std::ostringstream ss;
 | 
			
		||||
	if (!self->data) ss << "<UICollection (invalid internal object)>";
 | 
			
		||||
	else {
 | 
			
		||||
	    ss << "<UICollection (" << self->data->size() << " child objects)>";
 | 
			
		||||
	}
 | 
			
		||||
	std::string repr_str = ss.str();
 | 
			
		||||
	return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UICollection::init(PyUICollectionObject* self, PyObject* args, PyObject* kwds)
 | 
			
		||||
{
 | 
			
		||||
    PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UICollection::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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,88 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
#include "Common.h"
 | 
			
		||||
#include "Python.h"
 | 
			
		||||
 | 
			
		||||
#include "UIDrawable.h"
 | 
			
		||||
 | 
			
		||||
class UICollectionIter
 | 
			
		||||
{
 | 
			
		||||
    // really more of a namespace: all the members are public and static. But being consistent with other UI objects
 | 
			
		||||
public:
 | 
			
		||||
    static int init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds);
 | 
			
		||||
    static PyObject* next(PyUICollectionIterObject* self);
 | 
			
		||||
    static PyObject* repr(PyUICollectionIterObject* self);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class UICollection
 | 
			
		||||
{
 | 
			
		||||
    // really more of a namespace: all the members are public and static. But being consistent with other UI objects
 | 
			
		||||
public:
 | 
			
		||||
    static Py_ssize_t len(PyUICollectionObject* self);
 | 
			
		||||
	static PyObject* getitem(PyUICollectionObject* self, Py_ssize_t index);
 | 
			
		||||
	static PySequenceMethods sqmethods;
 | 
			
		||||
	static PyObject* append(PyUICollectionObject* self, PyObject* o);
 | 
			
		||||
	static PyObject* remove(PyUICollectionObject* self, PyObject* o);
 | 
			
		||||
	static PyMethodDef methods[];
 | 
			
		||||
	static PyObject* repr(PyUICollectionObject* self);
 | 
			
		||||
    static int init(PyUICollectionObject* self, PyObject* args, PyObject* kwds);
 | 
			
		||||
    static PyObject* iter(PyUICollectionObject* self);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace mcrfpydef {
 | 
			
		||||
    static PyTypeObject PyUICollectionIterType = {
 | 
			
		||||
        //PyVarObject_HEAD_INIT(NULL, 0)
 | 
			
		||||
        .tp_name = "mcrfpy.UICollectionIter",
 | 
			
		||||
        .tp_basicsize = sizeof(PyUICollectionIterObject),
 | 
			
		||||
        .tp_itemsize = 0,
 | 
			
		||||
        //TODO - as static method, not inline lambda def, please
 | 
			
		||||
        .tp_dealloc = (destructor)[](PyObject* self)
 | 
			
		||||
        {
 | 
			
		||||
            PyUICollectionIterObject* obj = (PyUICollectionIterObject*)self;
 | 
			
		||||
            obj->data.reset();
 | 
			
		||||
            Py_TYPE(self)->tp_free(self);
 | 
			
		||||
        },
 | 
			
		||||
        .tp_repr = (reprfunc)UICollectionIter::repr,
 | 
			
		||||
        .tp_flags = Py_TPFLAGS_DEFAULT,
 | 
			
		||||
        .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"),
 | 
			
		||||
        .tp_iternext = (iternextfunc)UICollectionIter::next,
 | 
			
		||||
        //.tp_getset = PyUICollection_getset,
 | 
			
		||||
        .tp_init = (initproc)UICollectionIter::init, // just raise an exception
 | 
			
		||||
        //TODO - as static method, not inline lambda def, please
 | 
			
		||||
        .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;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
	static PyTypeObject PyUICollectionType = {
 | 
			
		||||
		//PyVarObject_/HEAD_INIT(NULL, 0)
 | 
			
		||||
		.tp_name = "mcrfpy.UICollection",
 | 
			
		||||
		.tp_basicsize = sizeof(PyUICollectionObject),
 | 
			
		||||
		.tp_itemsize = 0,
 | 
			
		||||
        //TODO - as static method, not inline lambda def, please
 | 
			
		||||
		.tp_dealloc = (destructor)[](PyObject* self)
 | 
			
		||||
		{
 | 
			
		||||
		    PyUICollectionObject* obj = (PyUICollectionObject*)self;
 | 
			
		||||
		    obj->data.reset();
 | 
			
		||||
		    Py_TYPE(self)->tp_free(self);
 | 
			
		||||
		},
 | 
			
		||||
		.tp_repr = (reprfunc)UICollection::repr,
 | 
			
		||||
		.tp_as_sequence = &UICollection::sqmethods,
 | 
			
		||||
		.tp_flags = Py_TPFLAGS_DEFAULT,
 | 
			
		||||
		.tp_doc = PyDoc_STR("Iterable, indexable collection of UI objects"),
 | 
			
		||||
        .tp_iter = (getiterfunc)UICollection::iter,
 | 
			
		||||
		.tp_methods = UICollection::methods, // append, remove
 | 
			
		||||
        //.tp_getset = PyUICollection_getset,
 | 
			
		||||
		.tp_init = (initproc)UICollection::init, // just raise an exception
 | 
			
		||||
        //TODO - as static method, not inline lambda def, please
 | 
			
		||||
		.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;
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,81 @@
 | 
			
		|||
#include "UIDrawable.h"
 | 
			
		||||
#include "UIFrame.h"
 | 
			
		||||
#include "UICaption.h"
 | 
			
		||||
#include "UISprite.h"
 | 
			
		||||
#include "UIGrid.h"
 | 
			
		||||
 | 
			
		||||
UIDrawable::UIDrawable() { click_callable = NULL;  }
 | 
			
		||||
 | 
			
		||||
void UIDrawable::click_unregister()
 | 
			
		||||
{
 | 
			
		||||
    click_callable.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UIDrawable::render()
 | 
			
		||||
{
 | 
			
		||||
    render(sf::Vector2f());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIDrawable::get_click(PyObject* self, void* closure) {
 | 
			
		||||
    PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIDrawable::set_click(PyObject* self, PyObject* value, void* closure) {
 | 
			
		||||
    PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UIDrawable::click_register(PyObject* callable)
 | 
			
		||||
{
 | 
			
		||||
    click_callable = std::make_unique<PyClickCallable>(callable);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,176 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
#include "Common.h"
 | 
			
		||||
#include "Python.h"
 | 
			
		||||
#include "structmember.h"
 | 
			
		||||
#include "IndexTexture.h"
 | 
			
		||||
#include "Resources.h"
 | 
			
		||||
#include <list>
 | 
			
		||||
 | 
			
		||||
#include "PyCallable.h"
 | 
			
		||||
#include "PyTexture.h"
 | 
			
		||||
#include "PyColor.h"
 | 
			
		||||
#include "PyVector.h"
 | 
			
		||||
#include "PyFont.h"
 | 
			
		||||
 | 
			
		||||
#include "Resources.h"
 | 
			
		||||
#include "UIBase.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<PyClickCallable> click_callable;
 | 
			
		||||
    virtual UIDrawable* click_at(sf::Vector2f point) = 0;
 | 
			
		||||
    void click_register(PyObject*);
 | 
			
		||||
    void click_unregister();
 | 
			
		||||
 | 
			
		||||
    UIDrawable();
 | 
			
		||||
 | 
			
		||||
    static PyObject* get_click(PyObject* self, void* closure);
 | 
			
		||||
    static int set_click(PyObject* self, PyObject* value, void* closure);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PyObject_HEAD
 | 
			
		||||
    std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> data;
 | 
			
		||||
} PyUICollectionObject;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PyObject_HEAD
 | 
			
		||||
    std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> data;
 | 
			
		||||
    int index;
 | 
			
		||||
    int start_size;
 | 
			
		||||
} PyUICollectionIterObject;
 | 
			
		||||
 | 
			
		||||
namespace mcrfpydef {
 | 
			
		||||
    //PyObject* py_instance(std::shared_ptr<UIDrawable> 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<UIFrame>(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<UICaption>(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<UISprite>(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<UIGrid>(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(PyObject* self, void* closure) {
 | 
			
		||||
    PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(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(PyObject* self, PyObject* value, void* closure) {
 | 
			
		||||
    PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(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;
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,165 @@
 | 
			
		|||
#include "UIEntity.h"
 | 
			
		||||
#include "UIGrid.h"
 | 
			
		||||
#include "McRFPy_API.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)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIEntity::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*)((&mcrfpydef::PyUIGridPointStateType)->tp_alloc(&mcrfpydef::PyUIGridPointStateType, 0));
 | 
			
		||||
    */
 | 
			
		||||
    auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState");
 | 
			
		||||
    auto obj = (PyUIGridPointStateObject*)type->tp_alloc(type, 0);
 | 
			
		||||
    //auto target = std::static_pointer_cast<UIEntity>(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;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIEntity::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<char**>(keywords), &x, &y, &texture, &sprite_index, &grid))
 | 
			
		||||
    {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check types for texture
 | 
			
		||||
    //
 | 
			
		||||
    // Set Texture
 | 
			
		||||
    //
 | 
			
		||||
    if (texture != NULL && !PyObject_IsInstance(texture, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))){
 | 
			
		||||
        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_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) {
 | 
			
		||||
        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<UIEntity>();
 | 
			
		||||
    else
 | 
			
		||||
        self->data = std::make_shared<UIEntity>(*((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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PyObject* UIEntity::get_spritenumber(PyUIEntityObject* self, void* closure) {
 | 
			
		||||
    return PyLong_FromDouble(self->data->sprite.getSpriteIndex());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* sfVector2f_to_PyObject(sf::Vector2f vector) {
 | 
			
		||||
    return Py_BuildValue("(ff)", vector.x, vector.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO - deprecate / remove this helper
 | 
			
		||||
PyObject* UIGridPointState_to_PyObject(const UIGridPointState& state) {
 | 
			
		||||
    return PyObject_New(PyObject, (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState"));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIGridPointStateVector_to_PyList(const std::vector<UIGridPointState>& 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIEntity::get_position(PyUIEntityObject* self, void* closure) {
 | 
			
		||||
    return sfVector2f_to_PyObject(self->data->position);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIEntity::set_position(PyUIEntityObject* self, PyObject* value, void* closure) {
 | 
			
		||||
    self->data->position = PyObject_to_sfVector2f(value);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIEntity::get_gridstate(PyUIEntityObject* self, void* closure) {
 | 
			
		||||
    // Assuming a function to convert std::vector<UIGridPointState> to PyObject* list
 | 
			
		||||
    return UIGridPointStateVector_to_PyList(self->data->gridstate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIEntity::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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyMethodDef UIEntity::methods[] = {
 | 
			
		||||
    {"at", (PyCFunction)UIEntity::at, METH_O},
 | 
			
		||||
    {NULL, NULL, 0, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PyGetSetDef UIEntity::getsetters[] = {
 | 
			
		||||
    {"position", (getter)UIEntity::get_position, (setter)UIEntity::set_position, "Entity position", NULL},
 | 
			
		||||
    {"gridstate", (getter)UIEntity::get_gridstate, NULL, "Grid point states for the entity", NULL},
 | 
			
		||||
    {"sprite_number", (getter)UIEntity::get_spritenumber, (setter)UIEntity::set_spritenumber, "Sprite number (index) on the texture on the display", NULL},
 | 
			
		||||
    {NULL}  /* Sentinel */
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,202 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
#include "Common.h"
 | 
			
		||||
#include "Python.h"
 | 
			
		||||
#include "structmember.h"
 | 
			
		||||
#include "IndexTexture.h"
 | 
			
		||||
#include "Resources.h"
 | 
			
		||||
#include <list>
 | 
			
		||||
 | 
			
		||||
#include "PyCallable.h"
 | 
			
		||||
#include "PyTexture.h"
 | 
			
		||||
#include "PyColor.h"
 | 
			
		||||
#include "PyVector.h"
 | 
			
		||||
#include "PyFont.h"
 | 
			
		||||
 | 
			
		||||
#include "UIGridPoint.h"
 | 
			
		||||
#include "UIDrawable.h"
 | 
			
		||||
#include "UIBase.h"
 | 
			
		||||
#include "UISprite.h"
 | 
			
		||||
 | 
			
		||||
class UIGrid;
 | 
			
		||||
 | 
			
		||||
//class UIEntity;
 | 
			
		||||
//typedef struct {
 | 
			
		||||
//    PyObject_HEAD
 | 
			
		||||
//    std::shared_ptr<UIEntity> data;
 | 
			
		||||
//} PyUIEntityObject;
 | 
			
		||||
 | 
			
		||||
// helper methods with no namespace requirement
 | 
			
		||||
static PyObject* sfVector2f_to_PyObject(sf::Vector2f vector);
 | 
			
		||||
static sf::Vector2f PyObject_to_sfVector2f(PyObject* obj);
 | 
			
		||||
static PyObject* UIGridPointState_to_PyObject(const UIGridPointState& state);
 | 
			
		||||
static PyObject* UIGridPointStateVector_to_PyList(const std::vector<UIGridPointState>& vec);
 | 
			
		||||
 | 
			
		||||
// TODO: make UIEntity a drawable
 | 
			
		||||
class UIEntity//: public UIDrawable
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //PyObject* self;
 | 
			
		||||
    std::shared_ptr<UIGrid> grid;
 | 
			
		||||
    std::vector<UIGridPointState> gridstate;
 | 
			
		||||
    UISprite sprite;
 | 
			
		||||
    sf::Vector2f position; //(x,y) in grid coordinates; float for animation
 | 
			
		||||
    void render(sf::Vector2f); //override final;
 | 
			
		||||
 | 
			
		||||
    UIEntity();
 | 
			
		||||
    UIEntity(UIGrid&);
 | 
			
		||||
    
 | 
			
		||||
    static PyObject* at(PyUIEntityObject* self, PyObject* o);
 | 
			
		||||
    static int init(PyUIEntityObject* self, PyObject* args, PyObject* kwds);
 | 
			
		||||
 | 
			
		||||
    static PyObject* get_position(PyUIEntityObject* self, void* closure);
 | 
			
		||||
    static int set_position(PyUIEntityObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyObject* get_gridstate(PyUIEntityObject* self, void* closure);
 | 
			
		||||
    static PyObject* get_spritenumber(PyUIEntityObject* self, void* closure);
 | 
			
		||||
    static int set_spritenumber(PyUIEntityObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyMethodDef methods[];
 | 
			
		||||
    static PyGetSetDef getsetters[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace mcrfpydef {
 | 
			
		||||
/*
 | 
			
		||||
//TODO: add this method to class scope; move implementation to .cpp file; reconsider for moving to "UIBase.h/.cpp"
 | 
			
		||||
// 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//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::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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//TODO: add this method to class scope; move implementation to .cpp file
 | 
			
		||||
// 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;
 | 
			
		||||
}
 | 
			
		||||
//TODO: add this method to class scope; move implementation to .cpp file
 | 
			
		||||
// Function to convert std::vector<UIGridPointState> to a Python list TODO need a PyUICollection style iterable
 | 
			
		||||
static PyObject* UIGridPointStateVector_to_PyList(const std::vector<UIGridPointState>& 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//TODO: add this method to class scope; move implementation to .cpp file
 | 
			
		||||
static PyObject* PyUIEntity_get_position(PyUIEntityObject* self, void* closure) {
 | 
			
		||||
    return sfVector2f_to_PyObject(self->data->position);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//TODO: add this method to class scope; move implementation to .cpp file
 | 
			
		||||
static int PyUIEntity_set_position(PyUIEntityObject* self, PyObject* value, void* closure) {
 | 
			
		||||
    self->data->position = PyObject_to_sfVector2f(value);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//TODO: add this method to class scope; move implementation to .cpp file
 | 
			
		||||
static PyObject* PyUIEntity_get_gridstate(PyUIEntityObject* self, void* closure) {
 | 
			
		||||
    // Assuming a function to convert std::vector<UIGridPointState> to PyObject* list
 | 
			
		||||
    return UIGridPointStateVector_to_PyList(self->data->gridstate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//TODO: add this method to class scope; move implementation to .cpp file
 | 
			
		||||
static PyObject* PyUIEntity_get_spritenumber(PyUIEntityObject* self, void* closure) {
 | 
			
		||||
    return PyLong_FromDouble(self->data->sprite.getSpriteIndex());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//TODO: add this method to class scope; move implementation to .cpp file
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//TODO: add this method to class scope; move implementation to .cpp file
 | 
			
		||||
 | 
			
		||||
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<UIEntity>(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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//TODO: add this static array to class scope; move implementation to .cpp file
 | 
			
		||||
static PyMethodDef PyUIEntity_methods[] = {
 | 
			
		||||
    {"at", (PyCFunction)UIEntity::at, METH_O},
 | 
			
		||||
    {NULL, NULL, 0, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//TODO: add this static array to class scope; move implementation to .cpp file
 | 
			
		||||
// 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
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//TODO: add this method to class scope; forward declaration not required after .h/.cpp split
 | 
			
		||||
//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 = UIEntity::methods,
 | 
			
		||||
    .tp_getset = UIEntity::getsetters,
 | 
			
		||||
    .tp_init = (initproc)UIEntity::init,
 | 
			
		||||
    .tp_new = PyType_GenericNew,
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,265 @@
 | 
			
		|||
#include "UIFrame.h"
 | 
			
		||||
#include "UICollection.h"
 | 
			
		||||
#include "GameEngine.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<std::vector<std::shared_ptr<UIDrawable>>>();
 | 
			
		||||
    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<std::vector<std::shared_ptr<UIDrawable>>>();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIFrame::get_children(PyUIFrameObject* self, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    // create PyUICollection instance pointing to self->data->children
 | 
			
		||||
    //PyUICollectionObject* o = (PyUICollectionObject*)mcrfpydef::PyUICollectionType.tp_alloc(&mcrfpydef::PyUICollectionType, 0);
 | 
			
		||||
    auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "UICollection");
 | 
			
		||||
    auto o = (PyUICollectionObject*)type->tp_alloc(type, 0);
 | 
			
		||||
    if (o)
 | 
			
		||||
        o->data = self->data->children;
 | 
			
		||||
    return (PyObject*)o;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PyObject* UIFrame::get_float_member(PyUIFrameObject* self, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    auto member_ptr = reinterpret_cast<long>(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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIFrame::set_float_member(PyUIFrameObject* self, PyObject* value, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    float val;
 | 
			
		||||
    auto member_ptr = reinterpret_cast<long>(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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIFrame::get_color_member(PyUIFrameObject* self, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    // validate closure (should be impossible to be wrong, but it's thorough)
 | 
			
		||||
    auto member_ptr = reinterpret_cast<long>(closure);
 | 
			
		||||
    if (member_ptr != 0 && member_ptr != 1)
 | 
			
		||||
    {
 | 
			
		||||
        PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    //PyTypeObject* colorType = &PyColorType;
 | 
			
		||||
    auto colorType = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color");
 | 
			
		||||
    PyObject* pyColor = colorType->tp_alloc(colorType, 0);
 | 
			
		||||
    if (pyColor == NULL)
 | 
			
		||||
    {
 | 
			
		||||
        std::cout << "failure to allocate mcrfpy.Color / PyColorType" << std::endl;
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    PyColorObject* pyColorObj = reinterpret_cast<PyColorObject*>(pyColor);
 | 
			
		||||
 | 
			
		||||
    // 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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIFrame::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<long>(closure);
 | 
			
		||||
    int r, g, b, a;
 | 
			
		||||
    if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color")))
 | 
			
		||||
    {
 | 
			
		||||
        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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyGetSetDef UIFrame::getsetters[] = {
 | 
			
		||||
    {"x", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "X coordinate of top-left corner",   (void*)0},
 | 
			
		||||
    {"y", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "Y coordinate of top-left corner",   (void*)1},
 | 
			
		||||
    {"w", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "width of the rectangle",   (void*)2},
 | 
			
		||||
    {"h", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "height of the rectangle",   (void*)3},
 | 
			
		||||
    {"outline", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "Thickness of the border",   (void*)4},
 | 
			
		||||
    {"fill_color", (getter)UIFrame::get_color_member, (setter)UIFrame::set_color_member, "Fill color of the rectangle", (void*)0},
 | 
			
		||||
    {"outline_color", (getter)UIFrame::get_color_member, (setter)UIFrame::set_color_member, "Outline color of the rectangle", (void*)1},
 | 
			
		||||
    {"children", (getter)UIFrame::get_children, NULL, "UICollection of objects on top of this one", NULL},
 | 
			
		||||
    {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIFRAME},
 | 
			
		||||
    {NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PyObject* UIFrame::repr(PyUIFrameObject* self)
 | 
			
		||||
{
 | 
			
		||||
    std::ostringstream ss;
 | 
			
		||||
    if (!self->data) ss << "<Frame (invalid internal object)>";
 | 
			
		||||
    else {
 | 
			
		||||
        auto box = self->data->box;
 | 
			
		||||
        auto fc = box.getFillColor();
 | 
			
		||||
        auto oc = box.getOutlineColor();
 | 
			
		||||
        ss << "<Frame (x=" << box.getPosition().x << ", y=" << box.getPosition().y << ", x=" << 
 | 
			
		||||
            box.getSize().x << ", w=" << box.getSize().y << ", " <<
 | 
			
		||||
            "outline=" << box.getOutlineThickness() << ", " << 
 | 
			
		||||
            "fill_color=(" << (int)fc.r << ", " << (int)fc.g << ", " << (int)fc.b << ", " << (int)fc.a <<"), " <<
 | 
			
		||||
            "outline_color=(" << (int)oc.r << ", " << (int)oc.g << ", " << (int)oc.b << ", " << (int)oc.a <<"), " <<
 | 
			
		||||
            self->data->children->size() << " child objects" <<
 | 
			
		||||
            ")>";
 | 
			
		||||
    }
 | 
			
		||||
    std::string repr_str = ss.str();
 | 
			
		||||
    return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIFrame::init(PyUIFrameObject* self, PyObject* args, PyObject* kwds)
 | 
			
		||||
{
 | 
			
		||||
    //std::cout << "Init called\n";
 | 
			
		||||
    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<char**>(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 = UIFrame::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 = UIFrame::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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,80 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
#include "Common.h"
 | 
			
		||||
#include "Python.h"
 | 
			
		||||
#include "structmember.h"
 | 
			
		||||
#include "IndexTexture.h"
 | 
			
		||||
#include "Resources.h"
 | 
			
		||||
#include <list>
 | 
			
		||||
 | 
			
		||||
#include "PyCallable.h"
 | 
			
		||||
#include "PyColor.h"
 | 
			
		||||
#include "PyVector.h"
 | 
			
		||||
#include "UIDrawable.h"
 | 
			
		||||
#include "UIBase.h"
 | 
			
		||||
 | 
			
		||||
//class UIFrame;
 | 
			
		||||
//
 | 
			
		||||
//typedef struct {
 | 
			
		||||
//    PyObject_HEAD
 | 
			
		||||
//    std::shared_ptr<UIFrame> data;
 | 
			
		||||
//} PyUIFrameObject;
 | 
			
		||||
 | 
			
		||||
class UIFrame: public UIDrawable
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    UIFrame(float, float, float, float);
 | 
			
		||||
    UIFrame();
 | 
			
		||||
    ~UIFrame();
 | 
			
		||||
    sf::RectangleShape box;
 | 
			
		||||
    float outline;
 | 
			
		||||
    std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> 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;
 | 
			
		||||
 | 
			
		||||
    static PyObject* get_children(PyUIFrameObject* self, void* closure);
 | 
			
		||||
 | 
			
		||||
    static PyObject* get_float_member(PyUIFrameObject* self, void* closure);
 | 
			
		||||
    static int set_float_member(PyUIFrameObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyObject* get_color_member(PyUIFrameObject* self, void* closure);
 | 
			
		||||
    static int set_color_member(PyUIFrameObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyGetSetDef getsetters[];
 | 
			
		||||
    static PyObject* repr(PyUIFrameObject* self);
 | 
			
		||||
    static int init(PyUIFrameObject* self, PyObject* args, PyObject* kwds);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace mcrfpydef {
 | 
			
		||||
    //TODO: add this method to class scope; move implementation to .cpp file
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    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)UIFrame::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 = UIFrame::getsetters,
 | 
			
		||||
        //.tp_base = NULL,
 | 
			
		||||
        .tp_init = (initproc)UIFrame::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<UIFrame>();
 | 
			
		||||
            return (PyObject*)self;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,608 @@
 | 
			
		|||
#include "UIGrid.h"
 | 
			
		||||
#include "GameEngine.h"
 | 
			
		||||
#include "McRFPy_API.h"
 | 
			
		||||
 | 
			
		||||
UIGrid::UIGrid() {}
 | 
			
		||||
 | 
			
		||||
UIGrid::UIGrid(int gx, int gy, std::shared_ptr<PyTexture> _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<std::list<std::shared_ptr<UIEntity>>>();
 | 
			
		||||
 | 
			
		||||
    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<PyTexture> UIGrid::getTexture()
 | 
			
		||||
{
 | 
			
		||||
    return ptex;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
UIDrawable* UIGrid::click_at(sf::Vector2f point)
 | 
			
		||||
{
 | 
			
		||||
    if (click_callable)
 | 
			
		||||
    {
 | 
			
		||||
        if(box.getGlobalBounds().contains(point)) return this;
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int UIGrid::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)) {
 | 
			
		||||
    if (!PyObject_IsInstance(textureObj, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))) {
 | 
			
		||||
        PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    PyTextureObject* pyTexture = reinterpret_cast<PyTextureObject*>(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<UIGrid>(grid_x, grid_y, pyTexture->data, 
 | 
			
		||||
            sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h));
 | 
			
		||||
    return 0; // Success
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIGrid::get_grid_size(PyUIGridObject* self, void* closure) {
 | 
			
		||||
    return Py_BuildValue("(ii)", self->data->grid_x, self->data->grid_y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIGrid::get_position(PyUIGridObject* self, void* closure) {
 | 
			
		||||
    auto& box = self->data->box;
 | 
			
		||||
    return Py_BuildValue("(ff)", box.getPosition().x, box.getPosition().y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIGrid::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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIGrid::get_size(PyUIGridObject* self, void* closure) {
 | 
			
		||||
    auto& box = self->data->box;
 | 
			
		||||
    return Py_BuildValue("(ff)", box.getSize().x, box.getSize().y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIGrid::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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIGrid::get_center(PyUIGridObject* self, void* closure) {
 | 
			
		||||
    return Py_BuildValue("(ff)", self->data->center_x, self->data->center_y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIGrid::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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIGrid::get_float_member(PyUIGridObject* self, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    auto member_ptr = reinterpret_cast<long>(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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIGrid::set_float_member(PyUIGridObject* self, PyObject* value, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    float val;
 | 
			
		||||
    auto member_ptr = reinterpret_cast<long>(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
 | 
			
		||||
/*
 | 
			
		||||
PyObject* UIGrid::get_texture(PyUIGridObject* self, void* closure) {
 | 
			
		||||
    Py_INCREF(self->texture);
 | 
			
		||||
    return self->texture;
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
PyObject* UIGrid::get_texture(PyUIGridObject* self, void* closure) {
 | 
			
		||||
        //return self->data->getTexture()->pyObject();
 | 
			
		||||
        // PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState")
 | 
			
		||||
        //PyTextureObject* obj = (PyTextureObject*)((&PyTextureType)->tp_alloc(&PyTextureType, 0));
 | 
			
		||||
        auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture");
 | 
			
		||||
        auto obj = (PyTextureObject*)type->tp_alloc(type, 0);
 | 
			
		||||
        obj->data = self->data->getTexture();
 | 
			
		||||
        return (PyObject*)obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIGrid::py_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 type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPoint");
 | 
			
		||||
    auto obj = (PyUIGridPointObject*)type->tp_alloc(type, 0);
 | 
			
		||||
    //auto target = std::static_pointer_cast<UIEntity>(target);
 | 
			
		||||
    obj->data = &(self->data->points[x + self->data->grid_x * y]);
 | 
			
		||||
    obj->grid = self->data;
 | 
			
		||||
    return (PyObject*)obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyMethodDef UIGrid::methods[] = {
 | 
			
		||||
    {"at", (PyCFunction)UIGrid::py_at, METH_O},
 | 
			
		||||
    {NULL, NULL, 0, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PyGetSetDef UIGrid::getsetters[] = {
 | 
			
		||||
 | 
			
		||||
    // TODO - refactor into get_vector_member with field identifier values `(void*)n`
 | 
			
		||||
    {"grid_size", (getter)UIGrid::get_grid_size, NULL, "Grid dimensions (grid_x, grid_y)", NULL},
 | 
			
		||||
    {"position", (getter)UIGrid::get_position, (setter)UIGrid::set_position, "Position of the grid (x, y)", NULL},
 | 
			
		||||
    {"size", (getter)UIGrid::get_size, (setter)UIGrid::set_size, "Size of the grid (width, height)", NULL},
 | 
			
		||||
    {"center", (getter)UIGrid::get_center, (setter)UIGrid::set_center, "Grid coordinate at the center of the Grid's view (pan)", NULL},
 | 
			
		||||
 | 
			
		||||
    {"entities", (getter)UIGrid::get_children, NULL, "EntityCollection of entities on this grid", NULL},
 | 
			
		||||
 | 
			
		||||
    {"x", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "top-left corner X-coordinate", (void*)0},
 | 
			
		||||
    {"y", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "top-left corner Y-coordinate", (void*)1},
 | 
			
		||||
    {"w", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "visible widget width", (void*)2},
 | 
			
		||||
    {"h", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "visible widget height", (void*)3},
 | 
			
		||||
    {"center_x", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "center of the view X-coordinate", (void*)4},
 | 
			
		||||
    {"center_y", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "center of the view Y-coordinate", (void*)5},
 | 
			
		||||
    {"zoom", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "zoom factor for displaying the Grid", (void*)6},
 | 
			
		||||
 | 
			
		||||
    {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIGRID},
 | 
			
		||||
 | 
			
		||||
    {"texture", (getter)UIGrid::get_texture, NULL, "Texture of the grid", NULL}, //TODO 7DRL-day2-item5
 | 
			
		||||
    {NULL}  /* Sentinel */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PyObject* UIGrid::get_children(PyUIGridObject* self, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    // create PyUICollection instance pointing to self->data->children
 | 
			
		||||
    //PyUIEntityCollectionObject* o = (PyUIEntityCollectionObject*)PyUIEntityCollectionType.tp_alloc(&PyUIEntityCollectionType, 0);
 | 
			
		||||
    auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "EntityCollection");
 | 
			
		||||
    auto o = (PyUIEntityCollectionObject*)type->tp_alloc(type, 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<list<sp<UIEntity>>>
 | 
			
		||||
        o->grid = self->data;
 | 
			
		||||
    }
 | 
			
		||||
    return (PyObject*)o;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* // TODO standard pointer would need deleted, but I opted for a shared pointer. tp_dealloc currently not even defined in the PyTypeObject
 | 
			
		||||
void PyUIGrid_dealloc(PyUIGridObject* self) {
 | 
			
		||||
    delete self->data; // Clean up the allocated UIGrid object
 | 
			
		||||
    Py_TYPE(self)->tp_free((PyObject*)self);
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
int UIEntityCollectionIter::init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds)
 | 
			
		||||
{
 | 
			
		||||
    PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required.");
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIEntityCollectionIter::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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIEntityCollectionIter::repr(PyUIEntityCollectionIterObject* self)
 | 
			
		||||
{
 | 
			
		||||
    std::ostringstream ss;
 | 
			
		||||
    if (!self->data) ss << "<UICollectionIter (invalid internal object)>";
 | 
			
		||||
    else {
 | 
			
		||||
        ss << "<UICollectionIter (" << self->data->size() << " child objects, @ index " << self->index  << ")>";
 | 
			
		||||
    }
 | 
			
		||||
    std::string repr_str = ss.str();
 | 
			
		||||
    return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Py_ssize_t UIEntityCollection::len(PyUIEntityCollectionObject* self) {
 | 
			
		||||
    return self->data->size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIEntityCollection::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 type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity");
 | 
			
		||||
    auto o = (PyUIEntityObject*)type->tp_alloc(type, 0);
 | 
			
		||||
    auto p = std::static_pointer_cast<UIEntity>(target);
 | 
			
		||||
    o->data = p;
 | 
			
		||||
    return (PyObject*)o;
 | 
			
		||||
return NULL;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PySequenceMethods UIEntityCollection::sqmethods = {
 | 
			
		||||
    .sq_length = (lenfunc)UIEntityCollection::len,
 | 
			
		||||
    .sq_item = (ssizeargfunc)UIEntityCollection::getitem,
 | 
			
		||||
    //.sq_item_by_index = UIEntityCollection::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)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PyObject* UIEntityCollection::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))
 | 
			
		||||
    if (!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity")))
 | 
			
		||||
    {
 | 
			
		||||
        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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIEntityCollection::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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyMethodDef UIEntityCollection::methods[] = {
 | 
			
		||||
    {"append", (PyCFunction)UIEntityCollection::append, METH_O},
 | 
			
		||||
    //{"extend", (PyCFunction)UIEntityCollection::extend, METH_O}, // TODO
 | 
			
		||||
    {"remove", (PyCFunction)UIEntityCollection::remove, METH_O},
 | 
			
		||||
    {NULL, NULL, 0, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PyObject* UIEntityCollection::repr(PyUIEntityCollectionObject* self)
 | 
			
		||||
{
 | 
			
		||||
    std::ostringstream ss;
 | 
			
		||||
    if (!self->data) ss << "<UICollection (invalid internal object)>";
 | 
			
		||||
    else {
 | 
			
		||||
        ss << "<UICollection (" << self->data->size() << " child objects)>";
 | 
			
		||||
    }
 | 
			
		||||
    std::string repr_str = ss.str();
 | 
			
		||||
    return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIEntityCollection::init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds)
 | 
			
		||||
{
 | 
			
		||||
    PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required.");
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIEntityCollection::iter(PyUIEntityCollectionObject* self)
 | 
			
		||||
{
 | 
			
		||||
    //PyUIEntityCollectionIterObject* iterObj;
 | 
			
		||||
    //iterObj = (PyUIEntityCollectionIterObject*)PyUIEntityCollectionIterType.tp_alloc(&PyUIEntityCollectionIterType, 0);
 | 
			
		||||
    auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "EntityCollectionIter");
 | 
			
		||||
    auto iterObj = (PyUIEntityCollectionIterObject*)type->tp_alloc(type, 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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,184 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
#include "Common.h"
 | 
			
		||||
#include "Python.h"
 | 
			
		||||
#include "structmember.h"
 | 
			
		||||
#include "IndexTexture.h"
 | 
			
		||||
#include "Resources.h"
 | 
			
		||||
#include <list>
 | 
			
		||||
 | 
			
		||||
#include "PyCallable.h"
 | 
			
		||||
#include "PyTexture.h"
 | 
			
		||||
#include "PyColor.h"
 | 
			
		||||
#include "PyVector.h"
 | 
			
		||||
#include "PyFont.h"
 | 
			
		||||
 | 
			
		||||
#include "UIGridPoint.h"
 | 
			
		||||
#include "UIEntity.h"
 | 
			
		||||
#include "UIDrawable.h"
 | 
			
		||||
#include "UIBase.h"
 | 
			
		||||
 | 
			
		||||
class UIGrid: public UIDrawable
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
    std::shared_ptr<PyTexture> ptex;
 | 
			
		||||
public:
 | 
			
		||||
    UIGrid();
 | 
			
		||||
    //UIGrid(int, int, IndexTexture*, float, float, float, float);
 | 
			
		||||
    UIGrid(int, int, std::shared_ptr<PyTexture>, 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<PyTexture> getTexture();
 | 
			
		||||
    sf::Sprite sprite, output;
 | 
			
		||||
    sf::RenderTexture renderTexture;
 | 
			
		||||
    std::vector<UIGridPoint> points;
 | 
			
		||||
    std::shared_ptr<std::list<std::shared_ptr<UIEntity>>> entities;
 | 
			
		||||
 | 
			
		||||
    static int init(PyUIGridObject* self, PyObject* args, PyObject* kwds);
 | 
			
		||||
    static PyObject* get_grid_size(PyUIGridObject* self, void* closure);
 | 
			
		||||
    static PyObject* get_position(PyUIGridObject* self, void* closure);
 | 
			
		||||
    static int set_position(PyUIGridObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyObject* get_size(PyUIGridObject* self, void* closure);
 | 
			
		||||
    static int set_size(PyUIGridObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyObject* get_center(PyUIGridObject* self, void* closure);
 | 
			
		||||
    static int set_center(PyUIGridObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyObject* get_float_member(PyUIGridObject* self, void* closure);
 | 
			
		||||
    static int set_float_member(PyUIGridObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyObject* get_texture(PyUIGridObject* self, void* closure);
 | 
			
		||||
    static PyObject* py_at(PyUIGridObject* self, PyObject* o);
 | 
			
		||||
    static PyMethodDef methods[];
 | 
			
		||||
    static PyGetSetDef getsetters[];
 | 
			
		||||
    static PyObject* get_children(PyUIGridObject* self, void* closure);
 | 
			
		||||
    
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PyObject_HEAD
 | 
			
		||||
    std::shared_ptr<std::list<std::shared_ptr<UIEntity>>> data;
 | 
			
		||||
    std::shared_ptr<UIGrid> grid;
 | 
			
		||||
} PyUIEntityCollectionObject;
 | 
			
		||||
 | 
			
		||||
class UIEntityCollection {
 | 
			
		||||
public:
 | 
			
		||||
    static PySequenceMethods sqmethods;
 | 
			
		||||
    static PyObject* append(PyUIEntityCollectionObject* self, PyObject* o);
 | 
			
		||||
    static PyObject* remove(PyUIEntityCollectionObject* self, PyObject* o);
 | 
			
		||||
    static PyMethodDef methods[];
 | 
			
		||||
    static PyObject* repr(PyUIEntityCollectionObject* self);
 | 
			
		||||
    static int init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds);
 | 
			
		||||
    static PyObject* iter(PyUIEntityCollectionObject* self);
 | 
			
		||||
    static Py_ssize_t len(PyUIEntityCollectionObject* self);
 | 
			
		||||
    static PyObject* getitem(PyUIEntityCollectionObject* self, Py_ssize_t index);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PyObject_HEAD
 | 
			
		||||
    std::shared_ptr<std::list<std::shared_ptr<UIEntity>>> data;
 | 
			
		||||
    int index;
 | 
			
		||||
    int start_size;
 | 
			
		||||
} PyUIEntityCollectionIterObject;
 | 
			
		||||
 | 
			
		||||
class UIEntityCollectionIter {
 | 
			
		||||
public:
 | 
			
		||||
    static int init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds);
 | 
			
		||||
    static PyObject* next(PyUIEntityCollectionIterObject* self);
 | 
			
		||||
    static PyObject* repr(PyUIEntityCollectionIterObject* self);
 | 
			
		||||
    static PyObject* getitem(PyUIEntityCollectionObject* self, Py_ssize_t index);
 | 
			
		||||
    
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace mcrfpydef {
 | 
			
		||||
 | 
			
		||||
    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)UIGrid::repr,
 | 
			
		||||
        //.tp_hash = NULL,
 | 
			
		||||
        //.tp_iter
 | 
			
		||||
        //.tp_iternext
 | 
			
		||||
        .tp_flags = Py_TPFLAGS_DEFAULT,
 | 
			
		||||
        .tp_doc = PyDoc_STR("docstring"),
 | 
			
		||||
        .tp_methods = UIGrid::methods,
 | 
			
		||||
        //.tp_members = UIGrid::members,
 | 
			
		||||
        .tp_getset = UIGrid::getsetters,
 | 
			
		||||
        //.tp_base = NULL,
 | 
			
		||||
        .tp_init = (initproc)UIGrid::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<UIGrid>();
 | 
			
		||||
            return (PyObject*)self;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    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)UIEntityCollectionIter::repr,
 | 
			
		||||
        .tp_flags = Py_TPFLAGS_DEFAULT,
 | 
			
		||||
        .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"),
 | 
			
		||||
        .tp_iternext = (iternextfunc)UIEntityCollectionIter::next,
 | 
			
		||||
        //.tp_getset = UIEntityCollection::getset,
 | 
			
		||||
        .tp_init = (initproc)UIEntityCollectionIter::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;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    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)UIEntityCollection::repr,
 | 
			
		||||
        .tp_as_sequence = &UIEntityCollection::sqmethods,
 | 
			
		||||
        .tp_flags = Py_TPFLAGS_DEFAULT,
 | 
			
		||||
        .tp_doc = PyDoc_STR("Iterable, indexable collection of Entities"),
 | 
			
		||||
        .tp_iter = (getiterfunc)UIEntityCollection::iter,
 | 
			
		||||
        .tp_methods = UIEntityCollection::methods, // append, remove
 | 
			
		||||
        //.tp_getset = UIEntityCollection::getset,
 | 
			
		||||
        .tp_init = (initproc)UIEntityCollection::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;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,134 @@
 | 
			
		|||
#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)
 | 
			
		||||
{}
 | 
			
		||||
 | 
			
		||||
// Utility function to convert sf::Color to PyObject*
 | 
			
		||||
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
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIGridPoint::get_color(PyUIGridPointObject* self, void* closure) {
 | 
			
		||||
    if (reinterpret_cast<long>(closure) == 0) { // color
 | 
			
		||||
        return sfColor_to_PyObject(self->data->color);
 | 
			
		||||
    } else { // color_overlay
 | 
			
		||||
        return sfColor_to_PyObject(self->data->color_overlay);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIGridPoint::set_color(PyUIGridPointObject* self, PyObject* value, void* closure) {
 | 
			
		||||
    sf::Color color = PyObject_to_sfColor(value);
 | 
			
		||||
    if (reinterpret_cast<long>(closure) == 0) { // color
 | 
			
		||||
        self->data->color = color;
 | 
			
		||||
    } else { // color_overlay
 | 
			
		||||
        self->data->color_overlay = color;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIGridPoint::get_bool_member(PyUIGridPointObject* self, void* closure) {
 | 
			
		||||
    if (reinterpret_cast<long>(closure) == 0) { // walkable
 | 
			
		||||
        return PyBool_FromLong(self->data->walkable);
 | 
			
		||||
    } else { // transparent
 | 
			
		||||
        return PyBool_FromLong(self->data->transparent);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIGridPoint::set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure) {
 | 
			
		||||
    if (value == Py_True) {
 | 
			
		||||
        if (reinterpret_cast<long>(closure) == 0) { // walkable
 | 
			
		||||
            self->data->walkable = true;
 | 
			
		||||
        } else { // transparent
 | 
			
		||||
            self->data->transparent = true;
 | 
			
		||||
        }
 | 
			
		||||
    } else if (value == Py_False) {
 | 
			
		||||
        if (reinterpret_cast<long>(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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UIGridPoint::get_int_member(PyUIGridPointObject* self, void* closure) {
 | 
			
		||||
    switch(reinterpret_cast<long>(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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIGridPoint::set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure) {
 | 
			
		||||
    long val = PyLong_AsLong(value);
 | 
			
		||||
    if (PyErr_Occurred()) return -1;
 | 
			
		||||
 | 
			
		||||
    switch(reinterpret_cast<long>(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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyGetSetDef UIGridPoint::getsetters[] = {
 | 
			
		||||
    {"color", (getter)UIGridPoint::get_color, (setter)UIGridPoint::set_color, "GridPoint color", (void*)0},
 | 
			
		||||
    {"color_overlay", (getter)UIGridPoint::get_color, (setter)UIGridPoint::set_color, "GridPoint color overlay", (void*)1},
 | 
			
		||||
    {"walkable", (getter)UIGridPoint::get_bool_member, (setter)UIGridPoint::set_bool_member, "Is the GridPoint walkable", (void*)0},
 | 
			
		||||
    {"transparent", (getter)UIGridPoint::get_bool_member, (setter)UIGridPoint::set_bool_member, "Is the GridPoint transparent", (void*)1},
 | 
			
		||||
    {"tilesprite", (getter)UIGridPoint::get_int_member, (setter)UIGridPoint::set_int_member, "Tile sprite index", (void*)0},
 | 
			
		||||
    {"tile_overlay", (getter)UIGridPoint::get_int_member, (setter)UIGridPoint::set_int_member, "Tile overlay sprite index", (void*)1},
 | 
			
		||||
    {"uisprite", (getter)UIGridPoint::get_int_member, (setter)UIGridPoint::set_int_member, "UI sprite index", (void*)2},
 | 
			
		||||
    {NULL}  /* Sentinel */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PyObject* UIGridPointState::get_bool_member(PyUIGridPointStateObject* self, void* closure) {
 | 
			
		||||
    if (reinterpret_cast<long>(closure) == 0) { // visible
 | 
			
		||||
        return PyBool_FromLong(self->data->visible);
 | 
			
		||||
    } else { // discovered
 | 
			
		||||
        return PyBool_FromLong(self->data->discovered);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UIGridPointState::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<long>(closure) == 0) { // visible
 | 
			
		||||
        self->data->visible = truthValue;
 | 
			
		||||
    } else { // discovered
 | 
			
		||||
        self->data->discovered = truthValue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyGetSetDef UIGridPointState::getsetters[] = {
 | 
			
		||||
    {"visible", (getter)UIGridPointState::get_bool_member, (setter)UIGridPointState::set_bool_member, "Is the GridPointState visible", (void*)0},
 | 
			
		||||
    {"discovered", (getter)UIGridPointState::get_bool_member, (setter)UIGridPointState::set_bool_member, "Has the GridPointState been discovered", (void*)1},
 | 
			
		||||
    {NULL}  /* Sentinel */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,92 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
#include "Common.h"
 | 
			
		||||
#include "Python.h"
 | 
			
		||||
#include "structmember.h"
 | 
			
		||||
#include "IndexTexture.h"
 | 
			
		||||
#include "Resources.h"
 | 
			
		||||
#include <list>
 | 
			
		||||
 | 
			
		||||
#include "PyCallable.h"
 | 
			
		||||
#include "PyTexture.h"
 | 
			
		||||
#include "PyColor.h"
 | 
			
		||||
#include "PyVector.h"
 | 
			
		||||
#include "PyFont.h"
 | 
			
		||||
 | 
			
		||||
static PyObject* sfColor_to_PyObject(sf::Color color);
 | 
			
		||||
static sf::Color PyObject_to_sfColor(PyObject* obj);
 | 
			
		||||
 | 
			
		||||
class UIGrid;
 | 
			
		||||
class UIEntity;
 | 
			
		||||
class UIGridPoint;
 | 
			
		||||
class UIGridPointState;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PyObject_HEAD
 | 
			
		||||
    UIGridPoint* data;
 | 
			
		||||
    std::shared_ptr<UIGrid> grid;
 | 
			
		||||
} PyUIGridPointObject;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PyObject_HEAD
 | 
			
		||||
    UIGridPointState* data;
 | 
			
		||||
    std::shared_ptr<UIGrid> grid;
 | 
			
		||||
    std::shared_ptr<UIEntity> entity;
 | 
			
		||||
} PyUIGridPointStateObject;
 | 
			
		||||
 | 
			
		||||
// UIGridPoint - revised grid data for each point
 | 
			
		||||
class UIGridPoint
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    sf::Color color, color_overlay;
 | 
			
		||||
    bool walkable, transparent;
 | 
			
		||||
    int tilesprite, tile_overlay, uisprite;
 | 
			
		||||
    UIGridPoint();
 | 
			
		||||
 | 
			
		||||
    static int set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyGetSetDef getsetters[];
 | 
			
		||||
    static PyObject* get_color(PyUIGridPointObject* self, void* closure);
 | 
			
		||||
    static PyObject* get_int_member(PyUIGridPointObject* self, void* closure);
 | 
			
		||||
    static int set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyObject* get_bool_member(PyUIGridPointObject* self, void* closure);
 | 
			
		||||
    static int set_color(PyUIGridPointObject* self, PyObject* value, void* closure);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// UIGridPointState - entity-specific info for each cell
 | 
			
		||||
class UIGridPointState
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    bool visible, discovered;
 | 
			
		||||
 | 
			
		||||
    static PyObject* get_bool_member(PyUIGridPointStateObject* self, void* closure);
 | 
			
		||||
    static int set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyGetSetDef getsetters[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace mcrfpydef {
 | 
			
		||||
 | 
			
		||||
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 object",
 | 
			
		||||
    .tp_getset = UIGridPoint::getsetters,
 | 
			
		||||
    //.tp_init = (initproc)PyUIGridPoint_init, // TODO Define the init function
 | 
			
		||||
    .tp_new = PyType_GenericNew,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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 object", // TODO: Add PyUIGridPointState tp_init
 | 
			
		||||
    .tp_getset = UIGridPointState::getsetters,
 | 
			
		||||
    .tp_new = PyType_GenericNew,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,215 @@
 | 
			
		|||
#include "UISprite.h"
 | 
			
		||||
#include "GameEngine.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<PyTexture> _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<PyTexture> _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<PyTexture> UISprite::getTexture()
 | 
			
		||||
{
 | 
			
		||||
    return ptex;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UISprite::getSpriteIndex()
 | 
			
		||||
{
 | 
			
		||||
    return sprite_index;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObjectsEnum UISprite::derived_type()
 | 
			
		||||
{
 | 
			
		||||
    return PyObjectsEnum::UISPRITE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UISprite::get_float_member(PyUISpriteObject* self, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    auto member_ptr = reinterpret_cast<long>(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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UISprite::set_float_member(PyUISpriteObject* self, PyObject* value, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    float val;
 | 
			
		||||
    auto member_ptr = reinterpret_cast<long>(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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UISprite::get_int_member(PyUISpriteObject* self, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    auto member_ptr = reinterpret_cast<long>(closure);
 | 
			
		||||
    if (true) {}
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        PyErr_SetString(PyExc_AttributeError, "Invalid attribute");
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return PyLong_FromDouble(self->data->getSpriteIndex());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UISprite::set_int_member(PyUISpriteObject* self, PyObject* value, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    int val;
 | 
			
		||||
    auto member_ptr = reinterpret_cast<long>(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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* UISprite::get_texture(PyUISpriteObject* self, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    return self->data->getTexture()->pyObject();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UISprite::set_texture(PyUISpriteObject* self, PyObject* value, void* closure)
 | 
			
		||||
{
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyGetSetDef UISprite::getsetters[] = {
 | 
			
		||||
    {"x", (getter)UISprite::get_float_member, (setter)UISprite::set_float_member, "X coordinate of top-left corner",   (void*)0},
 | 
			
		||||
    {"y", (getter)UISprite::get_float_member, (setter)UISprite::set_float_member, "Y coordinate of top-left corner",   (void*)1},
 | 
			
		||||
    {"scale", (getter)UISprite::get_float_member, (setter)UISprite::set_float_member, "Size factor",                   (void*)2},
 | 
			
		||||
    {"sprite_number", (getter)UISprite::get_int_member, (setter)UISprite::set_int_member, "Which sprite on the texture is shown", NULL},
 | 
			
		||||
    {"texture", (getter)UISprite::get_texture, (setter)UISprite::set_texture,     "Texture object",                    NULL},
 | 
			
		||||
    {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UISPRITE},
 | 
			
		||||
    {NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PyObject* UISprite::repr(PyUISpriteObject* self)
 | 
			
		||||
{
 | 
			
		||||
    std::ostringstream ss;
 | 
			
		||||
    if (!self->data) ss << "<Sprite (invalid internal object)>";
 | 
			
		||||
    else {
 | 
			
		||||
        //auto sprite = self->data->sprite;
 | 
			
		||||
        ss << "<Sprite (x=" << self->data->getPosition().x << ", y=" << self->data->getPosition().y << ", " <<
 | 
			
		||||
            "scale=" << self->data->getScale().x << ", " <<
 | 
			
		||||
            "sprite_number=" << self->data->getSpriteIndex() << ")>";
 | 
			
		||||
    }
 | 
			
		||||
    std::string repr_str = ss.str();
 | 
			
		||||
    return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int UISprite::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<char**>(keywords), &x, &y, &texture, &sprite_index, &scale))
 | 
			
		||||
    {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check types for texture
 | 
			
		||||
    //if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&PyTextureType)){
 | 
			
		||||
    if (texture != NULL && !PyObject_IsInstance(texture, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))){
 | 
			
		||||
        PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    auto pytexture = (PyTextureObject*)texture;
 | 
			
		||||
    self->data = std::make_shared<UISprite>(pytexture->data, sprite_index, sf::Vector2f(x, y), scale);
 | 
			
		||||
    self->data->setPosition(sf::Vector2f(x, y));
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,97 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
#include "Common.h"
 | 
			
		||||
#include "Python.h"
 | 
			
		||||
#include "structmember.h"
 | 
			
		||||
#include "IndexTexture.h"
 | 
			
		||||
#include "Resources.h"
 | 
			
		||||
#include <list>
 | 
			
		||||
 | 
			
		||||
#include "PyCallable.h"
 | 
			
		||||
#include "PyTexture.h"
 | 
			
		||||
#include "PyColor.h"
 | 
			
		||||
#include "PyVector.h"
 | 
			
		||||
#include "PyFont.h"
 | 
			
		||||
#include "UIDrawable.h"
 | 
			
		||||
#include "UIBase.h"
 | 
			
		||||
 | 
			
		||||
class UISprite: public UIDrawable
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
    int sprite_index;
 | 
			
		||||
    sf::Sprite sprite;
 | 
			
		||||
protected:
 | 
			
		||||
    std::shared_ptr<PyTexture> ptex;
 | 
			
		||||
public:
 | 
			
		||||
    UISprite();
 | 
			
		||||
    UISprite(std::shared_ptr<PyTexture>, 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<PyTexture> _ptex, int _sprite_index=-1);
 | 
			
		||||
    std::shared_ptr<PyTexture> getTexture();
 | 
			
		||||
 | 
			
		||||
    PyObjectsEnum derived_type() override final;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    static PyObject* get_float_member(PyUISpriteObject* self, void* closure);
 | 
			
		||||
    static int set_float_member(PyUISpriteObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyObject* get_int_member(PyUISpriteObject* self, void* closure);
 | 
			
		||||
    static int set_int_member(PyUISpriteObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyObject* get_texture(PyUISpriteObject* self, void* closure);
 | 
			
		||||
    static int set_texture(PyUISpriteObject* self, PyObject* value, void* closure);
 | 
			
		||||
    static PyGetSetDef getsetters[];
 | 
			
		||||
    static PyObject* repr(PyUISpriteObject* self);
 | 
			
		||||
    static int init(PyUISpriteObject* self, PyObject* args, PyObject* kwds);
 | 
			
		||||
    
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//typedef struct {
 | 
			
		||||
//    PyObject_HEAD
 | 
			
		||||
//    std::shared_ptr<UISprite> data;
 | 
			
		||||
//} PyUISpriteObject;
 | 
			
		||||
 | 
			
		||||
namespace mcrfpydef {
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    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)UISprite::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 = UISprite::getsetters,
 | 
			
		||||
        //.tp_base = NULL,
 | 
			
		||||
        .tp_init = (initproc)UISprite::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<UICaption>();
 | 
			
		||||
            return (PyObject*)self;
 | 
			
		||||
        }
 | 
			
		||||
    }; 
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue