Fully python-driven scene. Lots of interaction needs testing but the broad strokes are there for mouse pan/zoom on multiple grids and any number of UIs

This commit is contained in:
John McCardle 2023-03-04 23:04:16 -05:00
parent a4b6c2c428
commit 257aa3c3d2
8 changed files with 191 additions and 3 deletions

View File

@ -3,6 +3,7 @@
#include "UITestScene.h" #include "UITestScene.h"
#include "ActionCode.h" #include "ActionCode.h"
#include "McRFPy_API.h" #include "McRFPy_API.h"
#include "PythonScene.h"
GameEngine::GameEngine() GameEngine::GameEngine()
{ {
@ -10,18 +11,21 @@ GameEngine::GameEngine()
window.create(sf::VideoMode(1024, 768), "McRogueFace Engine by John McCardle"); window.create(sf::VideoMode(1024, 768), "McRogueFace Engine by John McCardle");
visible = window.getDefaultView(); visible = window.getDefaultView();
window.setFramerateLimit(30); window.setFramerateLimit(30);
scene = "menu"; scene = "py";
//std::cout << "Constructing MenuScene" << std::endl; //std::cout << "Constructing MenuScene" << std::endl;
scenes["menu"] = new MenuScene(this); scenes["menu"] = new MenuScene(this);
//std::cout << "Constructed MenuScene" <<std::endl; //std::cout << "Constructed MenuScene" <<std::endl;
scenes["play"] = new UITestScene(this); scenes["play"] = new UITestScene(this);
//api = new McRFPy_API(this); //api = new McRFPy_API(this);
McRFPy_API::game = this; McRFPy_API::game = this;
McRFPy_API::api_init(); McRFPy_API::api_init();
McRFPy_API::executePyString("import mcrfpy"); McRFPy_API::executePyString("import mcrfpy");
McRFPy_API::executePyString("from UIMenu import *"); McRFPy_API::executePyString("from UIMenu import *");
McRFPy_API::executePyString("from Grid import *"); McRFPy_API::executePyString("from Grid import *");
scenes["py"] = new PythonScene(this, "TestScene");
IndexSprite::game = this; IndexSprite::game = this;
} }

View File

