diff --git a/binding/binding-mri.cpp b/binding/binding-mri.cpp index 09d92c0..3b9d3cf 100644 --- a/binding/binding-mri.cpp +++ b/binding/binding-mri.cpp @@ -104,6 +104,7 @@ void CUSLBindingInit(); void httpBindingInit(); +RB_METHOD(mkxpDelta); RB_METHOD(mriPrint); RB_METHOD(mriP); RB_METHOD(mkxpDataDirectory); @@ -188,6 +189,7 @@ static void mriBindingInit() { assert(!"unreachable"); VALUE mod = rb_define_module("System"); + _rb_define_module_function(mod, "delta", mkxpDelta); _rb_define_module_function(mod, "data_directory", mkxpDataDirectory); _rb_define_module_function(mod, "set_window_title", mkxpSetTitle); _rb_define_module_function(mod, "show_settings", mkxpSettingsMenu); @@ -244,6 +246,7 @@ static void printP(int argc, VALUE *argv, const char *convMethod, showMsg(RSTRING_PTR(dispString)); } + RB_METHOD(mriPrint) { RB_UNUSED_PARAM; @@ -260,6 +263,12 @@ RB_METHOD(mriP) { return Qnil; } +RB_METHOD(mkxpDelta) { + RB_UNUSED_PARAM; + + return ULL2NUM(shState->runTime()); +} + RB_METHOD(mkxpDataDirectory) { RB_UNUSED_PARAM; diff --git a/binding/graphics-binding.cpp b/binding/graphics-binding.cpp index 91bb7eb..2fab797 100644 --- a/binding/graphics-binding.cpp +++ b/binding/graphics-binding.cpp @@ -25,6 +25,12 @@ #include "binding-types.h" #include "exception.h" +RB_METHOD(graphicsDelta) { + RB_UNUSED_PARAM; + + return ULL2NUM(shState->graphics().getDelta()); +} + RB_METHOD(graphicsUpdate) { RB_UNUSED_PARAM; @@ -255,6 +261,7 @@ void graphicsBindingInit() { VALUE module = rb_define_module("Graphics"); + _rb_define_module_function(module, "delta", graphicsDelta); _rb_define_module_function(module, "update", graphicsUpdate); _rb_define_module_function(module, "freeze", graphicsFreeze); _rb_define_module_function(module, "transition", graphicsTransition); diff --git a/binding/input-binding.cpp b/binding/input-binding.cpp index bc650cb..0b18297 100644 --- a/binding/input-binding.cpp +++ b/binding/input-binding.cpp @@ -28,6 +28,12 @@ #include #include +RB_METHOD(inputDelta) { + RB_UNUSED_PARAM; + + return ULL2NUM(shState->input().getDelta()); +} + RB_METHOD(inputUpdate) { RB_UNUSED_PARAM; @@ -138,6 +144,19 @@ RB_METHOD(inputCount) { return UINT2NUM(shState->input().count(num)); } +RB_METHOD(inputRepeatTime) { + RB_UNUSED_PARAM; + + rb_check_argc(argc, 1); + + VALUE button; + rb_scan_args(argc, argv, "1", &button); + + int num = getButtonArg(&button); + + return ULL2NUM(shState->input().repeatTime(num)); +} + RB_METHOD(inputPressEx) { RB_UNUSED_PARAM; @@ -208,6 +227,20 @@ RB_METHOD(inputCountEx) { return UINT2NUM(shState->input().repeatcount(NUM2INT(button), 1)); } +RB_METHOD(inputRepeatTimeEx) { + RB_UNUSED_PARAM; + + VALUE button; + rb_scan_args(argc, argv, "1", &button); + + if (SYMBOL_P(button)) { + int num = getScancodeArg(&button); + return ULL2NUM(shState->input().repeatTimeEx(num, 0)); + } + + return ULL2NUM(shState->input().repeatTimeEx(NUM2INT(button), 1)); +} + RB_METHOD(inputDir4) { RB_UNUSED_PARAM; @@ -357,17 +390,20 @@ static elementsN(buttonCodes); void inputBindingInit() { VALUE module = rb_define_module("Input"); + _rb_define_module_function(module, "delta", inputDelta); _rb_define_module_function(module, "update", inputUpdate); _rb_define_module_function(module, "press?", inputPress); _rb_define_module_function(module, "trigger?", inputTrigger); _rb_define_module_function(module, "repeat?", inputRepeat); _rb_define_module_function(module, "release?", inputRelease); _rb_define_module_function(module, "count", inputCount); + _rb_define_module_function(module, "time?", inputRepeatTime); _rb_define_module_function(module, "pressex?", inputPressEx); _rb_define_module_function(module, "triggerex?", inputTriggerEx); _rb_define_module_function(module, "repeatex?", inputRepeatEx); _rb_define_module_function(module, "releaseex?", inputReleaseEx); _rb_define_module_function(module, "repeatcount", inputCountEx); + _rb_define_module_function(module, "timeex?", inputRepeatTimeEx); _rb_define_module_function(module, "dir4", inputDir4); _rb_define_module_function(module, "dir8", inputDir8); diff --git a/src/display/graphics.cpp b/src/display/graphics.cpp index 7901c92..e6926fc 100644 --- a/src/display/graphics.cpp +++ b/src/display/graphics.cpp @@ -421,6 +421,9 @@ struct GraphicsPrivate { int frameCount; int brightness; + unsigned long long last_update; + + FPSLimiter fpsLimiter; // Can be set from Ruby. Takes priority over config setting. @@ -440,7 +443,7 @@ struct GraphicsPrivate { screen(scRes.x, scRes.y), threadData(rtData), glCtx(SDL_GL_GetCurrentContext()), frameRate(DEF_FRAMERATE), frameCount(0), brightness(255), fpsLimiter(frameRate), - useFrameSkip(rtData->config.frameSkip), frozen(false) { + useFrameSkip(rtData->config.frameSkip), frozen(false), last_update() { recalculateScreenSize(rtData); updateScreenResoRatio(rtData); @@ -584,6 +587,10 @@ Graphics::Graphics(RGSSThreadData *data) { Graphics::~Graphics() { delete p; } +unsigned long long Graphics::getDelta() { + return shState->runTime() - p->last_update; +} + void Graphics::update() { p->checkShutDownReset(); p->checkSyncLock(); @@ -612,6 +619,7 @@ void Graphics::update() { p->checkResize(); p->redrawScreen(); + p->last_update = shState->runTime(); } void Graphics::freeze() { diff --git a/src/display/graphics.h b/src/display/graphics.h index 8c9b8ed..f225996 100644 --- a/src/display/graphics.h +++ b/src/display/graphics.h @@ -34,6 +34,8 @@ struct AtomicFlag; class Graphics { public: + unsigned long long getDelta(); + void update(); void freeze(); void transition(int duration = 8, diff --git a/src/input/input.cpp b/src/input/input.cpp index 2869dda..28ad848 100644 --- a/src/input/input.cpp +++ b/src/input/input.cpp @@ -680,9 +680,13 @@ struct InputPrivate int rawRepeating; unsigned int repeatCount; unsigned int rawRepeatCount; + unsigned long long repeatTime; + unsigned long long rawRepeatTime; unsigned int repeatStart; unsigned int repeatDelay; + + unsigned long long last_update; struct { @@ -708,6 +712,8 @@ struct InputPrivate InputPrivate(const RGSSThreadData &rtData) { + last_update = 0; + initStaticKbBindings(); initMsBindings(); @@ -1005,12 +1011,15 @@ struct InputPrivate else { rawRepeatCount = 0; + rawRepeatTime = shState->runTime(); rawRepeating = i; } - break; + return; } } + + rawRepeating = -1; } void updateDir4() @@ -1101,6 +1110,10 @@ Input::Input(const RGSSThreadData &rtData) p = new InputPrivate(rtData); } +unsigned long long Input::getDelta() { + return shState->runTime() - p->last_update; +} + void Input::recalcRepeat(unsigned int fps) { p->recalcRepeatTime(fps); } @@ -1126,8 +1139,10 @@ void Input::update() { p->repeating = repeatCand; p->repeatCount = 0; + p->repeatTime = shState->runTime(); p->getState(repeatCand).repeated = true; + p->last_update = p->repeatTime; return; } @@ -1146,10 +1161,12 @@ void Input::update() bool repeated = p->repeatCount >= p->repeatStart && ((p->repeatCount+1) & p->repeatDelay) == 0; p->getState(p->repeating).repeated |= repeated; + p->last_update = shState->runTime(); return; } - + p->repeating = None; + p->last_update = shState->runTime(); } std::vector Input::getBindings(ButtonCode code) { @@ -1203,6 +1220,13 @@ unsigned int Input::count(int button) { return p->repeatCount; } +unsigned long long Input::repeatTime(int button) { + if (button != p->repeating) + return 0; + + return shState->runTime() - p->repeatTime; +} + bool Input::isPressedEx(int code, bool isVKey) { return p->getStateRaw(code, isVKey).pressed; @@ -1239,6 +1263,19 @@ unsigned int Input::repeatcount(int code, bool isVKey) { return p->rawRepeatCount; } +unsigned long long Input::repeatTimeEx(int code, bool isVKey) { + int c = code; + if (isVKey) { + try { c = vKeyToScancode[code]; } + catch (...) { return 0; } + } + + if (c != p->rawRepeating) + return 0; + + return shState->runTime() - p->rawRepeatTime; +} + int Input::dir4Value() { return p->dir4Data.active; diff --git a/src/input/input.h b/src/input/input.h index 52a5be6..2cb7e5c 100644 --- a/src/input/input.h +++ b/src/input/input.h @@ -55,6 +55,7 @@ public: void recalcRepeat(unsigned int fps); + unsigned long long getDelta(); void update(); std::vector getBindings(ButtonCode code); @@ -64,11 +65,13 @@ public: bool isRepeated(int button); bool isReleased(int button); unsigned int count(int button); + unsigned long long repeatTime(int button); bool isPressedEx(int code, bool isVKey); bool isTriggeredEx(int code, bool isVKey); bool isRepeatedEx(int code, bool isVKey); bool isReleasedEx(int code, bool isVKey); unsigned int repeatcount(int code, bool isVKey); + unsigned long long repeatTimeEx(int code, bool isVKey); int dir4Value(); int dir8Value(); diff --git a/src/sharedstate.cpp b/src/sharedstate.cpp index 489ab63..305fd60 100644 --- a/src/sharedstate.cpp +++ b/src/sharedstate.cpp @@ -41,6 +41,7 @@ #include #include #include +#include SharedState *SharedState::instance = 0; int SharedState::rgssVersion = 0; @@ -97,6 +98,8 @@ struct SharedStatePrivate Quad gpQuad; unsigned int stampCounter; + + std::chrono::time_point startupTime; SharedStatePrivate(RGSSThreadData *threadData) : bindingData(0), @@ -113,6 +116,9 @@ struct SharedStatePrivate fontState(threadData->config), stampCounter(0) { + + startupTime = std::chrono::high_resolution_clock::now(); + /* Shaders have been compiled in ShaderSet's constructor */ if (gl.ReleaseShaderCompiler) gl.ReleaseShaderCompiler(); @@ -357,6 +363,12 @@ Font &SharedState::defaultFont() const return *p->defaultFont; } +unsigned long long SharedState::runTime() { + if (!p) return 0; + const auto now = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast(now - p->startupTime).count(); +} + unsigned int SharedState::genTimeStamp() { return p->stampCounter++; diff --git a/src/sharedstate.h b/src/sharedstate.h index 32c71d0..b7bca70 100644 --- a/src/sharedstate.h +++ b/src/sharedstate.h @@ -84,6 +84,9 @@ struct SharedState sigc::signal prepareDraw; unsigned int genTimeStamp(); + + // Returns time since SharedState was constructed in microseconds + unsigned long long runTime(); /* Returns global quad IBO, and ensures it has indices * for at least minSize quads */