diff --git a/src/lua_mathlib.c b/src/lua_mathlib.c index 244fdc36f..6bee31513 100644 --- a/src/lua_mathlib.c +++ b/src/lua_mathlib.c @@ -190,6 +190,16 @@ static int lib_coloropposite(lua_State *L) return 2; } +static int lib_approach(lua_State* L) +{ + lua_pushinteger(L, + ApproachFixed((fixed_t)luaL_checkinteger(L, 1), + (fixed_t)luaL_checkinteger(L, 2), + (fixed_t)luaL_checkinteger(L, 3), + (fixed_t)luaL_checkinteger(L, 4))); + return 1; +} + static luaL_Reg lib_math[] = { {"abs", lib_abs}, {"min", lib_min}, @@ -225,6 +235,7 @@ static luaL_Reg lib_math[] = { {"GetSecSpecial", lib_getsecspecial}, {"All7Emeralds", lib_all7emeralds}, {"ColorOpposite", lib_coloropposite}, + {"approach", lib_approach}, {NULL, NULL} }; diff --git a/src/m_fixed.c b/src/m_fixed.c index 72d5c451a..695ad4d93 100644 --- a/src/m_fixed.c +++ b/src/m_fixed.c @@ -1129,6 +1129,188 @@ void FM_Scale(matrix_t *dest, fixed_t x, fixed_t y, fixed_t z) #undef M } +// "Approach" functions taken from or based on the (decompiled) SM64 implementation +// https://github.com/n64decomp/sm64/blob/master/src/engine/math_util.c + +/** \brief Returns the INT32 value 'current' after it tries to approach target, going up at most + 'inc' and going down at most 'dec'. + * + * If target is close to the max or min of INT32, then it's possible to overflow past it without + stopping. + + \param current (INT32) current value + + \param target (INT32) target value + + \param inc (INT32) value to increment by + + \param dec (INT32) value to decrement by + + \return (INT32) current value after incrementing/decrementing +*/ +INT32 ApproachINT32(INT32 current, INT32 target, INT32 inc, INT32 dec) +{ + if (current < target) + { + current += inc; + if (current > target) + { + current = target; + } + } + else + { + current -= dec; + if (current < target) + { + current = target; + } + } + return current; +} + +/** \brief Returns the unsigned INT32 value ``current`` after it tries to approach target, going + up at most ``inc`` and going down at most ``dec``. + * + * If target is close to the max or min of UINT32, then it's possible to overflow past it without + stopping. + + \param current (UINT32) current value + + \param target (UINT32) target value + + \param inc (UINT32) value to increment by + + \param dec (UINT32) value to decrement by + + \return (UINT32) current value after incrementing/decrementing +*/ +UINT32 ApproachUINT32(UINT32 current, UINT32 target, UINT32 inc, UINT32 dec) +{ + if (current < target) + { + current += inc; + if (current > target) + { + current = target; + } + } + else + { + current -= dec; + if (current < target) + { + current = target; + } + } + return current; +} + +/** \brief Returns the fixed-point value 'current' after it tries to approach target, going up at + most 'inc' and going down at most 'dec'. + * + * If target is close to the max or min of fixed_t, then it's possible to overflow past it without + stopping. + + \param current (fixed_t) current value + + \param target (fixed_t) target value + + \param inc (fixed_t) value to increment by + + \param dec (fixed_t) value to decrement by + + \return (fixed_t) current value after incrementing/decrementing +*/ +fixed_t ApproachFixed(fixed_t current, fixed_t target, fixed_t inc, fixed_t dec) +{ + if (current < target) + { + current += inc; + if (current > target) + { + current = target; + } + } + else + { + current -= dec; + if (current < target) + { + current = target; + } + } + return current; +} + +/** \brief Returns the floating-point value 'current' after it tries to approach target, going up at + most 'inc' and going down at most 'dec'. + + \param current (float) current value + + \param target (float) target value + + \param inc (float) value to increment by + + \param dec (float) value to decrement by + + \return (float) current value after incrementing/decrementing +*/ +float ApproachFloat(float current, float target, float inc, float dec) +{ + if (current < target) + { + current += inc; + if (current > target) + { + current = target; + } + } + else + { + current -= dec; + if (current < target) + { + current = target; + } + } + return current; +} + +/** \brief Returns the double-precision floating-point value 'current' after it tries to approach + target, going up at most 'inc' and going down at most 'dec'. + + \param current (double) current value + + \param target (double) target value + + \param inc (double) value to increment by + + \param dec (double) value to decrement by + + \return (double) current value after incrementing/decrementing +*/ +double ApproachDouble(double current, double target, double inc, double dec) +{ + if (current < target) + { + current += inc; + if (current > target) + { + current = target; + } + } + else + { + current -= dec; + if (current < target) + { + current = target; + } + } + return current; +} + #ifdef M_TESTCASE //#define MULDIV_TEST #define SQRT_TEST diff --git a/src/m_fixed.h b/src/m_fixed.h index d97f3747f..b25d85107 100644 --- a/src/m_fixed.h +++ b/src/m_fixed.h @@ -462,6 +462,52 @@ void FM_MultMatrixEx(const matrix_t *m1, const matrix_t *m2, matrix_t *dest); void FM_Translate(matrix_t *dest, fixed_t x, fixed_t y, fixed_t z); void FM_Scale(matrix_t *dest, fixed_t x, fixed_t y, fixed_t z); +// +// Approach functions, see m_fixed.c for details +// + +INT32 ApproachINT32(INT32 current, INT32 target, INT32 inc, INT32 dec); +UINT32 ApproachUINT32(UINT32 current, UINT32 target, UINT32 inc, UINT32 dec); +fixed_t ApproachFixed(fixed_t current, fixed_t target, fixed_t inc, fixed_t dec); +float ApproachFloat(float current, float target, float inc, float dec); +double ApproachDouble(double current, double target, double inc, double dec); + +/** \brief Returns the INT32 value ``current`` after it tries to approach ``target``, going up + or down at most ``delta``. + * + * If ``target`` is close to the max or min of INT32, then it's possible to overflow past it + without stopping. + + \param current (INT32) current value + + \param target (INT32) target value + + \param delta (INT32) value to increment/decrement by + + \return (INT32) current value after incrementing/decrementing +*/ +#define ApproachOneWayINT32(current, target, delta) (ApproachINT32(current, target, delta, delta)) + +/** \brief Returns the unsigned INT32 value ``current`` after it tries to approach ``target``, + going up or down at most ``delta``. + * + * If ``target`` is close to the max or min of UINT32, then it's possible to overflow past it + without stopping. + + \param current (UINT32) current value + + \param target (UINT32) target value + + \param delta (UINT32) value to increment/decrement by + + \return (UINT32) current value after incrementing/decrementing +*/ +#define ApproachOneWayUINT32(current, target, delta) (ApproachUINT32(current, target, delta, delta)) + +#define ApproachOneWayFixed(current, target, delta) (ApproachFixed(current, target, delta, delta)) +#define ApproachOneWayFloat(current, target, delta) (ApproachFloat(current, target, delta, delta)) +#define ApproachOneWayDouble(current, target, delta) (ApproachDouble(current, target, delta, delta)) + #ifdef __cplusplus } // extern "C" #endif