@ -15,7 +15,7 @@ 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), grid_x(gx), grid_y(gy),
zoom(1.0f), center_x((gx/2) * gs), center_y((gy/2) * gs), zoom(1.0f), center_x((gx/2) * gs), center_y((gy/2) * gs),
texture_width(12), texture_height(11) texture_width(12), texture_height(11), visible(false)
{ {
//grid_size = gs; //grid_size = gs;
//zoom = 1.0f; //zoom = 1.0f;

View File

@ -20,11 +20,13 @@ private:
public: public:
Grid(); Grid();
sf::RectangleShape box; // view on window sf::RectangleShape box; // view on window
bool visible;
sf::Texture texture; sf::Texture texture;
sf::Sprite sprite, output; sf::Sprite sprite, output;
sf::RenderTexture renderTexture; sf::RenderTexture renderTexture;
void setSprite(int); void setSprite(int);
const int texture_width, texture_height; const int texture_width, texture_height;
auto contains(sf::Vector2i p) { return box.getGlobalBounds().contains(p.x, p.y); }
Grid(int gx, int gy, int gs, int _x, int _y, int _w, int _h); Grid(int gx, int gy, int gs, int _x, int _y, int _w, int _h);
int grid_x, grid_y; // rectangle map size (integer - sprites) int grid_x, grid_y; // rectangle map size (integer - sprites)

View File

@ -7,6 +7,7 @@
std::map<std::string, UIMenu*> McRFPy_API::menus; std::map<std::string, UIMenu*> McRFPy_API::menus;
std::map<std::string, Grid*> McRFPy_API::grids; std::map<std::string, Grid*> McRFPy_API::grids;
std::map<std::string, PyObject*> McRFPy_API::callbacks; std::map<std::string, PyObject*> McRFPy_API::callbacks;
EntityManager McRFPy_API::entities;
static PyMethodDef mcrfpyMethods[] = { static PyMethodDef mcrfpyMethods[] = {
{"drawSprite", McRFPy_API::_drawSprite, METH_VARARGS, {"drawSprite", McRFPy_API::_drawSprite, METH_VARARGS,

View File

@ -42,7 +42,7 @@ public:
// Jank mode engage: let the API hold data for Python to hack on // Jank mode engage: let the API hold data for Python to hack on
static std::map<std::string, UIMenu*> menus; static std::map<std::string, UIMenu*> menus;
EntityManager entities; // this is also kinda good, entities not on the current grid can still act (like monsters following you through doors??) static 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<std::string, Grid*> grids; static std::map<std::string, Grid*> grids;
static std::map<std::string, PyObject*> callbacks; static std::map<std::string, PyObject*> callbacks;

159
src/PythonScene.cpp Normal file
View File

@ -0,0 +1,159 @@
#include "PythonScene.h"
#include "ActionCode.h"
#include "McRFPy_API.h"
PythonScene::PythonScene(GameEngine* g, std::string pymodule)
: Scene(g) {
// mouse events
registerAction(ActionCode::MOUSEBUTTON + sf::Mouse::Left, "click");
registerAction(ActionCode::MOUSEBUTTON + sf::Mouse::Left, "rclick");
registerAction(ActionCode::MOUSEWHEEL + ActionCode::WHEEL_DEL, "wheel_up");
registerAction(ActionCode::MOUSEWHEEL + ActionCode::WHEEL_NEG + ActionCode::WHEEL_DEL, "wheel_down");
// keyboard events
registerAction(ActionCode::KEY + sf::Keyboard::Q, "upleft");
registerAction(ActionCode::KEY + sf::Keyboard::W, "up");
registerAction(ActionCode::KEY + sf::Keyboard::E, "upright");
registerAction(ActionCode::KEY + sf::Keyboard::A, "left");
registerAction(ActionCode::KEY + sf::Keyboard::S, "down");
registerAction(ActionCode::KEY + sf::Keyboard::D, "right");
registerAction(ActionCode::KEY + sf::Keyboard::Z, "downleft");
registerAction(ActionCode::KEY + sf::Keyboard::X, "wait");
registerAction(ActionCode::KEY + sf::Keyboard::C, "downright");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad7, "upleft");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad8, "up");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad9, "upright");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad4, "left");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad5, "wait");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad6, "right");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad1, "downleft");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad2, "down");
registerAction(ActionCode::KEY + sf::Keyboard::Numpad3, "downright");
// window resize
registerAction(0, "event");
dragging = false;
// import pymodule and call start()
McRFPy_API::executePyString("import " + pymodule);
McRFPy_API::executePyString(pymodule + ".start()");
}
void PythonScene::update() {
McRFPy_API::entities.update();
// check if left click is still down & mouse has moved
// continue the drag motion
if (dragging && drag_grid) {
auto mousepos = sf::Mouse::getPosition(game->getWindow());
auto dx = mousepos.x - mouseprev.x,
dy = mousepos.y - mouseprev.y;
drag_grid->center_x += (dx / drag_grid->zoom);
drag_grid->center_y += (dy / drag_grid->zoom);
}
}
void PythonScene::doLClick(sf::Vector2i mousepos) {
// UI buttons get first chance
for (auto pair : McRFPy_API::menus) {
if (!pair.second->visible) continue;
for (auto b : pair.second->buttons) {
if (b.contains(mousepos)) {
McRFPy_API::doAction(b.getAction());
return;
}
}
}
// left clicking a grid to select a square
for (auto pair : McRFPy_API::grids) {
if (!pair.second->visible) continue;
if (pair.second->contains(mousepos)) {
// grid was clicked
return;
}
}
}
void PythonScene::doRClick(sf::Vector2i mousepos) {
// just grids for right click
for (auto pair : McRFPy_API::grids) {
if (!pair.second->visible) continue;
if (pair.second->contains(mousepos)) {
// grid was clicked
return;
}
}
}
void PythonScene::doZoom(sf::Vector2i mousepos, int value) {
// just grids for right click
for (auto pair : McRFPy_API::grids) {
if (!pair.second->visible) continue;
if (pair.second->contains(mousepos)) {
// grid was zoomed
float new_zoom = pair.second->zoom + (value * 0.25);
if (new_zoom >= 0.5 && new_zoom <= 5.0) {
pair.second->zoom = new_zoom;
}
}
}
}
void PythonScene::doAction(std::string name, std::string type) {
auto mousepos = sf::Mouse::getPosition(game->getWindow());
if (ACTIONONCE("click")) {
// left click start
dragstart = mousepos;
mouseprev = mousepos;
dragging = true;
// determine the grid that contains the cursor
for (auto pair : McRFPy_API::grids) {
if (!pair.second->visible) continue;
if (pair.second->contains(mousepos)) {
// grid was clicked
drag_grid = pair.second;
}
}
}
else if (ACTIONAFTER("click")) {
// left click end
// if click ended without starting a drag event, try lclick?
if (dragstart == mousepos) {
// mouse did not move, do click
doLClick(mousepos);
}
dragging = false;
drag_grid = NULL;
}
else if (ACTIONONCE("rclick")) {
// not going to test for right click drag - just rclick
doRClick(mousepos);
}
else if (ACTIONONCE("wheel_up")) {
// try zoom in
doZoom(mousepos, 1);
}
else if (ACTIONONCE("wheel_down")) {
// try zoom out
doZoom(mousepos, -1);
}
}
void PythonScene::sRender() {
game->getWindow().clear();
for (auto pair: McRFPy_API::grids) {
if (!pair.second->visible) continue;
pair.second->render(game->getWindow());
}
for (auto pair: McRFPy_API::menus) {
if (!pair.second->visible) continue;
pair.second->render(game->getWindow());
}
game->getWindow().display();
}

21
src/PythonScene.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "Common.h"
#include "Scene.h"
#include "GameEngine.h"
#include "Grid.h"
class PythonScene: public Scene
{
sf::Vector2i dragstart, mouseprev;
bool dragging;
Grid* drag_grid;
void doLClick(sf::Vector2i);
void doRClick(sf::Vector2i);
void doZoom(sf::Vector2i, int);
public:
PythonScene(GameEngine*, std::string);
void update() override final;
void doAction(std::string, std::string) override final;
void sRender() override final;
};

View File

@ -3,6 +3,7 @@
// macros for scene input // macros for scene input
#define ACTION(X, Y) (name.compare(X) == 0 && type.compare(Y) == 0) #define ACTION(X, Y) (name.compare(X) == 0 && type.compare(Y) == 0)
#define ACTIONONCE(X) ((name.compare(X) == 0 && type.compare("start") == 0 && !actionState[name])) #define ACTIONONCE(X) ((name.compare(X) == 0 && type.compare("start") == 0 && !actionState[name]))
#define ACTIONAFTER(X) ((name.compare(X) == 0 && type.compare("end") == 0 && actionState[name]))
#include "Common.h" #include "Common.h"
//#include "GameEngine.h" //#include "GameEngine.h"