From abb7f9e8f9a982fc012c8aad155dc2d34bad8488 Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:46:07 -0300 Subject: [PATCH 1/5] NewCommand 2052 - AnimateVariable @raw 2052, "linear/bounceOut", 0, 1, 0, 0, 0, 240, 0, 640, 0, 0 ` CommandInterpolateVariable("easeTypeStart/easeTypeEnd",[targetIsVar, target, StartIsVar, start, endIsVar, end, durationIsVar, duration, pauseIsVar, pause]) ` cubic bezier is broken. There are some libraries about it, I'll see what to do. I'll need help with this one. It's almost fully working. --- src/game_interpreter.cpp | 333 +++++++++++++++++++++++++++++++++++++++ src/game_interpreter.h | 1 + 2 files changed, 334 insertions(+) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 98b9edef57..aa4054ecd5 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -786,6 +786,8 @@ bool Game_Interpreter::ExecuteCommand(lcf::rpg::EventCommand const& com) { return CommandManiacControlStrings(com); case Cmd::Maniac_CallCommand: return CommandManiacCallCommand(com); + case Cmd::EasyRpg_AnimateVariable: + return CommandEasyRpgAnimateVariable(com); case Cmd::EasyRpg_SetInterpreterFlag: return CommandEasyRpgSetInterpreterFlag(com); case static_cast(2056): //EasyRPG_CloneMapEvent @@ -5096,3 +5098,334 @@ int Game_Interpreter::ManiacBitmask(int value, int mask) const { return value; } +std::vector parseBezier(const std::string& bezierParams) { + std::vector params; + std::string temp; + size_t startPos = bezierParams.find("(") + 1; + size_t endPos = bezierParams.find(")"); + std::string valuesString = bezierParams.substr(startPos, endPos - startPos); + + size_t commaPos = valuesString.find(","); + while (commaPos != std::string::npos) { + temp = valuesString.substr(0, commaPos); + params.push_back(std::stod(temp)); + valuesString = valuesString.substr(commaPos + 1); + commaPos = valuesString.find(","); + } + // Push the last value into the vector + params.push_back(std::stod(valuesString)); + + return params; +} + +//FIXME: cubicBezier is completely Broken +// references to how it should work: +// https://matthewlein.com/tools/ceaser +// https://cubic-bezier.com/ + +double cubicBezier(double t, double p0, double p1, double p2, double p3) { + double u = 1 - t; + double tt = t * t; + double uu = u * u; + double uuu = uu * u; + double ttt = tt * t; + + double p = uuu * p0; // (1-t)^3 + double q = 3 * uu * t * p1; // 3t(1-t)^2 + double r = 3 * u * tt * p2; // 3(1-t)t^2 + double s = ttt * p3; // t^3 + + return p + q + r + s; +} + +double getEasedT(const std::string& easingType, double t, double b, double c, double d) { + if (easingType == "linear") { + return c * t / d + b; + } + else if (easingType == "quadIn") { + t /= d; + return c * t * t + b; + } + else if (easingType == "quadOut") { + t /= d; + return -c * t * (t - 2) + b; + } + else if (easingType == "quadInOut") { + t /= d / 2; + if (t < 1) { + return c / 2 * t * t + b; + } + else { + t -= 1; + return -c / 2 * (t * (t - 2) - 1) + b; + } + } + else if (easingType == "cubicIn") { + t /= d; + return c * t * t * t + b; + } + else if (easingType == "cubicOut") { + t = (t / d) - 1; + return c * (t * t * t + 1) + b; + } + else if (easingType == "cubicInOut") { + t /= d / 2; + if (t < 1) { + return c / 2 * t * t * t + b; + } + else { + t -= 2; + return c / 2 * (t * t * t + 2) + b; + } + } + else if (easingType == "sinIn") { + return -c * cos(t / d * (M_PI / 2)) + c + b; + } + else if (easingType == "sinOut") { + return c * sin(t / d * (M_PI / 2)) + b; + } + else if (easingType == "sinInOut") { + return -c / 2 * (cos(M_PI * t / d) - 1) + b; + } + else if (easingType == "expoIn") { + return c * pow(2, 10 * (t / d - 1)) + b; + } + else if (easingType == "expoOut") { + return c * (-pow(2, -10 * t / d) + 1) + b; + } + else if (easingType == "expoInOut") { + t /= d / 2; + if (t < 1) { + return c / 2 * pow(2, 10 * (t - 1)) + b; + } + else { + t -= 1; + return c / 2 * (-pow(2, -10 * t) + 2) + b; + } + } + else if (easingType == "circIn") { + t /= d; + return -c * (sqrt(1 - t * t) - 1) + b; + } + else if (easingType == "circOut") { + t = (t / d) - 1; + return c * sqrt(1 - t * t) + b; + } + else if (easingType == "circInOut") { + t /= d / 2; + if (t < 1) { + return -c / 2 * (sqrt(1 - t * t) - 1) + b; + } + else { + t -= 2; + return c / 2 * (sqrt(1 - t * t) + 1) + b; + } + } + else if (easingType == "elasticIn") { + if (t == 0) { + return b; + } + if ((t /= d) == 1) { + return b + c; + } + + double p = d * 0.3; + double a = c; + double s = p / 4; + + double postFix = a * pow(2, 10 * (t -= 1)); // this is a fix, again, with post-increment operators + return -(postFix * sin((t * d - s) * (2 * M_PI) / p)) + b; + } + else if (easingType == "elasticOut") { + if (t == 0) { + return b; + } + if ((t /= d) == 1) { + return b + c; + } + + double p = d * 0.3; + double a = c; + double s = p / 4; + + return (a * pow(2, -10 * t) * sin((t * d - s) * (2 * M_PI) / p) + c + b); + } + else if (easingType == "elasticInOut") { + if (t == 0) { + return b; + } + if ((t /= d / 2) == 2) { + return b + c; + } + + double p = d * (0.3 * 1.5); + double a = c; + double s = p / 4; + + if (t < 1) { + double postFix = a * pow(2, 10 * (t -= 1)); // this is a fix, again, with post-increment operators + return -0.5 * (postFix * sin((t * d - s) * (2 * M_PI) / p)) + b; + } + + double postFix = a * pow(2, -10 * (t -= 1)); // this is a fix, again, with post-increment operators + return postFix * sin((t * d - s) * (2 * M_PI) / p) * 0.5 + c + b; + } + else if (easingType == "bounceIn") { + return c - getEasedT("bounceOut", d - t, 0, c, d) + b; + } + else if (easingType == "bounceOut") { + if ((t /= d) < (1 / 2.75)) { + return c * (7.5625 * t * t) + b; + } + else if (t < (2 / 2.75)) { + t -= (1.5 / 2.75); + return c * (7.5625 * t * t + 0.75) + b; + } + else if (t < (2.5 / 2.75)) { + t -= (2.25 / 2.75); + return c * (7.5625 * t * t + 0.9375) + b; + } + else { + t -= (2.625 / 2.75); + return c * (7.5625 * t * t + 0.984375) + b; + } + } + else if (easingType == "bounceInOut") { + if (t < d / 2) { + return getEasedT("bounceIn", t * 2, 0, c, d) * 0.5 + b; + } + else { + return getEasedT("bounceOut", t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b; + } + } + if (easingType.substr(0, 6) == "bezier") { + std::vector < double > bezierParams = parseBezier(easingType.substr(7)); + if (bezierParams.size() == 4) { + return cubicBezier(t / d, bezierParams[0], bezierParams[1], bezierParams[2], bezierParams[3]); + } + } + + return c * t / d + b; // Default to linear easing if the easing type is not recognized +} + +std::vector interpolate(double start, double end, double duration, const std::string& easingTypeAtStart, const std::string& easingTypeAtEnd) { + std::vector interpolatedValues; + interpolatedValues.push_back(start); + + // Calculate the number of steps based on the duration + int numSteps = static_cast(duration); // Convert duration to an integer + double stepSize = 1.0 / numSteps; + + // Calculate the halfway point + double halfway = start + (end - start) * 0.5; + + if (easingTypeAtEnd == "null") { + // Use easingTypeAtStart for the entire animation + for (int step = 1; step <= numSteps; ++step) { + double t = step * stepSize; + double easedT = getEasedT(easingTypeAtStart, t, 0, 1, 1); // Call getEasedT with appropriate parameters + double interpolatedValue = start + easedT * (end - start); + interpolatedValues.push_back(interpolatedValue); + } + } + else { + // Generate the first half of the interpolation + for (int step = 1; step <= numSteps / 2; ++step) { + double t = step * stepSize; + double normalizedT = t / 0.5; // Normalize the time for the first half + double easedT = getEasedT(easingTypeAtStart, normalizedT, 0, 1, 1); // Call getEasedT with appropriate parameters + double interpolatedValue = start + easedT * (halfway - start); + interpolatedValues.push_back(interpolatedValue); + } + + // Generate the second half of the interpolation + for (int step = numSteps / 2 + 1; step <= numSteps; ++step) { + double t = step * stepSize; + double normalizedT = (t - 0.5) / 0.5; // Normalize the time for the second half + double easedT = getEasedT(easingTypeAtEnd, normalizedT, 0, 1, 1); // Call getEasedT with appropriate parameters + double interpolatedValue = halfway + easedT * (end - halfway); + interpolatedValues.push_back(interpolatedValue); + } + } + + interpolatedValues.push_back(end); + + return interpolatedValues; +} + +bool Game_Interpreter::CommandEasyRpgAnimateVariable(lcf::rpg::EventCommand const& com) { + // CommandInterpolateVariable("typeStart/typeEnd",[useVarTarget, target, useVarStart, start, useVarEnd, end, useVarDuration, duration]) + + auto* frame = GetFramePtr(); + const auto& list = frame->commands; + auto& index = frame->current_command; + + int i = frame->current_command + 1; + + // Extract parameters: target, start, end, and duration for the animation + int32_t target = ValueOrVariable(com.parameters[0], com.parameters[1]); + int32_t start = ValueOrVariable(com.parameters[2], com.parameters[3]); + int32_t end = ValueOrVariable(com.parameters[4], com.parameters[5]); + int32_t duration = ValueOrVariable(com.parameters[6], com.parameters[7]); + + // Prepare animation-related commands + lcf::rpg::EventCommand waitCom; + waitCom.code = int(Cmd::Wait); + + lcf::rpg::EventCommand updateVarCom; + updateVarCom.code = int(Cmd::ControlVars); + std::vector updateVarParams = { 0, static_cast(target), 0, 0, 0, static_cast(end) }; + updateVarCom.parameters = lcf::DBArray(updateVarParams.begin(), updateVarParams.end()); + + lcf::rpg::EventCommand branchCom; + branchCom.code = int(Cmd::ShowChoiceOption); + + // Extract easing information + std::string easeStart = ToString(com.string); + std::string easeEnd = "null"; + + std::size_t pos = easeStart.find('/'); + + if (pos != std::string::npos) { + easeEnd = easeStart.substr(pos + 1); + easeStart = easeStart.substr(0, pos); + } + + // Check if new commands don't exist in the timeline yet + if (!(i < frame->commands.size() && frame->commands.at(i).code == int(Cmd::ShowChoiceOption))) { + // Insert animation commands + Output::Debug("inserting animation commands"); + std::vector interpolatedValues = interpolate(start, end, duration, easeStart, easeEnd); + + // Insert ShowChoiceOption command + // This helps me isolating all the "keyframes" commands inside a nested commands, it also helps to avoid creating a repeated list. + // It's problematic when "start", "end" and "duration" are variables. + frame->commands.insert(frame->commands.begin() + i, branchCom); + i++; + + // Insert updateVarCom and waitCom commands for each interpolated value + for (int value : interpolatedValues) { + updateVarParams.back() = value; + updateVarCom.parameters = lcf::DBArray(updateVarParams.begin(), updateVarParams.end()); + updateVarCom.indent = com.indent + 1; + + frame->commands.insert(frame->commands.begin() + i, updateVarCom); + i++; + frame->commands.insert(frame->commands.begin() + i, waitCom); + i++; + } + + // Insert ShowChoiceEnd command + branchCom.code = int(Cmd::ShowChoiceEnd); + frame->commands.insert(frame->commands.begin() + i, branchCom); + i++; + } + else { + Output::Debug("Animated Commands Already Exists"); + } + + // Update current_command index and return true to indicate success + frame->current_command = index + 2; + return false; +} + diff --git a/src/game_interpreter.h b/src/game_interpreter.h index 7149d59ca2..9f94f04d2a 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -297,6 +297,7 @@ class Game_Interpreter : public Game_BaseInterpreterContext bool CommandManiacSetGameOption(lcf::rpg::EventCommand const& com); bool CommandManiacControlStrings(lcf::rpg::EventCommand const& com); bool CommandManiacCallCommand(lcf::rpg::EventCommand const& com); + bool CommandEasyRpgAnimateVariable(lcf::rpg::EventCommand const& com); bool CommandEasyRpgSetInterpreterFlag(lcf::rpg::EventCommand const& com); bool CommandEasyRpgCloneMapEvent(lcf::rpg::EventCommand const& com); bool CommandEasyRpgDestroyMapEvent(lcf::rpg::EventCommand const& com); From 58fc18cafedc38d05033ef9ba1c3e48c1722b628 Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Fri, 24 Nov 2023 20:32:41 -0300 Subject: [PATCH 2/5] AnimateVars - Refactor to use Interpreter's Push( ) command --- src/game_interpreter.cpp | 62 +++++++++++----------------------------- 1 file changed, 17 insertions(+), 45 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index aa4054ecd5..8f1eaa3d8f 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -5354,15 +5354,8 @@ std::vector interpolate(double start, double end, double duration, const } bool Game_Interpreter::CommandEasyRpgAnimateVariable(lcf::rpg::EventCommand const& com) { - // CommandInterpolateVariable("typeStart/typeEnd",[useVarTarget, target, useVarStart, start, useVarEnd, end, useVarDuration, duration]) + // $InterpolateVariable("typeStart/typeEnd",[useVarTarget, target, useVarStart, start, useVarEnd, end, useVarDuration, duration]) - auto* frame = GetFramePtr(); - const auto& list = frame->commands; - auto& index = frame->current_command; - - int i = frame->current_command + 1; - - // Extract parameters: target, start, end, and duration for the animation int32_t target = ValueOrVariable(com.parameters[0], com.parameters[1]); int32_t start = ValueOrVariable(com.parameters[2], com.parameters[3]); int32_t end = ValueOrVariable(com.parameters[4], com.parameters[5]); @@ -5372,13 +5365,12 @@ bool Game_Interpreter::CommandEasyRpgAnimateVariable(lcf::rpg::EventCommand cons lcf::rpg::EventCommand waitCom; waitCom.code = int(Cmd::Wait); - lcf::rpg::EventCommand updateVarCom; - updateVarCom.code = int(Cmd::ControlVars); - std::vector updateVarParams = { 0, static_cast(target), 0, 0, 0, static_cast(end) }; - updateVarCom.parameters = lcf::DBArray(updateVarParams.begin(), updateVarParams.end()); + lcf::rpg::EventCommand animatedCom; + animatedCom.code = int(Cmd::ControlVars); + std::vector animatedVarParams = { 0, static_cast(target), 0, 0, 0, static_cast(end) }; + animatedCom.parameters = lcf::DBArray(animatedVarParams.begin(), animatedVarParams.end()); - lcf::rpg::EventCommand branchCom; - branchCom.code = int(Cmd::ShowChoiceOption); + std::vector cmdList; // Extract easing information std::string easeStart = ToString(com.string); @@ -5391,41 +5383,21 @@ bool Game_Interpreter::CommandEasyRpgAnimateVariable(lcf::rpg::EventCommand cons easeStart = easeStart.substr(0, pos); } - // Check if new commands don't exist in the timeline yet - if (!(i < frame->commands.size() && frame->commands.at(i).code == int(Cmd::ShowChoiceOption))) { - // Insert animation commands - Output::Debug("inserting animation commands"); - std::vector interpolatedValues = interpolate(start, end, duration, easeStart, easeEnd); - - // Insert ShowChoiceOption command - // This helps me isolating all the "keyframes" commands inside a nested commands, it also helps to avoid creating a repeated list. - // It's problematic when "start", "end" and "duration" are variables. - frame->commands.insert(frame->commands.begin() + i, branchCom); - i++; - - // Insert updateVarCom and waitCom commands for each interpolated value - for (int value : interpolatedValues) { - updateVarParams.back() = value; - updateVarCom.parameters = lcf::DBArray(updateVarParams.begin(), updateVarParams.end()); - updateVarCom.indent = com.indent + 1; + // Insert animation commands + std::vector interpolatedValues = interpolate(start, end, duration, easeStart, easeEnd); - frame->commands.insert(frame->commands.begin() + i, updateVarCom); - i++; - frame->commands.insert(frame->commands.begin() + i, waitCom); - i++; - } + // Insert animatedCom and waitCom commands for each interpolated value + for (int value : interpolatedValues) { + animatedVarParams.back() = value; + animatedCom.parameters = lcf::DBArray(animatedVarParams.begin(), animatedVarParams.end()); + animatedCom.indent = com.indent + 1; - // Insert ShowChoiceEnd command - branchCom.code = int(Cmd::ShowChoiceEnd); - frame->commands.insert(frame->commands.begin() + i, branchCom); - i++; - } - else { - Output::Debug("Animated Commands Already Exists"); + cmdList.push_back(animatedCom); + cmdList.push_back(waitCom); } // Update current_command index and return true to indicate success - frame->current_command = index + 2; - return false; + Push(cmdList, 0, false); + return true; } From ab9569bbebd6e74beadc161df57bad4b74028161 Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Sat, 25 Nov 2023 05:07:04 -0300 Subject: [PATCH 3/5] AnimateVars - Major Refactor, everything works! Update game_interpreter.cpp --- src/game_interpreter.cpp | 287 +++++++++++++++------------------------ 1 file changed, 107 insertions(+), 180 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 8f1eaa3d8f..bc91bac639 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -5098,129 +5098,65 @@ int Game_Interpreter::ManiacBitmask(int value, int mask) const { return value; } -std::vector parseBezier(const std::string& bezierParams) { - std::vector params; - std::string temp; - size_t startPos = bezierParams.find("(") + 1; - size_t endPos = bezierParams.find(")"); - std::string valuesString = bezierParams.substr(startPos, endPos - startPos); - size_t commaPos = valuesString.find(","); - while (commaPos != std::string::npos) { - temp = valuesString.substr(0, commaPos); - params.push_back(std::stod(temp)); - valuesString = valuesString.substr(commaPos + 1); - commaPos = valuesString.find(","); - } - // Push the last value into the vector - params.push_back(std::stod(valuesString)); - - return params; -} - -//FIXME: cubicBezier is completely Broken -// references to how it should work: +// references for cubic bezier: // https://matthewlein.com/tools/ceaser // https://cubic-bezier.com/ +double cubicBezier(float t, const double& p0,const double& p1, const double& p2, const double& p3) { -double cubicBezier(double t, double p0, double p1, double p2, double p3) { - double u = 1 - t; - double tt = t * t; - double uu = u * u; - double uuu = uu * u; - double ttt = tt * t; + float u = 1 - t; + float tt = t * t; + float uu = u * u; + float uuu = uu * u; + float ttt = tt * t; - double p = uuu * p0; // (1-t)^3 - double q = 3 * uu * t * p1; // 3t(1-t)^2 - double r = 3 * u * tt * p2; // 3(1-t)t^2 - double s = ttt * p3; // t^3 + //Point2d p = {0,0}; + //p.x = uuu * 0 + 3 * uu * t * p0 + 3 * u * tt * p2 + ttt * 1; + return uuu * 0 + 3 * uu * t * p1 + 3 * u * tt * p3 + ttt * 1; - return p + q + r + s; + //return p.y; } -double getEasedT(const std::string& easingType, double t, double b, double c, double d) { - if (easingType == "linear") { - return c * t / d + b; - } - else if (easingType == "quadIn") { - t /= d; - return c * t * t + b; - } - else if (easingType == "quadOut") { - t /= d; - return -c * t * (t - 2) + b; - } - else if (easingType == "quadInOut") { - t /= d / 2; - if (t < 1) { - return c / 2 * t * t + b; - } - else { - t -= 1; - return -c / 2 * (t * (t - 2) - 1) + b; - } - } - else if (easingType == "cubicIn") { - t /= d; - return c * t * t * t + b; - } - else if (easingType == "cubicOut") { - t = (t / d) - 1; - return c * (t * t * t + 1) + b; - } - else if (easingType == "cubicInOut") { - t /= d / 2; - if (t < 1) { - return c / 2 * t * t * t + b; - } - else { - t -= 2; - return c / 2 * (t * t * t + 2) + b; - } - } - else if (easingType == "sinIn") { - return -c * cos(t / d * (M_PI / 2)) + c + b; - } - else if (easingType == "sinOut") { - return c * sin(t / d * (M_PI / 2)) + b; - } - else if (easingType == "sinInOut") { - return -c / 2 * (cos(M_PI * t / d) - 1) + b; - } - else if (easingType == "expoIn") { - return c * pow(2, 10 * (t / d - 1)) + b; - } - else if (easingType == "expoOut") { - return c * (-pow(2, -10 * t / d) + 1) + b; - } - else if (easingType == "expoInOut") { - t /= d / 2; - if (t < 1) { - return c / 2 * pow(2, 10 * (t - 1)) + b; - } - else { - t -= 1; - return c / 2 * (-pow(2, -10 * t) + 2) + b; - } - } - else if (easingType == "circIn") { - t /= d; - return -c * (sqrt(1 - t * t) - 1) + b; - } - else if (easingType == "circOut") { - t = (t / d) - 1; - return c * sqrt(1 - t * t) + b; - } - else if (easingType == "circInOut") { - t /= d / 2; - if (t < 1) { - return -c / 2 * (sqrt(1 - t * t) - 1) + b; - } - else { - t -= 2; - return c / 2 * (sqrt(1 - t * t) + 1) + b; - } - } +double getEasedTime(const std::string& easingType, double t, double b, double c, double d) { + if (easingType == "linear") return cubicBezier(t, 0.250, 0.250, 0.750, 0.750); + + else if (easingType == "ease") return cubicBezier(t, 0.250, 0.100, 0.250, 1.000); + else if (easingType == "easeIn") return cubicBezier(t, 0.420, 0.000, 1.000, 1.000); + else if (easingType == "easeOut") return cubicBezier(t, 0.000, 0.000, 0.580, 1.000); + else if (easingType == "easeInOut") return cubicBezier(t, 0.420, 0.000, 0.580, 1.000); + + else if (easingType == "quadIn") return cubicBezier(t, 0.550, 0.085, 0.680, 0.530); + else if (easingType == "quadOut") return cubicBezier(t, 0.250, 0.460, 0.450, 0.940); + else if (easingType == "quadInOut") return cubicBezier(t, 0.455, 0.030, 0.515, 0.955); + + else if (easingType == "cubicIn") return cubicBezier(t, 0.550, 0.055, 0.675, 0.190); + else if (easingType == "cubicOut") return cubicBezier(t, 0.215, 0.610, 0.355, 1.000); + else if (easingType == "cubicInOut") return cubicBezier(t, 0.645, 0.045, 0.355, 1.000); + + else if (easingType == "quartIn") return cubicBezier(t, 0.895, 0.030, 0.685, 0.220); + else if (easingType == "quartOut") return cubicBezier(t, 0.165, 0.840, 0.440, 1.000); + else if (easingType == "quartInOut") return cubicBezier(t, 0.770, 0.000, 0.175, 1.000); + + else if (easingType == "quintIn") return cubicBezier(t, 0.755, 0.050, 0.855, 0.060); + else if (easingType == "quintOut") return cubicBezier(t, 0.230, 1.000, 0.320, 1.000); + else if (easingType == "quintInOut") return cubicBezier(t, 0.860, 0.000, 0.070, 1.000); + + else if (easingType == "sineIn") return cubicBezier(t, 0.470, 0.000, 0.745, 0.715); + else if (easingType == "sineOut") return cubicBezier(t, 0.390, 0.575, 0.565, 1.000); + else if (easingType == "sineInOut") return cubicBezier(t, 0.445, 0.050, 0.550, 0.950); + + else if (easingType == "ExpoIn") return cubicBezier(t, 0.950, 0.050, 0.795, 0.035); + else if (easingType == "expoOut") return cubicBezier(t, 0.190, 1.000, 0.220, 1.000); + else if (easingType == "expoInOut") return cubicBezier(t, 1.000, 0.000, 0.000, 1.000); + + else if (easingType == "circIn") return cubicBezier(t, 0.600, 0.040, 0.980, 0.335); + else if (easingType == "circOut") return cubicBezier(t, 0.075, 0.820, 0.165, 1.000); + else if (easingType == "circInOut") return cubicBezier(t, 0.785, 0.135, 0.150, 0.860); + + else if (easingType == "backIn") return cubicBezier(t, 0.600, -0.280, 0.735, 0.045); + else if (easingType == "backOut") return cubicBezier(t, 0.175, 0.885, 0.320, 1.275); + else if (easingType == "backInOut") return cubicBezier(t, 0.680, -0.550, 0.265, 1.550); + else if (easingType == "elasticIn") { if (t == 0) { return b; @@ -5270,8 +5206,9 @@ double getEasedT(const std::string& easingType, double t, double b, double c, do double postFix = a * pow(2, -10 * (t -= 1)); // this is a fix, again, with post-increment operators return postFix * sin((t * d - s) * (2 * M_PI) / p) * 0.5 + c + b; } + else if (easingType == "bounceIn") { - return c - getEasedT("bounceOut", d - t, 0, c, d) + b; + return c - getEasedTime("bounceOut", d - t, 0, c, d) + b; } else if (easingType == "bounceOut") { if ((t /= d) < (1 / 2.75)) { @@ -5292,65 +5229,30 @@ double getEasedT(const std::string& easingType, double t, double b, double c, do } else if (easingType == "bounceInOut") { if (t < d / 2) { - return getEasedT("bounceIn", t * 2, 0, c, d) * 0.5 + b; + return getEasedTime("bounceIn", t * 2, 0, c, d) * 0.5 + b; } else { - return getEasedT("bounceOut", t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b; - } - } - if (easingType.substr(0, 6) == "bezier") { - std::vector < double > bezierParams = parseBezier(easingType.substr(7)); - if (bezierParams.size() == 4) { - return cubicBezier(t / d, bezierParams[0], bezierParams[1], bezierParams[2], bezierParams[3]); + return getEasedTime("bounceOut", t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b; } } - return c * t / d + b; // Default to linear easing if the easing type is not recognized -} - -std::vector interpolate(double start, double end, double duration, const std::string& easingTypeAtStart, const std::string& easingTypeAtEnd) { - std::vector interpolatedValues; - interpolatedValues.push_back(start); + if (easingType.substr(0, 6) == "bezier") { + std::vector bezierParams; - // Calculate the number of steps based on the duration - int numSteps = static_cast(duration); // Convert duration to an integer - double stepSize = 1.0 / numSteps; + size_t startPos = easingType.find("(") + 1; + size_t endPos = easingType.find(")"); + std::string valuesString = easingType.substr(startPos, endPos - startPos); - // Calculate the halfway point - double halfway = start + (end - start) * 0.5; + std::istringstream iss(valuesString); + double value; - if (easingTypeAtEnd == "null") { - // Use easingTypeAtStart for the entire animation - for (int step = 1; step <= numSteps; ++step) { - double t = step * stepSize; - double easedT = getEasedT(easingTypeAtStart, t, 0, 1, 1); // Call getEasedT with appropriate parameters - double interpolatedValue = start + easedT * (end - start); - interpolatedValues.push_back(interpolatedValue); - } - } - else { - // Generate the first half of the interpolation - for (int step = 1; step <= numSteps / 2; ++step) { - double t = step * stepSize; - double normalizedT = t / 0.5; // Normalize the time for the first half - double easedT = getEasedT(easingTypeAtStart, normalizedT, 0, 1, 1); // Call getEasedT with appropriate parameters - double interpolatedValue = start + easedT * (halfway - start); - interpolatedValues.push_back(interpolatedValue); - } + while (iss >> value) bezierParams.push_back(value), iss.ignore(); - // Generate the second half of the interpolation - for (int step = numSteps / 2 + 1; step <= numSteps; ++step) { - double t = step * stepSize; - double normalizedT = (t - 0.5) / 0.5; // Normalize the time for the second half - double easedT = getEasedT(easingTypeAtEnd, normalizedT, 0, 1, 1); // Call getEasedT with appropriate parameters - double interpolatedValue = halfway + easedT * (end - halfway); - interpolatedValues.push_back(interpolatedValue); - } + if (bezierParams.size() == 4) + return cubicBezier(t, bezierParams[0], bezierParams[1], bezierParams[2], bezierParams[3]); } - interpolatedValues.push_back(end); - - return interpolatedValues; + return c * t / d + b; // Default to linear easing if the easing type is not recognized } bool Game_Interpreter::CommandEasyRpgAnimateVariable(lcf::rpg::EventCommand const& com) { @@ -5361,6 +5263,17 @@ bool Game_Interpreter::CommandEasyRpgAnimateVariable(lcf::rpg::EventCommand cons int32_t end = ValueOrVariable(com.parameters[4], com.parameters[5]); int32_t duration = ValueOrVariable(com.parameters[6], com.parameters[7]); + // Extract easing information + std::string easingTypeAtStart = ToString(com.string); + std::string easingTypeAtEnd = "null"; + + std::size_t pos = easingTypeAtStart.find('/'); + + if (pos != std::string::npos) { + easingTypeAtEnd = easingTypeAtStart.substr(pos + 1); + easingTypeAtStart = easingTypeAtStart.substr(0, pos); + } + // Prepare animation-related commands lcf::rpg::EventCommand waitCom; waitCom.code = int(Cmd::Wait); @@ -5368,35 +5281,49 @@ bool Game_Interpreter::CommandEasyRpgAnimateVariable(lcf::rpg::EventCommand cons lcf::rpg::EventCommand animatedCom; animatedCom.code = int(Cmd::ControlVars); std::vector animatedVarParams = { 0, static_cast(target), 0, 0, 0, static_cast(end) }; - animatedCom.parameters = lcf::DBArray(animatedVarParams.begin(), animatedVarParams.end()); std::vector cmdList; - // Extract easing information - std::string easeStart = ToString(com.string); - std::string easeEnd = "null"; + int numSteps = static_cast(duration); + double stepSize = 1.0 / numSteps; - std::size_t pos = easeStart.find('/'); + for (int step = 1; step <= numSteps; ++step) { + double normalizedTime; + double currentTime = step * stepSize; + double halfway; - if (pos != std::string::npos) { - easeEnd = easeStart.substr(pos + 1); - easeStart = easeStart.substr(0, pos); - } + std::string easingType; + + if (easingTypeAtEnd == "null") { // use a single interpolation. + normalizedTime = currentTime; + easingType = easingTypeAtStart; + halfway = (step <= numSteps / 2) ? end : start; + } + else { + if (step <= numSteps / 2) { // use 2 interpolations: start and end. + normalizedTime = currentTime / 0.5; + easingType = easingTypeAtStart; + } + else { + normalizedTime = (currentTime - 0.5) / 0.5; + easingType = easingTypeAtEnd; + } + halfway = start + 0.5 * (end - start); + } + + double easedTime = getEasedTime(easingType, normalizedTime, 0, 1, 1); - // Insert animation commands - std::vector interpolatedValues = interpolate(start, end, duration, easeStart, easeEnd); + double startValue = (step <= numSteps / 2) ? start : halfway; + double endValue = (step <= numSteps / 2) ? halfway : end; + double interpolatedValue = startValue + easedTime * (endValue - startValue); - // Insert animatedCom and waitCom commands for each interpolated value - for (int value : interpolatedValues) { - animatedVarParams.back() = value; + animatedVarParams.back() = interpolatedValue; animatedCom.parameters = lcf::DBArray(animatedVarParams.begin(), animatedVarParams.end()); - animatedCom.indent = com.indent + 1; cmdList.push_back(animatedCom); cmdList.push_back(waitCom); } - // Update current_command index and return true to indicate success Push(cmdList, 0, false); return true; } From c08d8497fe460996060c876a57a8ea05fc318cad Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Fri, 10 May 2024 18:38:44 -0300 Subject: [PATCH 4/5] AnimateVars - Syntax Rewritten and Cleanup - Moved 2 longer functions to a file called animation_helper. - Snake_Case and removal of useless comments. Update Makefile.am --- CMakeLists.txt | 2 + Makefile.am | 2 + src/animation_helper.cpp | 161 +++++++++++++++++++++ src/animation_helper.h | 11 ++ src/game_interpreter.cpp | 304 ++++++++++----------------------------- 5 files changed, 250 insertions(+), 230 deletions(-) create mode 100644 src/animation_helper.cpp create mode 100644 src/animation_helper.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ac8def072c..87515b9d5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,8 @@ add_library(${PROJECT_NAME} OBJECT src/async_op.h src/algo.h src/algo.cpp + src/animation_helper.cpp + src/animation_helper.h src/attribute.h src/attribute.cpp src/audio.cpp diff --git a/Makefile.am b/Makefile.am index 9426404206..90d21f1f5d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,6 +14,8 @@ libeasyrpg_player_a_SOURCES = \ src/async_op.h \ src/algo.h \ src/algo.cpp \ + src/animation_helper.cpp \ + src/animation_helper.h \ src/attribute.h \ src/attribute.cpp \ src/audio.cpp \ diff --git a/src/animation_helper.cpp b/src/animation_helper.cpp new file mode 100644 index 0000000000..00edda4f3e --- /dev/null +++ b/src/animation_helper.cpp @@ -0,0 +1,161 @@ +#include "animation_helper.h" +#include +#include +#include + + +// references for cubic bezier: +// https://matthewlein.com/tools/ceaser +// https://cubic-bezier.com/ +double Animation_Helper::CubicBezier(float t, const double& p0, const double& p1, const double& p2, const double& p3) { + + float u = 1 - t; + float tt = t * t; + float uu = u * u; + float uuu = uu * u; + float ttt = tt * t; + + //Point2d p = {0,0}; + //p.x = uuu * 0 + 3 * uu * t * p0 + 3 * u * tt * p2 + ttt * 1; + return uuu * 0 + 3 * uu * t * p1 + 3 * u * tt * p3 + ttt * 1; + + //return p.y; +} + +double Animation_Helper::GetEasedTime(const std::string& easing_type, double t, double b, double c, double d) { + if (easing_type == "linear") return Animation_Helper::CubicBezier(t, 0.250, 0.250, 0.750, 0.750); + + else if (easing_type == "ease") return Animation_Helper::CubicBezier(t, 0.250, 0.100, 0.250, 1.000); + else if (easing_type == "easeIn") return Animation_Helper::CubicBezier(t, 0.420, 0.000, 1.000, 1.000); + else if (easing_type == "easeOut") return Animation_Helper::CubicBezier(t, 0.000, 0.000, 0.580, 1.000); + else if (easing_type == "easeInOut") return Animation_Helper::CubicBezier(t, 0.420, 0.000, 0.580, 1.000); + + else if (easing_type == "quadIn") return Animation_Helper::CubicBezier(t, 0.550, 0.085, 0.680, 0.530); + else if (easing_type == "quadOut") return Animation_Helper::CubicBezier(t, 0.250, 0.460, 0.450, 0.940); + else if (easing_type == "quadInOut") return Animation_Helper::CubicBezier(t, 0.455, 0.030, 0.515, 0.955); + + else if (easing_type == "cubicIn") return Animation_Helper::CubicBezier(t, 0.550, 0.055, 0.675, 0.190); + else if (easing_type == "cubicOut") return Animation_Helper::CubicBezier(t, 0.215, 0.610, 0.355, 1.000); + else if (easing_type == "cubicInOut") return Animation_Helper::CubicBezier(t, 0.645, 0.045, 0.355, 1.000); + + else if (easing_type == "quartIn") return Animation_Helper::CubicBezier(t, 0.895, 0.030, 0.685, 0.220); + else if (easing_type == "quartOut") return Animation_Helper::CubicBezier(t, 0.165, 0.840, 0.440, 1.000); + else if (easing_type == "quartInOut") return Animation_Helper::CubicBezier(t, 0.770, 0.000, 0.175, 1.000); + + else if (easing_type == "quintIn") return Animation_Helper::CubicBezier(t, 0.755, 0.050, 0.855, 0.060); + else if (easing_type == "quintOut") return Animation_Helper::CubicBezier(t, 0.230, 1.000, 0.320, 1.000); + else if (easing_type == "quintInOut") return Animation_Helper::CubicBezier(t, 0.860, 0.000, 0.070, 1.000); + + else if (easing_type == "sineIn") return Animation_Helper::CubicBezier(t, 0.470, 0.000, 0.745, 0.715); + else if (easing_type == "sineOut") return Animation_Helper::CubicBezier(t, 0.390, 0.575, 0.565, 1.000); + else if (easing_type == "sineInOut") return Animation_Helper::CubicBezier(t, 0.445, 0.050, 0.550, 0.950); + + else if (easing_type == "ExpoIn") return Animation_Helper::CubicBezier(t, 0.950, 0.050, 0.795, 0.035); + else if (easing_type == "expoOut") return Animation_Helper::CubicBezier(t, 0.190, 1.000, 0.220, 1.000); + else if (easing_type == "expoInOut") return Animation_Helper::CubicBezier(t, 1.000, 0.000, 0.000, 1.000); + + else if (easing_type == "circIn") return Animation_Helper::CubicBezier(t, 0.600, 0.040, 0.980, 0.335); + else if (easing_type == "circOut") return Animation_Helper::CubicBezier(t, 0.075, 0.820, 0.165, 1.000); + else if (easing_type == "circInOut") return Animation_Helper::CubicBezier(t, 0.785, 0.135, 0.150, 0.860); + + else if (easing_type == "backIn") return Animation_Helper::CubicBezier(t, 0.600, -0.280, 0.735, 0.045); + else if (easing_type == "backOut") return Animation_Helper::CubicBezier(t, 0.175, 0.885, 0.320, 1.275); + else if (easing_type == "backInOut") return Animation_Helper::CubicBezier(t, 0.680, -0.550, 0.265, 1.550); + + else if (easing_type == "elasticIn") { + if (t == 0) { + return b; + } + if ((t /= d) == 1) { + return b + c; + } + + double p = d * 0.3; + double a = c; + double s = p / 4; + + double post_increment_fix = a * pow(2, 10 * (t -= 1)); + return -(post_increment_fix * sin((t * d - s) * (2 * M_PI) / p)) + b; + } + else if (easing_type == "elasticOut") { + if (t == 0) { + return b; + } + if ((t /= d) == 1) { + return b + c; + } + + double p = d * 0.3; + double a = c; + double s = p / 4; + + return (a * pow(2, -10 * t) * sin((t * d - s) * (2 * M_PI) / p) + c + b); + } + else if (easing_type == "elasticInOut") { + if (t == 0) { + return b; + } + if ((t /= d / 2) == 2) { + return b + c; + } + + double p = d * (0.3 * 1.5); + double a = c; + double s = p / 4; + + if (t < 1) { + double post_increment_fix = a * pow(2, 10 * (t -= 1)); + return -0.5 * (post_increment_fix * sin((t * d - s) * (2 * M_PI) / p)) + b; + } + + double post_increment_fix = a * pow(2, -10 * (t -= 1)); + return post_increment_fix * sin((t * d - s) * (2 * M_PI) / p) * 0.5 + c + b; + } + + else if (easing_type == "bounceIn") { + return c - Animation_Helper::GetEasedTime("bounceOut", d - t, 0, c, d) + b; + } + else if (easing_type == "bounceOut") { + if ((t /= d) < (1 / 2.75)) { + return c * (7.5625 * t * t) + b; + } + else if (t < (2 / 2.75)) { + t -= (1.5 / 2.75); + return c * (7.5625 * t * t + 0.75) + b; + } + else if (t < (2.5 / 2.75)) { + t -= (2.25 / 2.75); + return c * (7.5625 * t * t + 0.9375) + b; + } + else { + t -= (2.625 / 2.75); + return c * (7.5625 * t * t + 0.984375) + b; + } + } + else if (easing_type == "bounceInOut") { + if (t < d / 2) { + return Animation_Helper::GetEasedTime("bounceIn", t * 2, 0, c, d) * 0.5 + b; + } + else { + return Animation_Helper::GetEasedTime("bounceOut", t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b; + } + } + + if (easing_type.substr(0, 6) == "bezier") { + std::vector bezier_params; + + size_t start_pos = easing_type.find("(") + 1; + size_t end_pos = easing_type.find(")"); + std::string value_string = easing_type.substr(start_pos, end_pos - start_pos); + + std::istringstream iss(value_string); + double value; + + while (iss >> value) bezier_params.push_back(value), iss.ignore(); + + if (bezier_params.size() == 4) + return Animation_Helper::CubicBezier(t, bezier_params[0], bezier_params[1], bezier_params[2], bezier_params[3]); + } + + return c * t / d + b; // Default to linear easing if the easing type is not recognized +} diff --git a/src/animation_helper.h b/src/animation_helper.h new file mode 100644 index 0000000000..26efcdbbf8 --- /dev/null +++ b/src/animation_helper.h @@ -0,0 +1,11 @@ +#ifndef ANIMATION_HELPER_H +#define ANIMATION_HELPER_H + +#include + +namespace Animation_Helper { + double CubicBezier(float t, const double& p0, const double& p1, const double& p2, const double& p3); + double GetEasedTime(const std::string& easing_type, double t, double b, double c, double d); +} + +#endif diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index bc91bac639..135dbd7706 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -26,6 +26,7 @@ #include #include "game_interpreter.h" #include "async_handler.h" +#include "animation_helper.h" #include "audio.h" #include "dynrpg.h" #include "filefinder.h" @@ -4997,6 +4998,79 @@ bool Game_Interpreter::CommandManiacCallCommand(lcf::rpg::EventCommand const& co return true; } +bool Game_Interpreter::CommandEasyRpgAnimateVariable(lcf::rpg::EventCommand const& com) { + // $InterpolateVariable("typeStart/typeEnd",[useVarTarget, target, useVarStart, start, useVarEnd, end, useVarDuration, duration]) + + int32_t target = ValueOrVariable(com.parameters[0], com.parameters[1]); + int32_t start = ValueOrVariable(com.parameters[2], com.parameters[3]); + int32_t end = ValueOrVariable(com.parameters[4], com.parameters[5]); + int32_t duration = ValueOrVariable(com.parameters[6], com.parameters[7]); + + // Extract easing information + std::string easing_type_at_start = ToString(com.string); + std::string easing_type_at_end = "null"; + + std::size_t pos = easing_type_at_start.find('/'); + + if (pos != std::string::npos) { + easing_type_at_end = easing_type_at_start.substr(pos + 1); + easing_type_at_start = easing_type_at_start.substr(0, pos); + } + + // Prepare animation-related commands + lcf::rpg::EventCommand wait_com; + wait_com.code = int(Cmd::Wait); + + lcf::rpg::EventCommand animated_com; + animated_com.code = int(Cmd::ControlVars); + std::vector animated_var_params = { 0, static_cast(target), 0, 0, 0, static_cast(end) }; + + std::vector cmd_list; + + int num_steps = static_cast(duration); + double step_size = 1.0 / num_steps; + + for (int step = 1; step <= num_steps; ++step) { + double normalized_time; + double current_time = step * step_size; + double half_way; + + std::string easing_type; + + if (easing_type_at_end == "null") { // use a single interpolation. + normalized_time = current_time; + easing_type = easing_type_at_start; + half_way = (step <= num_steps / 2) ? end : start; + } + else { + if (step <= num_steps / 2) { // use 2 interpolations: start and end. + normalized_time = current_time / 0.5; + easing_type = easing_type_at_start; + } + else { + normalized_time = (current_time - 0.5) / 0.5; + easing_type = easing_type_at_end; + } + half_way = start + 0.5 * (end - start); + } + + double eased_time = Animation_Helper::GetEasedTime(easing_type, normalized_time, 0, 1, 1); + + double start_value = (step <= num_steps / 2) ? start : half_way; + double end_value = (step <= num_steps / 2) ? half_way : end; + double interpolated_value = start_value + eased_time * (end_value - start_value); + + animated_var_params.back() = interpolated_value; + animated_com.parameters = lcf::DBArray(animated_var_params.begin(), animated_var_params.end()); + + cmd_list.push_back(animated_com); + cmd_list.push_back(wait_com); + } + + Push(cmd_list, 0, false); + return true; +} + bool Game_Interpreter::CommandEasyRpgSetInterpreterFlag(lcf::rpg::EventCommand const& com) { if (!Player::HasEasyRpgExtensions()) { return true; @@ -5098,233 +5172,3 @@ int Game_Interpreter::ManiacBitmask(int value, int mask) const { return value; } - -// references for cubic bezier: -// https://matthewlein.com/tools/ceaser -// https://cubic-bezier.com/ -double cubicBezier(float t, const double& p0,const double& p1, const double& p2, const double& p3) { - - float u = 1 - t; - float tt = t * t; - float uu = u * u; - float uuu = uu * u; - float ttt = tt * t; - - //Point2d p = {0,0}; - //p.x = uuu * 0 + 3 * uu * t * p0 + 3 * u * tt * p2 + ttt * 1; - return uuu * 0 + 3 * uu * t * p1 + 3 * u * tt * p3 + ttt * 1; - - //return p.y; -} - -double getEasedTime(const std::string& easingType, double t, double b, double c, double d) { - if (easingType == "linear") return cubicBezier(t, 0.250, 0.250, 0.750, 0.750); - - else if (easingType == "ease") return cubicBezier(t, 0.250, 0.100, 0.250, 1.000); - else if (easingType == "easeIn") return cubicBezier(t, 0.420, 0.000, 1.000, 1.000); - else if (easingType == "easeOut") return cubicBezier(t, 0.000, 0.000, 0.580, 1.000); - else if (easingType == "easeInOut") return cubicBezier(t, 0.420, 0.000, 0.580, 1.000); - - else if (easingType == "quadIn") return cubicBezier(t, 0.550, 0.085, 0.680, 0.530); - else if (easingType == "quadOut") return cubicBezier(t, 0.250, 0.460, 0.450, 0.940); - else if (easingType == "quadInOut") return cubicBezier(t, 0.455, 0.030, 0.515, 0.955); - - else if (easingType == "cubicIn") return cubicBezier(t, 0.550, 0.055, 0.675, 0.190); - else if (easingType == "cubicOut") return cubicBezier(t, 0.215, 0.610, 0.355, 1.000); - else if (easingType == "cubicInOut") return cubicBezier(t, 0.645, 0.045, 0.355, 1.000); - - else if (easingType == "quartIn") return cubicBezier(t, 0.895, 0.030, 0.685, 0.220); - else if (easingType == "quartOut") return cubicBezier(t, 0.165, 0.840, 0.440, 1.000); - else if (easingType == "quartInOut") return cubicBezier(t, 0.770, 0.000, 0.175, 1.000); - - else if (easingType == "quintIn") return cubicBezier(t, 0.755, 0.050, 0.855, 0.060); - else if (easingType == "quintOut") return cubicBezier(t, 0.230, 1.000, 0.320, 1.000); - else if (easingType == "quintInOut") return cubicBezier(t, 0.860, 0.000, 0.070, 1.000); - - else if (easingType == "sineIn") return cubicBezier(t, 0.470, 0.000, 0.745, 0.715); - else if (easingType == "sineOut") return cubicBezier(t, 0.390, 0.575, 0.565, 1.000); - else if (easingType == "sineInOut") return cubicBezier(t, 0.445, 0.050, 0.550, 0.950); - - else if (easingType == "ExpoIn") return cubicBezier(t, 0.950, 0.050, 0.795, 0.035); - else if (easingType == "expoOut") return cubicBezier(t, 0.190, 1.000, 0.220, 1.000); - else if (easingType == "expoInOut") return cubicBezier(t, 1.000, 0.000, 0.000, 1.000); - - else if (easingType == "circIn") return cubicBezier(t, 0.600, 0.040, 0.980, 0.335); - else if (easingType == "circOut") return cubicBezier(t, 0.075, 0.820, 0.165, 1.000); - else if (easingType == "circInOut") return cubicBezier(t, 0.785, 0.135, 0.150, 0.860); - - else if (easingType == "backIn") return cubicBezier(t, 0.600, -0.280, 0.735, 0.045); - else if (easingType == "backOut") return cubicBezier(t, 0.175, 0.885, 0.320, 1.275); - else if (easingType == "backInOut") return cubicBezier(t, 0.680, -0.550, 0.265, 1.550); - - else if (easingType == "elasticIn") { - if (t == 0) { - return b; - } - if ((t /= d) == 1) { - return b + c; - } - - double p = d * 0.3; - double a = c; - double s = p / 4; - - double postFix = a * pow(2, 10 * (t -= 1)); // this is a fix, again, with post-increment operators - return -(postFix * sin((t * d - s) * (2 * M_PI) / p)) + b; - } - else if (easingType == "elasticOut") { - if (t == 0) { - return b; - } - if ((t /= d) == 1) { - return b + c; - } - - double p = d * 0.3; - double a = c; - double s = p / 4; - - return (a * pow(2, -10 * t) * sin((t * d - s) * (2 * M_PI) / p) + c + b); - } - else if (easingType == "elasticInOut") { - if (t == 0) { - return b; - } - if ((t /= d / 2) == 2) { - return b + c; - } - - double p = d * (0.3 * 1.5); - double a = c; - double s = p / 4; - - if (t < 1) { - double postFix = a * pow(2, 10 * (t -= 1)); // this is a fix, again, with post-increment operators - return -0.5 * (postFix * sin((t * d - s) * (2 * M_PI) / p)) + b; - } - - double postFix = a * pow(2, -10 * (t -= 1)); // this is a fix, again, with post-increment operators - return postFix * sin((t * d - s) * (2 * M_PI) / p) * 0.5 + c + b; - } - - else if (easingType == "bounceIn") { - return c - getEasedTime("bounceOut", d - t, 0, c, d) + b; - } - else if (easingType == "bounceOut") { - if ((t /= d) < (1 / 2.75)) { - return c * (7.5625 * t * t) + b; - } - else if (t < (2 / 2.75)) { - t -= (1.5 / 2.75); - return c * (7.5625 * t * t + 0.75) + b; - } - else if (t < (2.5 / 2.75)) { - t -= (2.25 / 2.75); - return c * (7.5625 * t * t + 0.9375) + b; - } - else { - t -= (2.625 / 2.75); - return c * (7.5625 * t * t + 0.984375) + b; - } - } - else if (easingType == "bounceInOut") { - if (t < d / 2) { - return getEasedTime("bounceIn", t * 2, 0, c, d) * 0.5 + b; - } - else { - return getEasedTime("bounceOut", t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b; - } - } - - if (easingType.substr(0, 6) == "bezier") { - std::vector bezierParams; - - size_t startPos = easingType.find("(") + 1; - size_t endPos = easingType.find(")"); - std::string valuesString = easingType.substr(startPos, endPos - startPos); - - std::istringstream iss(valuesString); - double value; - - while (iss >> value) bezierParams.push_back(value), iss.ignore(); - - if (bezierParams.size() == 4) - return cubicBezier(t, bezierParams[0], bezierParams[1], bezierParams[2], bezierParams[3]); - } - - return c * t / d + b; // Default to linear easing if the easing type is not recognized -} - -bool Game_Interpreter::CommandEasyRpgAnimateVariable(lcf::rpg::EventCommand const& com) { - // $InterpolateVariable("typeStart/typeEnd",[useVarTarget, target, useVarStart, start, useVarEnd, end, useVarDuration, duration]) - - int32_t target = ValueOrVariable(com.parameters[0], com.parameters[1]); - int32_t start = ValueOrVariable(com.parameters[2], com.parameters[3]); - int32_t end = ValueOrVariable(com.parameters[4], com.parameters[5]); - int32_t duration = ValueOrVariable(com.parameters[6], com.parameters[7]); - - // Extract easing information - std::string easingTypeAtStart = ToString(com.string); - std::string easingTypeAtEnd = "null"; - - std::size_t pos = easingTypeAtStart.find('/'); - - if (pos != std::string::npos) { - easingTypeAtEnd = easingTypeAtStart.substr(pos + 1); - easingTypeAtStart = easingTypeAtStart.substr(0, pos); - } - - // Prepare animation-related commands - lcf::rpg::EventCommand waitCom; - waitCom.code = int(Cmd::Wait); - - lcf::rpg::EventCommand animatedCom; - animatedCom.code = int(Cmd::ControlVars); - std::vector animatedVarParams = { 0, static_cast(target), 0, 0, 0, static_cast(end) }; - - std::vector cmdList; - - int numSteps = static_cast(duration); - double stepSize = 1.0 / numSteps; - - for (int step = 1; step <= numSteps; ++step) { - double normalizedTime; - double currentTime = step * stepSize; - double halfway; - - std::string easingType; - - if (easingTypeAtEnd == "null") { // use a single interpolation. - normalizedTime = currentTime; - easingType = easingTypeAtStart; - halfway = (step <= numSteps / 2) ? end : start; - } - else { - if (step <= numSteps / 2) { // use 2 interpolations: start and end. - normalizedTime = currentTime / 0.5; - easingType = easingTypeAtStart; - } - else { - normalizedTime = (currentTime - 0.5) / 0.5; - easingType = easingTypeAtEnd; - } - halfway = start + 0.5 * (end - start); - } - - double easedTime = getEasedTime(easingType, normalizedTime, 0, 1, 1); - - double startValue = (step <= numSteps / 2) ? start : halfway; - double endValue = (step <= numSteps / 2) ? halfway : end; - double interpolatedValue = startValue + easedTime * (endValue - startValue); - - animatedVarParams.back() = interpolatedValue; - animatedCom.parameters = lcf::DBArray(animatedVarParams.begin(), animatedVarParams.end()); - - cmdList.push_back(animatedCom); - cmdList.push_back(waitCom); - } - - Push(cmdList, 0, false); - return true; -} - From 7bd16bbe20322b4b12b731438f6fbc48154c2c4b Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Tue, 21 May 2024 21:48:51 -0300 Subject: [PATCH 5/5] AnimateVars - License --- src/animation_helper.cpp | 17 +++++++++++++++++ src/animation_helper.h | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/animation_helper.cpp b/src/animation_helper.cpp index 00edda4f3e..3a939f0929 100644 --- a/src/animation_helper.cpp +++ b/src/animation_helper.cpp @@ -1,3 +1,20 @@ +/* + * This file is part of EasyRPG Player. + * + * EasyRPG Player is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EasyRPG Player is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EasyRPG Player. If not, see . + */ + #include "animation_helper.h" #include #include diff --git a/src/animation_helper.h b/src/animation_helper.h index 26efcdbbf8..2ab1955040 100644 --- a/src/animation_helper.h +++ b/src/animation_helper.h @@ -1,3 +1,20 @@ +/* + * This file is part of EasyRPG Player. + * + * EasyRPG Player is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EasyRPG Player is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EasyRPG Player. If not, see . + */ + #ifndef ANIMATION_HELPER_H #define ANIMATION_HELPER_H