diff --git a/src/GameEngine.cpp b/src/GameEngine.cpp index 4f02b21..dfc202a 100644 --- a/src/GameEngine.cpp +++ b/src/GameEngine.cpp @@ -37,6 +37,7 @@ GameEngine::GameEngine() IndexSprite::game = this; clock.restart(); + runtime.restart(); } Scene* GameEngine::currentScene() { return scenes[scene]; } @@ -55,6 +56,7 @@ void GameEngine::run() while (running) { currentScene()->update(); + testTimers(); sUserInput(); if (!paused) { @@ -67,6 +69,36 @@ void GameEngine::run() } } +void GameEngine::manageTimer(std::string name, PyObject* target, int interval) +{ + //std::cout << "Manage timer called. " << name << " " << interval << std::endl; + if (timers.find(name) != timers.end()) // overwrite existing + { + if (target == NULL || target == Py_None) // delete + { + Py_DECREF(target); + timers.erase(name); + return; + } + } + if (target == NULL || target == Py_None) + { + std::cout << "Refusing to initialize timer to None. It's not an error, it's just pointless." << std::endl; + return; + } + timers[name] = Timer(target, interval, runtime.getElapsedTime().asMilliseconds()); + Py_INCREF(target); +} + +void GameEngine::testTimers() +{ + int now = runtime.getElapsedTime().asMilliseconds(); + for (auto& [name, timer]: timers) + { + timer.test(now); + } +} + void GameEngine::sUserInput() { sf::Event event; diff --git a/src/GameEngine.h b/src/GameEngine.h index 2448b6b..d24f231 100644 --- a/src/GameEngine.h +++ b/src/GameEngine.h @@ -6,6 +6,7 @@ #include "Scene.h" #include "McRFPy_API.h" #include "IndexTexture.h" +#include "Timer.h" class GameEngine { @@ -20,6 +21,10 @@ class GameEngine float frameTime; std::string window_title; + sf::Clock runtime; + std::map timers; + void testTimers(); + public: std::string scene; GameEngine(); @@ -35,6 +40,7 @@ public: int getFrame() { return currentFrame; } float getFrameTime() { return frameTime; } sf::View getView() { return visible; } + void manageTimer(std::string, PyObject*, int); // global textures for scripts to access std::vector textures; diff --git a/src/McRFPy_API.cpp b/src/McRFPy_API.cpp index 0df0ce2..042f22e 100644 --- a/src/McRFPy_API.cpp +++ b/src/McRFPy_API.cpp @@ -32,6 +32,9 @@ static PyMethodDef mcrfpyMethods[] = { {"createScene", McRFPy_API::_createScene, METH_VARARGS, "createScene(scene) - create a new blank scene with given name"}, {"keypressScene", McRFPy_API::_keypressScene, METH_VARARGS, "keypressScene(callable) - assign a callable object to the current scene receive keypress events"}, + {"setTimer", McRFPy_API::_setTimer, METH_VARARGS, "setTimer(name:str, callable:object, interval:int) - callable will be called with args (runtime:float) every `interval` milliseconds"}, + {"delTimer", McRFPy_API::_delTimer, METH_VARARGS, "delTimer(name:str) - stop calling the timer labelled with `name`"}, + {NULL, NULL, 0, NULL} }; @@ -450,3 +453,21 @@ PyObject* McRFPy_API::_keypressScene(PyObject* self, PyObject* args) { Py_INCREF(Py_None); return Py_None; } + +PyObject* McRFPy_API::_setTimer(PyObject* self, PyObject* args) { // TODO - compare with UIDrawable mouse & Scene Keyboard methods - inconsistent responsibility for incref/decref around mcrogueface + const char* name; + PyObject* callable; + int interval; + if (!PyArg_ParseTuple(args, "sOi", &name, &callable, &interval)) return NULL; + game->manageTimer(name, callable, interval); + Py_INCREF(Py_None); + return Py_None; +} + +PyObject* McRFPy_API::_delTimer(PyObject* self, PyObject* args) { + const char* name; + if (!PyArg_ParseTuple(args, "s", &name)) return NULL; + game->manageTimer(name, NULL, 0); + Py_INCREF(Py_None); + return Py_None; +} diff --git a/src/McRFPy_API.h b/src/McRFPy_API.h index 2935d33..52730c8 100644 --- a/src/McRFPy_API.h +++ b/src/McRFPy_API.h @@ -74,6 +74,11 @@ public: static PyObject* _currentScene(PyObject*, PyObject*); static PyObject* _createScene(PyObject*, PyObject*); static PyObject* _keypressScene(PyObject*, PyObject*); + + // timer control + static PyObject* _setTimer(PyObject*, PyObject*); + static PyObject* _delTimer(PyObject*, PyObject*); + // accept keyboard input from scene static sf::Vector2i cursor_position; static void player_input(int, int); diff --git a/src/Timer.cpp b/src/Timer.cpp new file mode 100644 index 0000000..c04ccf4 --- /dev/null +++ b/src/Timer.cpp @@ -0,0 +1,37 @@ +#include "Timer.h" + +Timer::Timer(PyObject* _target, int _interval, int now) +: target(_target), interval(_interval), last_ran(now) +{ + //Py_INCREF(target); +} + +Timer::Timer() +: target(Py_None), interval(0), last_ran(0) +{} + +Timer::Timer(Timer& other) +: target(other.target), interval(other.interval), last_ran(other.last_ran) +{ + //Py_INCREF(target); +} + +Timer::~Timer() +{ + //if (target && target != Py_None) + // Py_DECREF(target); +} + +bool Timer::test(int now) +{ + if (!target || target == Py_None) return false; + if (now > last_ran + interval) + { + last_ran = now; + PyObject* args = Py_BuildValue("(i)", now); + std::cout << PyUnicode_AsUTF8(PyObject_Repr(args)) << std::endl; + PyObject_Call(target, args, NULL); + return true; + } + return false; +} diff --git a/src/Timer.h b/src/Timer.h new file mode 100644 index 0000000..0e01344 --- /dev/null +++ b/src/Timer.h @@ -0,0 +1,17 @@ +#pragma once +#include "Common.h" +#include "Python.h" +class GameEngine; // forward declare + +class Timer +{ +public: + PyObject* target; + int interval; + int last_ran; + Timer(); // for map to build + Timer(Timer& other); // copy constructor + Timer(PyObject*, int, int); + ~Timer(); + bool test(int); +};