From e295bfb74238fd92df8d8d7a0aa312c983e4c0b8 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Fri, 3 Mar 2023 22:16:47 -0500 Subject: [PATCH] python callbacks, working on grid --- src/Grid.cpp | 180 ++++++++++++++++++++++++++++++++++++++------ src/Grid.h | 4 + src/McRFPy_API.cpp | 38 ++++++++-- src/McRFPy_API.h | 6 ++ src/UIMenu.cpp | 9 ++- src/UITestScene.cpp | 70 ++++++++++++++--- 6 files changed, 260 insertions(+), 47 deletions(-) diff --git a/src/Grid.cpp b/src/Grid.cpp index 87dd12b..bf64431 100644 --- a/src/Grid.cpp +++ b/src/Grid.cpp @@ -12,7 +12,7 @@ void Grid::setSprite(int ti) } Grid::Grid(int gx, int gy, int gs, int _x, int _y, int _w, int _h): - grid_size(gs), + grid_size(gs), grid_x(gx), grid_y(gy), zoom(1.0f), center_x(gx), center_y(gy), texture_width(12), texture_height(11) @@ -44,53 +44,114 @@ Grid::Grid(int gx, int gy, int gs, int _x, int _y, int _w, int _h): sprite.setTexture(texture); } +void Grid::screenToGrid(int sx, int sy, int& gx, int& gy) { + float width_sq = box.getSize().x / (grid_size * zoom); + float height_sq = box.getSize().y / (grid_size * zoom); + float left_edge = center_x - (width_sq / 2.0); + float right_edge = center_x + (width_sq / 2.0); + float top_edge = center_y - (height_sq / 2.0); + float bottom_edge = center_y + (height_sq / 2.0); + + float grid_px = zoom * grid_size; + std::cout << "##############################" << + "\nscreen coord: (" << sx << ", " << sy << ")" << std::endl; + + sx -= box.getPosition().x; + sy -= box.getPosition().y; + + std::cout << "box coord: (" << sx << ", " << sy << ")" << std::endl; + float mouse_x_sq = sx / grid_px; + float mouse_y_sq = sy / grid_px; + + float ans_x = mouse_x_sq + left_edge; + float ans_y = mouse_y_sq + top_edge; + + gx = ans_x; + gy = ans_y; + std::cout << + "C: (" << center_x << ", " << center_y << ")" << std::endl << + "W: " << width_sq << " H: " << height_sq << std::endl << + "L: " << left_edge << " T: " << top_edge << std::endl << + "R: " << right_edge << " B: " << bottom_edge << std::endl << + "Grid Px: " << grid_px << "( zoom: " << zoom << ")" << std::endl << + "answer: G(" << ans_x << ", " << ans_y << ")" << std::endl << + "##############################" << + std::endl; +} + void Grid::render(sf::RenderWindow & window) { renderTexture.clear(); //renderTexture.draw(box); // sprites that are visible according to zoom, center_x, center_y, and box width - auto box_size = box.getSize(); - float view_width = box_size.x / (grid_size * zoom); - float view_height = box_size.y / (grid_size * zoom); + float width_sq = box.getSize().x / (grid_size * zoom); + float height_sq = box.getSize().y / (grid_size * zoom); + float left_edge = center_x - (width_sq / 2.0); + float right_edge = center_x + (width_sq / 2.0); + float top_edge = center_y - (height_sq / 2.0); + float bottom_edge = center_y + (height_sq / 2.0); - float top_offset = (center_y * grid_size) - box_size.y/2.0f; - float left_offset = (center_x * grid_size) - box_size.x/2.0f; + //auto box_size = box.getSize(); + //float view_width = box_size.x / (grid_size * zoom); + //float view_height = box_size.y / (grid_size * zoom); + + //float top_offset = (center_y * grid_size) - box_size.y/2.0f; + //float left_offset = (center_x * grid_size) - box_size.x/2.0f; sprite.setScale(sf::Vector2f(zoom, zoom)); + sf::RectangleShape r; // for colors and overlays + r.setSize(sf::Vector2f(grid_size * zoom, grid_size * zoom)); + r.setOutlineThickness(0); - auto box_pos = box.getPosition(); + //auto box_pos = box.getPosition(); - int x_start = std::floor(center_x - view_width/2.0f); - int x_end = std::ceil(center_x + view_width/2.0f); - int y_start = std::floor(center_y - view_height/2.0f); - int y_end = std::ceil(center_y + view_height/2.0f); + //int x_start = std::floor(center_x - view_width/2.0f); + //int x_end = std::ceil(center_x + view_width/2.0f); + //int y_start = std::floor(center_y - view_height/2.0f); + //int y_end = std::ceil(center_y + view_height/2.0f); + int x_limit = left_edge + width_sq + 1; + if (x_limit > grid_x) x_limit = grid_x; - for (int x = 0; - x < grid_x; //x < view_width; - x++) + int y_limit = top_edge + height_sq + 1; + if (y_limit > grid_y) y_limit = grid_y; + + for (float x = (left_edge >= 0 ? left_edge : 0); + x < x_limit; //x < view_width; + x+=1.0) { - for (int y = 0; - y < grid_y; //y < view_height; - y++) + for (float y = (top_edge >= 0 ? top_edge : 0); + y < y_limit; //y < view_height; + y+=1.0) { + auto pixel_pos = sf::Vector2f( + (x - left_edge) * (zoom * grid_size), + (y - top_edge) * (zoom * grid_size)); + auto gridpoint = at(std::floor(x), std::floor(y)); + // convert grid's coordinate to pixel coords to draw //float window_x = (x_start + x) * grid_size * zoom; //float window_y = (y_start + y) * grid_size * zoom; - float natural_x = x * grid_size * zoom; - float natural_y = y * grid_size * zoom; - sprite.setPosition( - sf::Vector2f(natural_x - left_offset, - natural_y - top_offset)); + //float natural_x = x * grid_size * zoom; + //float natural_y = y * grid_size * zoom; + sprite.setPosition(pixel_pos); + //sf::Vector2f(natural_x - left_offset, + // natural_y - top_offset)); - auto gridPoint = at(x, y); // color? + //r.setPosition(sf::Vector2f(natural_x - left_offset, + // natural_y - top_offset)); + 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? - setSprite(at(x, y).tilesprite); - renderTexture.draw(sprite); + if (gridpoint.tilesprite != -1) { + setSprite(gridpoint.tilesprite); + renderTexture.draw(sprite); + } // overlay @@ -100,6 +161,22 @@ void Grid::render(sf::RenderWindow & window) } // 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 @@ -111,3 +188,56 @@ GridPoint& Grid::at(int x, int y) { return points[y * grid_x + x]; } + +GridPoint* Grid::atScreenPixel(int sx, int sy, int* gx=NULL, int* gy=NULL) { + + int _x, _y; + screenToGrid(sx, sy, _x, _y); + std::cout << "screenToGrid gave: (" << _x << ", " << _y << ")" << std::endl; + + auto p = box.getPosition(); + auto s = box.getSize(); + + // debug render values + int x_start = std::floor(center_x - s.x/2.0f); + int x_end = std::ceil(center_x + s.x/2.0f); + int y_start = std::floor(center_y - s.y/2.0f); + int y_end = std::ceil(center_y + s.y/2.0f); + std::cout << "Center: " << center_x << ", " << center_y << std::endl << + "Start grid: " << x_start << ", " << y_start << std::endl << + "End grid: " << x_end << ", " << y_end << std::endl; + + + // check if the mouse is even over the grid + if (sx < p.x || sx > p.x+s.x || sy < p.y || sy > p.y+s.y) { + std::cout << "(" << sx << ", " << sy << ") not over grid" << std::endl; + return NULL; + } + // get dx, dy to box's center + int dx = sx - (s.x/2.0 + p.x), + dy = sy - (s.y/2.0 + p.y); + + // divide dx, dy by (gridsize * zoom) to get # in boxes + int gdx = dx / (grid_size * zoom), + gdy = dy / (grid_size * zoom); + + int targetx = gdx + center_x, + targety = gdy + center_y; + // return + if (gx) { + *gx = targetx; + } + + if (gy) { + *gy = targety; + } + + if (targetx >= 0 && targetx <= grid_x && targety >= 0 && targety <= grid_y) { + return &points[targety * grid_x + targetx]; + } + else { + std::cout << "(" << sx << ", " << sy << ") not over actual grid content" << std::endl; + return NULL; + } + +} diff --git a/src/Grid.h b/src/Grid.h index ff0c055..1fc8be2 100644 --- a/src/Grid.h +++ b/src/Grid.h @@ -35,4 +35,8 @@ public: void render(sf::RenderWindow&); // draw to screen GridPoint& at(int, int); + void screenToGrid(int, int, int&, int&); + GridPoint* atScreenPixel(int, int, int*, int*); + + }; diff --git a/src/McRFPy_API.cpp b/src/McRFPy_API.cpp index b86ddff..04408a2 100644 --- a/src/McRFPy_API.cpp +++ b/src/McRFPy_API.cpp @@ -6,6 +6,7 @@ // static class members...? std::map McRFPy_API::menus; std::map McRFPy_API::grids; +std::map McRFPy_API::callbacks; static PyMethodDef mcrfpyMethods[] = { {"drawSprite", McRFPy_API::_drawSprite, METH_VARARGS, @@ -32,6 +33,9 @@ static PyMethodDef mcrfpyMethods[] = { {"createTexture", McRFPy_API::_createTexture, METH_VARARGS, "Create a new texture (filename_str, grid_size, width, height) - grid_size is in pixels (only square sprites for now), width and height are in tiles"}, + {"registerPyAction", McRFPy_API::_registerPyAction, METH_VARARGS, + "Register a callable Python object to correspond to an action string. (actionstr, callable)"}, + {NULL, NULL, 0, NULL} }; @@ -307,7 +311,7 @@ PyObject* McRFPy_API::_modMenu(PyObject* self, PyObject* args) { // jank, or dank? iterate over .captions, .buttons, .sprites to modify them // captions PyObject* captionlist = PyObject_GetAttrString(o, "captions"); - std::cout << PyUnicode_AsUTF8(PyObject_Repr(captionlist)) << std::endl; + //std::cout << PyUnicode_AsUTF8(PyObject_Repr(captionlist)) << std::endl; for (int i = 0; i < menu->captions.size(); i++) { PyObject* captionobj = PyList_GetItem(captionlist, i); menu->captions[i].setString( @@ -325,7 +329,7 @@ PyObject* McRFPy_API::_modMenu(PyObject* self, PyObject* args) { // buttons PyObject* buttonlist = PyObject_GetAttrString(o, "buttons"); - std::cout << PyUnicode_AsUTF8(PyObject_Repr(buttonlist)) << std::endl; + //std::cout << PyUnicode_AsUTF8(PyObject_Repr(buttonlist)) << std::endl; for (int i = 0; i < menu->buttons.size(); i++) { PyObject* buttonobj = PyList_GetItem(buttonlist, i); menu->buttons[i].setPosition(sf::Vector2f( @@ -338,7 +342,7 @@ PyObject* McRFPy_API::_modMenu(PyObject* self, PyObject* args) { ); menu->buttons[i].setSize(sizevec); PyObject* btncolor = PyObject_GetAttrString(buttonobj, "bgcolor"); - std::cout << PyUnicode_AsUTF8(PyObject_Repr(btncolor)) << std::endl; + //std::cout << PyUnicode_AsUTF8(PyObject_Repr(btncolor)) << std::endl; menu->buttons[i].setBackground( sf::Color( PyLong_AsLong(PyTuple_GetItem(btncolor, 0)), @@ -346,24 +350,24 @@ PyObject* McRFPy_API::_modMenu(PyObject* self, PyObject* args) { PyLong_AsLong(PyTuple_GetItem(btncolor, 2)) )); PyObject* btxtcolor = PyObject_GetAttrString(buttonobj, "textcolor"); - std::cout << PyUnicode_AsUTF8(PyObject_Repr(btxtcolor)) << std::endl; + //std::cout << PyUnicode_AsUTF8(PyObject_Repr(btxtcolor)) << std::endl; menu->buttons[i].setTextColor( sf::Color( PyLong_AsLong(PyTuple_GetItem(btxtcolor, 0)), PyLong_AsLong(PyTuple_GetItem(btxtcolor, 1)), PyLong_AsLong(PyTuple_GetItem(btxtcolor, 2)) )); - std::cout << PyObject_Repr(PyObject_GetAttrString(buttonobj, "text")) << std::endl; + //std::cout << PyObject_Repr(PyObject_GetAttrString(buttonobj, "text")) << std::endl; menu->buttons[i].caption.setString( PyUnicode_AsUTF8(PyObject_GetAttrString(buttonobj, "text"))); - std::cout << PyObject_Repr(PyObject_GetAttrString(buttonobj, "actioncode")) << std::endl; + //std::cout << PyObject_Repr(PyObject_GetAttrString(buttonobj, "actioncode")) << std::endl; menu->buttons[i].action = PyUnicode_AsUTF8(PyObject_GetAttrString(buttonobj, "actioncode")); } // sprites PyObject* spriteslist = PyObject_GetAttrString(o, "sprites"); - std::cout << PyUnicode_AsUTF8(PyObject_Repr(spriteslist)) << std::endl; + //std::cout << PyUnicode_AsUTF8(PyObject_Repr(spriteslist)) << std::endl; for (int i = 0; i < menu->sprites.size(); i++) { PyObject* spriteobj = PyList_GetItem(spriteslist, i); menu->sprites[i].texture_index = @@ -461,3 +465,23 @@ int McRFPy_API::createTexture(std::string filename, int grid_size, int grid_widt return game->textures.size() - 1; } + +// python connection +PyObject* McRFPy_API::_registerPyAction(PyObject *self, PyObject *args) +{ + PyObject* callable; + const char * actionstr; + if (!PyArg_ParseTuple(args, "sO", &actionstr, &callable)) return NULL; + callbacks[std::string(actionstr)] = callable; + Py_INCREF(callable); + + // return None correctly + Py_INCREF(Py_None); + return Py_None; +} + +void McRFPy_API::doAction(std::string actionstr) { + if (callbacks.find(actionstr) == callbacks.end()) return; + //std::cout << "Calling: " << PyUnicode_AsUTF8(PyObject_Repr(callbacks[actionstr])) << std::endl; + PyObject_Call(callbacks[actionstr], PyTuple_New(0), NULL); +} diff --git a/src/McRFPy_API.h b/src/McRFPy_API.h index 45c675f..8b88aa9 100644 --- a/src/McRFPy_API.h +++ b/src/McRFPy_API.h @@ -45,6 +45,8 @@ public: EntityManager entities; // this is also kinda good, entities not on the current grid can still act (like monsters following you through doors??) static std::map grids; + static std::map callbacks; + // Jank Python Method Exposures static PyObject* _createMenu(PyObject*, PyObject*); // creates a new menu object in McRFPy_API::menus static PyObject* _listMenus(PyObject*, PyObject*); @@ -65,6 +67,8 @@ public: //static PyObject* _createGrid(PyObject*, PyObject*); //static PyObject* _listGrids(PyObject*, PyObject*); + + static PyObject* _registerPyAction(PyObject*, PyObject*); // Jank Functionality static UIMenu* createMenu(int posx, int posy, int sizex, int sizey); @@ -74,6 +78,8 @@ public: static int createTexture(std::string filename, int grid_size, int grid_width, int grid_height); //static void playSound(const char * filename); //static void playMusic(const char * filename); + + static void doAction(std::string); // McRFPy_API(GameEngine*); diff --git a/src/UIMenu.cpp b/src/UIMenu.cpp index 15a5c4b..485df59 100644 --- a/src/UIMenu.cpp +++ b/src/UIMenu.cpp @@ -13,16 +13,17 @@ UIMenu::UIMenu(sf::Font & _font) void UIMenu::render(sf::RenderWindow & window) { window.draw(box); + for (auto& s: sprites) { + auto _s = s.drawable(); + _s.move(box.getPosition()); + window.draw(_s); + } for (auto& c : captions) { //auto s = std::string(c.getString()); //std::cout << s << std::flush << std::endl; window.draw(c); } for (auto& b : buttons) { b.render(window); } - for (auto& s: sprites) { - auto _s = s.drawable(); - // TODO: s.move or whatever to make it's 0,0 relative to menu box - window.draw(_s); } } void UIMenu::refresh() diff --git a/src/UITestScene.cpp b/src/UITestScene.cpp index 6f9cea4..c975da7 100644 --- a/src/UITestScene.cpp +++ b/src/UITestScene.cpp @@ -19,8 +19,15 @@ void setSpriteTexture(int ti) test_sprite.setTextureRect(sf::IntRect(tx * texture_size, ty * texture_size, texture_size, texture_size)); } +// random for this test +std::random_device rd; // Will be used to obtain a seed for the random number engine +std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() +std::uniform_int_distribution<> distrib(0, 10); +std::uniform_int_distribution<> coldistrib(64, 192); +std::uniform_real_distribution<> snoise(-3, 3); + UITestScene::UITestScene(GameEngine* g) -: Scene(g), grid(150, 150, 16, 20, 20, 800, 520) +: Scene(g), grid(10, 20, 16, 20, 20, 800, 520) { // demo sprites from texture file texture.loadFromFile("./assets/kenney_tinydungeon.png"); @@ -32,21 +39,26 @@ UITestScene::UITestScene(GameEngine* g) test_sprite.setScale(sf::Vector2f(4.0f, 4.0f)); setSpriteTexture(0); - // random for this test - std::random_device rd; // Will be used to obtain a seed for the random number engine - std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() - std::uniform_int_distribution<> distrib(84, 100); + // Test grid with random sprite noise - for (int _x = 0; _x < 150; _x++) - for (int _y = 0; _y < 150; _y++) + //std::array ground = {0, 12, 24, 48, 42, 48, 49, 50, 51, -1, -1}; + std::array ground = {0, 0, 0, -1, -1, -1, -1, -1, -1, -1, 51}; + + for (int _x = 0; _x < 10; _x++) + for (int _y = 0; _y < 20; _y++) { //grid.at(_x, _y).tilesprite = _y*11 + _x; //if (!_x % 2 || _y == 0) grid.at(_x, _y).tilesprite = 121; //else - grid.at(_x, _y).tilesprite = distrib(gen); + auto &gridpoint = grid.at(_x, _y); + grid.at(_x, _y).tilesprite = ground[distrib(gen)]; + grid.at(_x, _y).color = sf::Color( + coldistrib(gen), 0, 0); - for (int _x = 0; _x < 30; _x++) + } + + for (int _x = 0; _x < 10; _x++) { grid.at(_x, 0).tilesprite = 121; grid.at(_x, 5).tilesprite = 123; @@ -79,6 +91,7 @@ UITestScene::UITestScene(GameEngine* g) registerAction(ActionCode::MOUSEWHEEL + ActionCode::WHEEL_NEG + ActionCode::WHEEL_DEL, "wheel_down"); registerAction(ActionCode::KEY + sf::Keyboard::Num4, "sound_test"); + registerAction(ActionCode::KEY + sf::Keyboard::Q, "gridtests"); registerAction(0, "event"); @@ -117,7 +130,7 @@ UITestScene::UITestScene(GameEngine* g) void UITestScene::update() { - + /* if (abs(desired_angle - test_ship.angle) < 1) { test_ship.angle = desired_angle; @@ -127,6 +140,19 @@ void UITestScene::update() } else if (test_ship.angle > desired_angle){ test_ship.angle -= 1; } + */ + + /* + // Too slow: updating every grid manually + // Restrict to visible squares... use noise for coherence? + for (int _x = 0; _x < grid.grid_x; _x++) + for (int _y = 0; _y < grid.grid_y; _y++) { + auto &square = grid.at(_x, _y); + square.color.r += snoise(gen); + if (square.color.r > 254) square.color.r = 254; + if (square.color.r < 1) square.color.r = 1; + } + */ entities.update(); @@ -184,6 +210,20 @@ void UITestScene::doAction(std::string name, std::string type) } } } + if (!ui_clicked) { + for (auto pair : McRFPy_API::menus) { + if (!pair.second->visible) continue; + for (auto b : pair.second->buttons) + { + if (b.contains(mousepos)) { + std::cout << "(api) " << b.getAction() <getWindow()); auto worldpos = game->getWindow().mapPixelToCoords(mousepos, viewport); @@ -242,6 +282,13 @@ void UITestScene::doAction(std::string name, std::string type) viewport.zoom(zoom); } + if (ACTION("gridtests", "start")) { + int tx, ty; + auto mousepos = sf::Mouse::getPosition(game->getWindow()); + GridPoint* pgrid = grid.atScreenPixel(mousepos.x, mousepos.y, &tx, &ty); + std::cout << "\ntx: " << tx << " ty: " << ty << std::endl; + } + // after processing: set actionState if (type.compare("start") == 0 && !actionState[name]) { actionState[name] = true; } else if (type.compare("end") == 0 && actionState[name]) { actionState[name] = false; } @@ -277,6 +324,7 @@ void UITestScene::sRender() // Python API menus for (auto pair: McRFPy_API::menus) { + if (!pair.second->visible) continue; pair.second->render(game->getWindow()); } @@ -284,7 +332,7 @@ void UITestScene::sRender() //McRFPy_API::executePyString("mcrfpy.drawSprite(123, 36, 10)"); // draw test sprite on top of everything - game->getWindow().draw(test_sprite); + //game->getWindow().draw(test_sprite); game->getWindow().display(); }