Tinkering with input
I want to move keyboard input defs to the Python API. I laid the groundwork for it today. From the JANKFILE: - working on API endpoint `_registerInputAction`. it will add "_py" as a suffix to the action string and register it along with other scene actions. - Adding public Scene methods. These are on the base class with default of return `false`. `bool Scene::registerActionInjected(int code, std::string name)` and `unregisterActionInjected` the PythonScene (and other scenes that support injected user input) can override this method, check existing registrations, and return `true` when succeeding. Also, upgraded to C++20 (g++ `c++2a`), mostly because I want to use map::contains.
This commit is contained in:
parent
d3826804a0
commit
d6446e18ea
67
JANKFILE.md
67
JANKFILE.md
|
@ -15,3 +15,70 @@
|
||||||
* ==Buttons don't know their parent...?== So there's arithmetic happening in the event loop to determine it's actual positions. Fix this with the widgets-on-widgets system (so we can go deeper, and just ask the widgets if they or their children were clicked)
|
* ==Buttons don't know their parent...?== So there's arithmetic happening in the event loop to determine it's actual positions. Fix this with the widgets-on-widgets system (so we can go deeper, and just ask the widgets if they or their children were clicked)
|
||||||
* Keep aspect ratio correct (and hopefully unbork any mouse issues) when maximizing / full screening the game
|
* Keep aspect ratio correct (and hopefully unbork any mouse issues) when maximizing / full screening the game
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# r/RoguelikeDev Does the Complete Roguelike Tutorial - July 2023
|
||||||
|
|
||||||
|
## Planning
|
||||||
|
|
||||||
|
Event ends roughly 26 August (last post will be 22 August)
|
||||||
|
|
||||||
|
* Add and remove keystroke registration from Python scripts
|
||||||
|
* Error checking: raise Python exceptions instead of null reference segfault in C++
|
||||||
|
* Proper exception handling: figure out the "any code at REPL shows the unhandled exception" bug thingy
|
||||||
|
* Extra clarity: display more status info about what Python stuff is being done
|
||||||
|
- load all files in directory at first
|
||||||
|
- list modules / classes found
|
||||||
|
- list Scenes that were found
|
||||||
|
- Read all Python modules, then call objects & methods at C++-managed events (Engine provided decorators, perhaps??)
|
||||||
|
* PythonScene version of MenuScene
|
||||||
|
- instantiate PythonScenes
|
||||||
|
* Switch Scenes from Python (edge of board / stairs / teleportation feature)
|
||||||
|
* Update the visible map from Python (fix "map blank until you move" bug)
|
||||||
|
- update "camera state" and "Recenter camera" buttons' API info as well without a whole Turn
|
||||||
|
* C++/TCOD Entity pathfinding without calling Python every Turn
|
||||||
|
* Replace jank .py files that define engine-required objects with C++ definitions inside of `mcrfpy` module
|
||||||
|
|
||||||
|
This actually a pretty big list, but there's about 7 weeks left, so it's just over one item per week.
|
||||||
|
|
||||||
|
I have no bad feelings if these items leak over to EngJam or beyond.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Notes 12 July
|
||||||
|
|
||||||
|
Some changes to make in McRFPy_API.cpp:
|
||||||
|
* create a parallel to _registerPyAction which will register a keystroke to a Python label in the current scene.
|
||||||
|
|
||||||
|
## Notes 13 July
|
||||||
|
|
||||||
|
- working on API endpoint `_registerInputAction`.
|
||||||
|
|
||||||
|
it will add "_py" as a suffix to the action string and register it along with other scene actions.
|
||||||
|
|
||||||
|
- Adding public Scene methods. These are on the base class with default of return `false`.
|
||||||
|
|
||||||
|
`bool Scene::registerActionInjected(int code, std::string name)` and `unregisterActionInjected`
|
||||||
|
|
||||||
|
the PythonScene (and other scenes that support injected user input) can override this method, check existing registrations, and return `true` when succeeding.
|
||||||
|
|
||||||
|
- then remove `McRFPy_API::player_input`
|
||||||
|
|
||||||
|
This behavior can be more flexibly implemented in Python using keyboard registration. There's some deduplication as well, since I have a Python implementation of collision detection anyway (for items, doors, and NPCs).
|
||||||
|
|
||||||
|
Also, upgraded to C++20 (g++ `c++2a`), mostly because I want to use map::contains.
|
||||||
|
|
||||||
|
More ideas:
|
||||||
|
|
||||||
|
* Need to make "pan" and "zoom" optional on each grid (for minimap type use cases)
|
||||||
|
* clicks are handled by C++ and only used on buttons. Grids could have a Python click function (with pixel & grid coords available)
|
||||||
|
* pixel-on-menu Python click function should be possible too
|
||||||
|
* Need to call a Python update function when C++ events cause camera following to change: UI is only updating after player input
|
||||||
|
|
||||||
|
|
||||||
|
Tomorrow, start with:
|
||||||
|
|
||||||
|
* implement checks in PythonScene::registerActionInjected - Save injected actions, don't let regular actions be overwritten, return success
|
||||||
|
* Remove PythonScene key definitions and McRFPy_API::player_input
|
||||||
|
* re-implement walking via keyboard input in Python
|
||||||
|
* Find a good spot for camera following to update Python immediately
|
||||||
|
* Find a good spot for grid updates to redraw TCOD line of sight immediately
|
|
@ -35,7 +35,7 @@ do
|
||||||
-I../../deps_linux \
|
-I../../deps_linux \
|
||||||
-I../../deps_linux/Python-3.11.1 \
|
-I../../deps_linux/Python-3.11.1 \
|
||||||
-I../../platform/linux \
|
-I../../platform/linux \
|
||||||
--std=c++17 \
|
--std=c++2a \
|
||||||
-c ../../src/$fn.cpp \
|
-c ../../src/$fn.cpp \
|
||||||
-o ../../obj/$fn.o \
|
-o ../../obj/$fn.o \
|
||||||
-lm \
|
-lm \
|
||||||
|
@ -53,7 +53,7 @@ done
|
||||||
|
|
||||||
# Final executable
|
# Final executable
|
||||||
g++ \
|
g++ \
|
||||||
--std=c++17 \
|
--std=c++2a \
|
||||||
-I../../deps_linux \
|
-I../../deps_linux \
|
||||||
-I../../deps_linux/Python-3.11.1 \
|
-I../../deps_linux/Python-3.11.1 \
|
||||||
-I../../platform/linux \
|
-I../../platform/linux \
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
GameEngine::GameEngine()
|
GameEngine::GameEngine()
|
||||||
{
|
{
|
||||||
font.loadFromFile("./assets/JetbrainsMono.ttf");
|
font.loadFromFile("./assets/JetbrainsMono.ttf");
|
||||||
window.create(sf::VideoMode(1024, 768), "McRogueFace - LGJ Submission by John McCardle");
|
window.create(sf::VideoMode(1024, 768), "McRogueFace - r/RoguelikeDev Tutorial Run");
|
||||||
visible = window.getDefaultView();
|
visible = window.getDefaultView();
|
||||||
window.setFramerateLimit(30);
|
window.setFramerateLimit(30);
|
||||||
scene = "menu";
|
scene = "menu";
|
||||||
|
|
|
@ -46,6 +46,9 @@ static PyMethodDef mcrfpyMethods[] = {
|
||||||
{"registerPyAction", McRFPy_API::_registerPyAction, METH_VARARGS,
|
{"registerPyAction", McRFPy_API::_registerPyAction, METH_VARARGS,
|
||||||
"Register a callable Python object to correspond to an action string. (actionstr, callable)"},
|
"Register a callable Python object to correspond to an action string. (actionstr, callable)"},
|
||||||
|
|
||||||
|
{"registerInputAction", McRFPy_API::_registerInputAction, METH_VARARGS,
|
||||||
|
"Register a SFML input code to correspond to an action string. (input_code, actionstr)"},
|
||||||
|
|
||||||
{"createGrid", McRFPy_API::_createGrid, METH_VARARGS,
|
{"createGrid", McRFPy_API::_createGrid, METH_VARARGS,
|
||||||
"create a new grid (title, grid_x, grid_y, grid_size, x, y, w, h). grid_x and grid_y are the width and height in squares. grid_size is the pixel w/h of sprites on the grid. x,y are the grid's screen position. w,h are the grid's screen size" },
|
"create a new grid (title, grid_x, grid_y, grid_size, x, y, w, h). grid_x and grid_y are the width and height in squares. grid_size is the pixel w/h of sprites on the grid. x,y are the grid's screen position. w,h are the grid's screen size" },
|
||||||
|
|
||||||
|
@ -536,6 +539,8 @@ PyObject* McRFPy_API::_registerPyAction(PyObject *self, PyObject *args)
|
||||||
PyObject* callable;
|
PyObject* callable;
|
||||||
const char * actionstr;
|
const char * actionstr;
|
||||||
if (!PyArg_ParseTuple(args, "sO", &actionstr, &callable)) return NULL;
|
if (!PyArg_ParseTuple(args, "sO", &actionstr, &callable)) return NULL;
|
||||||
|
//TODO: if the string already exists in the callbacks map,
|
||||||
|
// decrease our reference count so it can potentially be garbage collected
|
||||||
callbacks[std::string(actionstr)] = callable;
|
callbacks[std::string(actionstr)] = callable;
|
||||||
Py_INCREF(callable);
|
Py_INCREF(callable);
|
||||||
|
|
||||||
|
@ -544,11 +549,35 @@ PyObject* McRFPy_API::_registerPyAction(PyObject *self, PyObject *args)
|
||||||
return Py_None;
|
return Py_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject* McRFPy_API::_registerInputAction(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
int action_code;
|
||||||
|
const char * actionstr;
|
||||||
|
if (!PyArg_ParseTuple(args, "iz", &action_code, &actionstr)) return NULL;
|
||||||
|
|
||||||
|
bool success;
|
||||||
|
|
||||||
|
if (actionstr == NULL) { // Action provided is None, i.e. unregister
|
||||||
|
success = game->currentScene()->unregisterActionInjected(action_code, std::string(actionstr) + "_py");
|
||||||
|
} else {
|
||||||
|
success = game->currentScene()->registerActionInjected(action_code, std::string(actionstr) + "_py");
|
||||||
|
}
|
||||||
|
|
||||||
|
success ? Py_INCREF(Py_True) : Py_INCREF(Py_False);
|
||||||
|
return success ? Py_True : Py_False;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void McRFPy_API::doAction(std::string actionstr) {
|
void McRFPy_API::doAction(std::string actionstr) {
|
||||||
// hard coded actions that require no registration
|
// hard coded actions that require no registration
|
||||||
|
//std::cout << "Calling Python Action: " << actionstr;
|
||||||
if (!actionstr.compare("startrepl")) return McRFPy_API::REPL();
|
if (!actionstr.compare("startrepl")) return McRFPy_API::REPL();
|
||||||
if (callbacks.find(actionstr) == callbacks.end()) return;
|
if (callbacks.find(actionstr) == callbacks.end())
|
||||||
//std::cout << "Calling: " << PyUnicode_AsUTF8(PyObject_Repr(callbacks[actionstr])) << std::endl;
|
{
|
||||||
|
//std::cout << " (not found)" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//std::cout << " (" << PyUnicode_AsUTF8(PyObject_Repr(callbacks[actionstr])) << ")" << std::endl;
|
||||||
PyObject_Call(callbacks[actionstr], PyTuple_New(0), NULL);
|
PyObject_Call(callbacks[actionstr], PyTuple_New(0), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,7 @@ public:
|
||||||
static PyObject* _createAnimation(PyObject*, PyObject*);
|
static PyObject* _createAnimation(PyObject*, PyObject*);
|
||||||
|
|
||||||
static PyObject* _registerPyAction(PyObject*, PyObject*);
|
static PyObject* _registerPyAction(PyObject*, PyObject*);
|
||||||
|
static PyObject* _registerInputAction(PyObject*, PyObject*);
|
||||||
|
|
||||||
static PyObject* _createSoundBuffer(PyObject*, PyObject*);
|
static PyObject* _createSoundBuffer(PyObject*, PyObject*);
|
||||||
static PyObject* _loadMusic(PyObject*, PyObject*);
|
static PyObject* _loadMusic(PyObject*, PyObject*);
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
MenuScene::MenuScene(GameEngine* g) : Scene(g)
|
MenuScene::MenuScene(GameEngine* g) : Scene(g)
|
||||||
{
|
{
|
||||||
text.setFont(game->getFont());
|
text.setFont(game->getFont());
|
||||||
text.setString("McRogueFace Engine - LGJ 2023 (Incomplete)");
|
text.setString("McRogueFace Engine - r/RoguelikeDev Tutorial 2023");
|
||||||
text.setCharacterSize(24);
|
text.setCharacterSize(24);
|
||||||
//std::cout << "MenuScene Initialized. " << game << std::endl;
|
//std::cout << "MenuScene Initialized. " << game << std::endl;
|
||||||
//std::cout << "Font: " << game->getFont().getInfo().family << std::endl;
|
//std::cout << "Font: " << game->getFont().getInfo().family << std::endl;
|
||||||
|
|
|
@ -186,7 +186,10 @@ void PythonScene::doZoom(sf::Vector2i mousepos, int value) {
|
||||||
void PythonScene::doAction(std::string name, std::string type) {
|
void PythonScene::doAction(std::string name, std::string type) {
|
||||||
auto mousepos = sf::Mouse::getPosition(game->getWindow());
|
auto mousepos = sf::Mouse::getPosition(game->getWindow());
|
||||||
//std::cout << "name: " << name << ", type: " << type << std::endl;
|
//std::cout << "name: " << name << ", type: " << type << std::endl;
|
||||||
if (ACTIONONCE("click")) {
|
if (ACTIONPY) {
|
||||||
|
McRFPy_API::doAction(name);
|
||||||
|
}
|
||||||
|
else if (ACTIONONCE("click")) {
|
||||||
// left click start
|
// left click start
|
||||||
//std::cout << "LClick started at (" << mousepos.x << ", " << mousepos.y << ")" << std::endl;
|
//std::cout << "LClick started at (" << mousepos.x << ", " << mousepos.y << ")" << std::endl;
|
||||||
dragstart = mousepos;
|
dragstart = mousepos;
|
||||||
|
@ -237,6 +240,15 @@ void PythonScene::doAction(std::string name, std::string type) {
|
||||||
else if (ACTIONONCE("wait")) { McRFPy_API::player_input(+0, +0); }
|
else if (ACTIONONCE("wait")) { McRFPy_API::player_input(+0, +0); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PythonScene::registerActionInjected(int code, std::string name) {
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PythonScene::unregisterActionInjected(int code, std::string name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void PythonScene::sRender() {
|
void PythonScene::sRender() {
|
||||||
game->getWindow().clear();
|
game->getWindow().clear();
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,13 @@ class PythonScene: public Scene
|
||||||
void doZoom(sf::Vector2i, int);
|
void doZoom(sf::Vector2i, int);
|
||||||
//std::list<Animation*> animations;
|
//std::list<Animation*> animations;
|
||||||
void animate();
|
void animate();
|
||||||
|
std::map<std::string, bool> actionInjected;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PythonScene(GameEngine*, std::string);
|
PythonScene(GameEngine*, std::string);
|
||||||
void update() override final;
|
void update() override final;
|
||||||
void doAction(std::string, std::string) override final;
|
void doAction(std::string, std::string) override final;
|
||||||
void sRender() override final;
|
void sRender() override final;
|
||||||
|
bool registerActionInjected(int, std::string) override final;
|
||||||
|
bool unregisterActionInjected(int, std::string) override final;
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,3 +23,13 @@ std::string Scene::action(int code)
|
||||||
{
|
{
|
||||||
return actions[code];
|
return actions[code];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Scene::registerActionInjected(int code, std::string name)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Scene::unregisterActionInjected(int code, std::string name)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
#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))
|
#define ACTIONAFTER(X) ((name.compare(X) == 0 && type.compare("end") == 0))
|
||||||
|
#define ACTIONPY ((name.size() > 3 && name.compare(name.size() - 3, 3, "_py") == 0))
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
//#include "GameEngine.h"
|
//#include "GameEngine.h"
|
||||||
|
@ -33,4 +34,7 @@ public:
|
||||||
bool hasAction(int);
|
bool hasAction(int);
|
||||||
std::string action(int);
|
std::string action(int);
|
||||||
|
|
||||||
|
virtual bool registerActionInjected(int, std::string);
|
||||||
|
virtual bool unregisterActionInjected(int, std::string);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue