/** * @file m_lib.c * @brief Utility functions for various mathematical and memory operations. * * This file contains a collection of utility functions used for various mathematical * and memory operations. These functions include: * * - mem_copy(): Copy memory from one location to another. * - mem_clear(): Clear (set) a memory area to a given value. * - mem_cmp(): Compare two memory areas. * - cos_s(): Calculate the cosine of a short angle. * - sin_s(): Calculate the sine of a short angle. * - chase_angle(): Chase an angle with a given step value. * - chase_s(): Chase a short integer value with a given step value. * - chase_f(): Chase a float value with a given step value. * - chase_xyz_t(): Chase an xyz_t structure value with a given fraction value. * - chase_angle2(): Chase an angle with a given step value and limit. * - inter_float(): Interpolate between two float values with a given step value. * - get_random_timer(): Get a random timer value with a base and range. * - xyz_t_move(): Move an xyz_t value from a source to a destination. * - xyz_t_move_s_xyz(): Move an xyz_t value from an s_xyz source to a destination. * - xyz_t_add(): Add two xyz_t values. * - xyz_t_sub(): Subtract two xyz_t values. * - xyz_t_mult_v(): Multiply an xyz_t value by a scalar. * - search_position_distance(): Calculate the distance between two 3D positions. * - search_position_distanceXZ(): Calculate the horizontal distance between two 3D positions. * - search_position_angleY(): Calculate the Y angle between two 3D positions. * - search_position_angleX(): Calculate the X angle between two 3D positions. * - add_calc(): Update a float value towards a target with a given fraction, max step, and min step. * - add_calc2(): Update a float value towards a target with a given fraction and max step. * - add_calc0(): Update a float value with a given fraction and max step. * - add_calc_short_angle2(): Update a short angle value towards a target with given fraction, max step, and min step. * - add_calc_short_angle3(): Update a short angle value towards a target with given fraction, max step, and min step. * - rgba_t_move(): Move an rgba_t value from a source to a destination. * - none_proc1(): A general no-op function that returns 0. * - none_proc2(): A no-op function meant for ACTORs which does nothing. * - _Game_play_isPause(): Check if the game is paused. * - check_percent_abs(): Calculate a percentage value with scaling and optional shifting. * - get_percent_forAccelBrake(): Calculate a percentage value for acceleration and braking. * - Game_play_Projection_Trans(): Transform a world position into screen space. * - get_percent(): Calculate a percentage value given a min and max range. */ #include "m_lib.h" #include "game.h" #include "m_actor.h" #include "m_play.h" #include "main.h" #include "libultra/libultra.h" #include "MSL_C/w_math.h" #include "libc64/qrand.h" #include "sys_matrix.h" #include "sys_math.h" #include "sys_math3d.h" /** * @brief Copy memory from the source buffer to the destination buffer. * * @param dst Pointer to the destination buffer. * @param src Pointer to the source buffer. * @param size Number of bytes to copy. */ extern void mem_copy(u8* dst, u8* src, size_t size) { for (size; size != 0; size--) { *dst = *src; src++; dst++; } } /** * @brief Clear memory in the destination buffer by setting it to the specified value. * * @param dst Pointer to the destination buffer. * @param size Number of bytes to clear. * @param val Value to set each byte in the destination buffer. */ extern void mem_clear(u8* dst, size_t size, u8 val) { u32 i; for (i = 0; i < size; i++) { *dst++ = val; } } /** * @brief Compare two memory buffers for equality. * * @param p1 Pointer to the first memory buffer. * @param p2 Pointer to the second memory buffer. * @param size Number of bytes to compare. * @return TRUE if the memory buffers are equal, FALSE otherwise. */ extern int mem_cmp(u8* p1, u8* p2, size_t size) { for (size; size != 0; size--) { if (*p1 != *p2) { return FALSE; } p1++; p2++; } return TRUE; } /** * @brief Calculate the cosine of the given s16 angle. * * @param angle Angle in s16 format. * @return Cosine of the angle as a floating-point value. */ extern f32 cos_s(s16 angle) { return coss(angle) * SHT_MINV; } /** * @brief Calculate the sine of the given s16 angle. * * @param angle Angle in s16 format. * @return Sine of the angle as a floating-point value. */ extern f32 sin_s(s16 angle) { return sins(angle) * SHT_MINV; } /** * @brief Chase an angle value towards a target angle, with a specified step. * * This function updates the angle value, moving it towards the target angle * by a specified step. The angle will not overshoot the target. The function * returns TRUE if the angle reaches the target, and FALSE otherwise. * * @param pValue Pointer to the angle value to be updated. * @param target Target angle to chase. * @param step Step value to be added or subtracted from the current angle. * @return TRUE if the angle reaches the target, FALSE otherwise. */ extern int chase_angle(s16* const pValue, const s16 target, s16 step) { if (step) { f32 updateScale = game_GameFrame_2F; if ((s16)(*pValue - target) > 0) { step = -step; } *pValue += (s16)(step * updateScale); if (((s16)(*pValue - target) * step) >= 0) { *pValue = target; return TRUE; } } else if (*pValue == target) { return TRUE; } return FALSE; } /** * @brief Chase a signed 16-bit value towards a target value, with a specified step. * * This function updates the value, moving it towards the target value * by a specified step. The value will not overshoot the target. The function * returns TRUE if the value reaches the target, and FALSE otherwise. * * @param pValue Pointer to the value to be updated. * @param target Target value to chase. * @param step Step value to be added or subtracted from the current value. * @return TRUE if the value reaches the target, FALSE otherwise. */ extern int chase_s(s16* const pValue, const s16 target, s16 step) { if (step) { if (*pValue > target) { step = -step; } *pValue += step; if ((step * (*pValue - target)) >= 0) { *pValue = target; return TRUE; } } else { if (*pValue == target) { return TRUE; } } return FALSE; } /** * @brief Chase a floating-point value towards a target value, with a specified step. * * This function updates the value, moving it towards the target value * by a specified step. The value will not overshoot the target. The function * returns TRUE if the value reaches the target, and FALSE otherwise. * * @param pValue Pointer to the value to be updated. * @param target Target value to chase. * @param step Step value to be added or subtracted from the current value. * @return TRUE if the value reaches the target, FALSE otherwise. */ extern int chase_f(f32* const pValue, const f32 target, f32 step) { if (step) { if (*pValue > target) { step = -step; } *pValue += step; if ((step * (*pValue - target)) >= 0.0f) { *pValue = target; return TRUE; } } else { if (*pValue == target) { return TRUE; } } return FALSE; } /** * @brief Chase an xyz_t value towards a target xyz_t value, with a specified fraction. * * This function updates the xyz_t value, moving it towards the target xyz_t value * by a specified fraction of the distance between the current value and the target. * The function returns the remaining distance after the chase. * * @param pValue Pointer to the xyz_t value to be updated. * @param target Pointer to the target xyz_t value to chase. * @param fraction Fraction of the distance to move towards the target. * @return Remaining distance after the chase. */ extern f32 chase_xyz_t(xyz_t* const pValue, const xyz_t* const target, const f32 fraction) { xyz_t diff; f32 dist; f32 stepSize; xyz_t_sub(target, pValue, &diff); dist = Math3DVecLength(&diff); if (dist > fraction) { stepSize = fraction / dist; pValue->x += stepSize * diff.x; pValue->y += stepSize * diff.y; pValue->z += stepSize * diff.z; return dist - fraction; } else { xyz_t_move(pValue, target); return 0.0f; } } /** * @brief Chase an angle value towards a limit angle with a specified step, considering wrap-around. * * This function updates the angle value, moving it towards the limit angle by a specified step, * taking into account the angle wrap-around (0 to 65535). The function returns TRUE if the value * reaches the limit, and FALSE otherwise. * * @param pValue Pointer to the angle value to be updated. * @param limit Limit angle to chase. * @param step Step value to be added to the current angle. * @return TRUE if the angle reaches the limit, FALSE otherwise. */ extern int chase_angle2(s16* const pValue, const s16 limit, const s16 step) { s16 prev = *pValue; *pValue += step; if (((s16)(*pValue - limit) * (s16)(prev - limit)) <= 0) { s32 absDiff = ABS((s16)(*pValue - limit)); if (absDiff < 16384) { *pValue = limit; return TRUE; } } return FALSE; } /** * @brief Interpolate between a float and a target using a step. * * @param pValue Pointer to the float to be updated. * @param arg1 Target value for interpolation. * @param step Step value for interpolation speed. */ extern void inter_float(f32* const pValue, const f32 arg1, const int step) { if (step <= 0) { *pValue = arg1; } else { f32 diff = arg1 - *pValue; *pValue += diff / step; } } /** * @brief Generate a random timer value based on a base value and a range. * * This function generates a random timer value by adding a fraction of the specified range * to the base value. The random fraction is generated using fqrand(). * Range will be [base, base + range). * * @param base Base value for the timer. * @param range Range value used to generate the random fraction. * @return Random timer value. */ extern s16 get_random_timer(const s16 base, const s16 range) { return base + (s16)(range * fqrand()); } /** * @brief Copy an xyz_t to another xyz_t. * * @param dest Destination xyz_t structure. * @param src Source xyz_t structure. */ extern void xyz_t_move(xyz_t* const dest, const xyz_t* const src) { dest->x = src->x; dest->y = src->y; dest->z = src->z; } /** * @brief Move the content of an s_xyz structure to an xyz_t structure. * * @param dest Pointer to the destination xyz_t structure. * @param src Pointer to the source s_xyz structure. */ extern void xyz_t_move_s_xyz(xyz_t* const dest, const s_xyz* const src) { dest->x = src->x; dest->y = src->y; dest->z = src->z; } /** * @brief Add two xyz_t structures element-wise. * * @param augend First input xyz_t structure. * @param addend Second input xyz_t structure. * @param total Output xyz_t structure for the result. */ extern void xyz_t_add(const xyz_t* const augend, const xyz_t* const addend, xyz_t* const total) { total->x = augend->x + addend->x; total->y = augend->y + addend->y; total->z = augend->z + addend->z; } /** * @brief Subtract two xyz_t structures element-wise. * * @param minuend First input xyz_t structure. * @param subtrahend Second input xyz_t structure. * @param diff Output xyz_t structure for the result. */ extern void xyz_t_sub(const xyz_t* const minuend, const xyz_t* const subtrahend, xyz_t* const diff) { diff->x = minuend->x - subtrahend->x; diff->y = minuend->y - subtrahend->y; diff->z = minuend->z - subtrahend->z; } /** * @brief Multiply an xyz_t by a scalar. * * @param multiplicand Input xyz_t structure. * @param multiplier Scalar value. */ extern void xyz_t_mult_v(xyz_t* const multiplicand, const f32 multiplier) { multiplicand->x *= multiplier; multiplicand->y *= multiplier; multiplicand->z *= multiplier; } /** * @brief Calculate the Euclidean distance between two xyz_t structures. * * @param pos Pointer to the first xyz_t structure representing the position. * @param target Pointer to the second xyz_t structure representing the target position. * @return The Euclidean distance between the two xyz_t structures. */ extern f32 search_position_distance(const xyz_t* const pos, const xyz_t* const target) { f32 diffX = target->x - pos->x; f32 diffY = target->y - pos->y; f32 diffZ = target->z - pos->z; return sqrtf((diffX * diffX) + (diffY * diffY) + (diffZ * diffZ)); } /** * @brief Calculate the Euclidean distance between two xyz_t structures in the XZ plane. * * @param pos Pointer to the first xyz_t structure representing the position. * @param target Pointer to the second xyz_t structure representing the target position. * @return The Euclidean distance between the two xyz_t structures in the XZ plane. */ extern f32 search_position_distanceXZ(const xyz_t* const pos, const xyz_t* const target) { f32 diffX = target->x - pos->x; f32 diffZ = target->z - pos->z; return sqrtf((diffX * diffX) + (diffZ * diffZ)); } /** * @brief Calculate the angle in the Y axis (yaw) between two xyz_t positions. * * @param pos Pointer to the first xyz_t structure representing the current position. * @param target Pointer to the second xyz_t structure representing the target position. * @return The angle in the Y axis (yaw) between the two xyz_t positions. */ extern s16 search_position_angleY(const xyz_t* const pos, const xyz_t* const target) { f32 diffX = target->x - pos->x; f32 diffZ = target->z - pos->z; return atans_table(diffZ, diffX); } /** * @brief Calculate the angle in the X axis (pitch) between two xyz_t positions. * * @param pos Pointer to the first xyz_t structure representing the current position. * @param target Pointer to the second xyz_t structure representing the target position. * @return The angle in the X axis (pitch) between the two xyz_t structures. */ extern s16 search_position_angleX(const xyz_t* const pos, const xyz_t* const target) { f32 diffXZ = search_position_distanceXZ(pos, target); f32 diffY = pos->y - target->y; return atans_table(diffXZ, diffY); } /** * @brief Add a calculated value to a variable to approach a target value with minimum and maximum step limits. * * This function adds a calculated step size to the input variable to approach the target value. * The step size is calculated based on the fraction and is limited by the minimum and maximum step values. * * @param pValue Pointer to the input variable. * @param target Target value to approach. * @param fraction Fraction to use in the step size calculation. * @param maxStep Maximum allowed step size. * @param minStep Minimum allowed step size. * @return The difference between the updated input variable value and the target value. */ extern f32 add_calc(f32* pValue, f32 target, f32 fraction, f32 maxStep, f32 minStep) { f32 negMinStep; f32 stepSize; if (*pValue != target) { stepSize = fraction * (target - *pValue); negMinStep = -minStep; if ((stepSize <= negMinStep) || (minStep <= stepSize)) { if (stepSize > maxStep) { stepSize = maxStep; } else if (stepSize < -maxStep) { stepSize = -maxStep; } *pValue += stepSize; if (stepSize > 0.0f) { if (*pValue > target) { *pValue = target; } } else { if (*pValue < target) { *pValue = target; } } } else { if (stepSize > 0.0f) { *pValue += minStep; if (*pValue > target) { *pValue = target; } } else { *pValue += negMinStep; if (*pValue < target) { *pValue = target; } } } } return target - *pValue; } /** * @brief Add a calculated value to a variable to approach a target value with a maximum step limit. * * This function adds a calculated step size to the input variable to approach the target value. * The step size is calculated based on the fraction and is limited by the maximum step value. * * @param pValue Pointer to the input variable. * @param target Target value to approach. * @param fraction Fraction to use in the step size calculation. * @param maxStep Maximum allowed step size. */ extern void add_calc2(f32* pValue, f32 target, f32 fraction, f32 maxStep) { f32 stepSize; if (*pValue != target) { stepSize = fraction * (target - *pValue); if (stepSize > maxStep) { stepSize = maxStep; } else if (stepSize < -maxStep) { stepSize = -maxStep; } *pValue += stepSize; } } /** * @brief Step a variable towards 0 by a fixed fraction, with a specified maximum possible step. * * @param pValue Pointer to the input variable. * @param fraction Fraction to use in the step size calculation. * @param maxStep Maximum allowed step size. */ extern void add_calc0(f32* pValue, f32 fraction, f32 maxStep) { f32 stepSize = *pValue * fraction; if (stepSize > maxStep) { stepSize = maxStep; } else if (stepSize < -maxStep) { stepSize = -maxStep; } *pValue -= stepSize; } /** * @brief Add a calculated value to a short integer variable to approach a target angle with minimum and maximum step * limits. * * This function adds a calculated step size to the input short integer variable to approach the target angle. * The step size is calculated based on the fraction and is limited by the minimum and maximum step values. * * @param pValue Pointer to the input short integer variable. * @param target Target angle to approach. * @param fraction Fraction to use in the step size calculation. * @param maxStep Maximum allowed step size. * @param minStep Minimum allowed step size. * @return The difference between the updated input variable angle and the target angle. */ extern s16 add_calc_short_angle2(s16* pValue, s16 target, f32 fraction, s16 maxStep, s16 minStep) { s16 stepSize = 0; s16 diff = target - *pValue; if (*pValue != target) { stepSize = (s16)(diff * fraction); if ((stepSize > minStep) || (stepSize < -minStep)) { if (stepSize > maxStep) { stepSize = maxStep; } else if (stepSize < -maxStep) { stepSize = -maxStep; } *pValue += stepSize; if (stepSize > 0) { if ((s16)(target - *pValue) < 0) { *pValue = target; } } else { if ((s16)(target - *pValue) > 0) { *pValue = target; } } } else { if (diff >= 0) { *pValue += minStep; if ((s16)(target - *pValue) < 0) { *pValue = target; } } else { *pValue -= minStep; if ((s16)(target - *pValue) > 0) { *pValue = target; } } } } return target - *pValue; } /** * @brief Add a calculated value to a short integer angle variable to approach a target angle with minimum and maximum * step limits. * * This function adds a calculated step size to the input short integer angle variable to approach the target angle. * The step size is calculated based on the fraction and is limited by the minimum and maximum step values. * Handles angle wrapping correctly. * * @param pValue Pointer to the input short integer angle variable. * @param target Target angle to approach. * @param fraction Fraction to use in the step size calculation. * @param maxStep Maximum allowed step size. * @param minStep Minimum allowed step size. * @return The difference between the updated input variable angle and the target angle. */ extern s16 add_calc_short_angle3(s16* pValue, s16 target, f32 fraction, s16 maxStep, s16 minStep) { f32 stepSize; s32 uTarget; s32 newValue; s32 uValue; if (target != *pValue) { uValue = (u16)*pValue; uTarget = (u16)target; if (uValue > uTarget) { uTarget += 65536; /* Add 360 short degrees */ } stepSize = (uTarget - uValue) * fraction; if (stepSize > maxStep) { stepSize = maxStep; } else if (stepSize < minStep) { stepSize = minStep; } newValue = uValue + (s32)stepSize; if (newValue > uTarget) { newValue = uTarget; } *pValue = newValue; } return target - *pValue; } /** * @brief Copy RBGA color from one rgba_t to another. * * @param dest Pointer to the destination rgba_t variable. * @param src Pointer to the source rgba_t variable. */ extern void rgba_t_move(rgba_t* dest, const rgba_t* const src) { dest->r = src->r; dest->g = src->g; dest->b = src->b; dest->a = src->a; } /** * @brief A general purpose no-op function. * * @return 0 */ extern int none_proc1() { return 0; } /** * @brief No-op function meant for use in actor profiles. * * @param actor Pointer to an ACTOR structure. * @param game Pointer to a GAME structure. */ extern void none_proc2(ACTOR* actor, GAME* game) { } /** * @brief Check if the game is in pause state. * * @param play Pointer to a GAME_PLAY structure. * @return TRUE if the game is in pause state, FALSE otherwise. */ extern int _Game_play_isPause(GAME_PLAY* play) { return (play->pause.enabled != 0); } /** * @brief Calculate a percentage with respect to minimum and maximum values, and apply scaling. * * - If `x` is closer to 0 than `min`, return 0 * - If `x` is further from 0 than `max`, return the sign of `x` * - Otherwise, scale `x` by `scale` and return it. * If `shift_by_min`, move `x` by `min` towards 0 before scaling it. * * @param x Input value to check. * @param min Minimum value for the range. * @param max Maximum value for the range. * @param scale Scaling factor to apply to the percentage. * @param shift_by_min Flag to shift the percentage by the minimum value (1 to shift, 0 not to shift). * @return Scaled percentage of the input value within the specified range. */ extern f32 check_percent_abs(f32 x, f32 min, f32 max, f32 scale, int shift_by_min) { if ((-min <= x) && (x <= min)) { return 0.0f; } if (x >= max) { return 1.0f; } if (x <= -max) { return -1.0f; } if (shift_by_min) { if (x > 0.0f) { return (x - min) * scale; } else { return (x + min) * scale; } } else { return x * scale; } } /** * @brief Calculate the percentage of completion based on acceleration and braking values. * * This function calculates the percentage of completion using the current position, start and end positions, * acceleration and braking distances using quadratic easing. * * @param now Current position value. * @param start Start position value. * @param end End position value. * @param accelerateDist Acceleration distance. * @param brakeDist Braking distance. * @return Percentage of completion. */ extern f32 get_percent_forAccelBrake(f32 now, f32 start, f32 end, f32 accelerateDist, f32 brakeDist) { f32 percent; f32 total_delta; f32 now_delta; f32 step; if (now >= end) { return 1.0f; } if (now <= start) { return 0.0f; } total_delta = end - start; now_delta = now - start; if (total_delta < (accelerateDist + brakeDist)) { return 0.0f; } step = 1.0f / (((2.0f * total_delta) - accelerateDist) - brakeDist); if (accelerateDist != 0.0f) { if (now_delta <= accelerateDist) { percent = (now_delta * (step * now_delta)); percent /= accelerateDist; return percent; } percent = step * accelerateDist; } else { percent = 0.0f; } if (now_delta <= (total_delta - brakeDist)) { percent += (step * 2.0f) * (now_delta - accelerateDist); return percent; } percent += (2.0f * step * ((total_delta - accelerateDist) - brakeDist)); if (brakeDist != 0.0f) { percent += step * brakeDist; if (now_delta < total_delta) { f32 diff = total_delta - now_delta; percent -= step * diff * diff / brakeDist; } } return percent; } /** * @brief Project a 3D world position onto a 2D screen position. * * @param play Pointer to a GAME_PLAY structure. * @param wpos Pointer to the 3D world position (xyz_t). * @param screen_pos Pointer to the resulting 2D screen position (xyz_t). */ extern void Game_play_Projection_Trans(GAME_PLAY* const play, xyz_t* world_pos, xyz_t* screen_pos) { f32 w; Matrix_mult(&play->projection_matrix, 0); Matrix_Position(world_pos, screen_pos); w = play->projection_matrix.ww + ((play->projection_matrix.wx * world_pos->x) + (play->projection_matrix.wy * world_pos->y) + (play->projection_matrix.wz * world_pos->z)); screen_pos->x = (SCREEN_WIDTH_F / 2.0f) + ((screen_pos->x / w) * (SCREEN_WIDTH_F / 2.0f)); screen_pos->y = (SCREEN_HEIGHT_F / 2.0f) - ((screen_pos->y / w) * (SCREEN_HEIGHT_F / 2.0f)); } /** * @brief Calculate the percentage of the input value within the specified range. * * @param max Maximum value for the range. * @param min Minimum value for the range. * @param x Input value to check. * @return Percentage of the input value within the specified range. */ extern f32 get_percent(const int max, const int min, const int x) { f32 total_delta; f32 percent; percent = 1.0f; if (x < min) { percent = 0.0f; } else if (x < max) { total_delta = max - min; if (total_delta != 0.0f) { percent = (f32)(x - min) / total_delta; if (percent > 1.0f) { percent = 1.0f; } } } return percent; }