diff --git a/.gitignore b/.gitignore index 1649f4d3..fd02a3e9 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,7 @@ build/* !/assets/**/*custom*.bin !/assets/**/*custom*/**/*.bin !/textures/crash_custom/*.png +!/actors/breath_meter/breath_meter_*.png # libultra !/lib/*.a diff --git a/Makefile.split b/Makefile.split index 02925fee..d6b6cb9d 100644 --- a/Makefile.split +++ b/Makefile.split @@ -66,7 +66,7 @@ KING_BOBOMB_DIRS := king_bobomb water_bubble BOO_DIRS := bookend book chair small_key mad_piano boo haunted_cage MR_I_DIRS := mr_i_eyeball mr_i_iris swoop snufit dorrie scuttlebug HOOT_DIRS := yellow_sphere_small hoot yoshi_egg thwomp bullet_bill heave_ho -COINS_DIRS := mist explosion butterfly coin warp_pipe door bowser_key flame blue_fish pebble leaves warp_collision mario_cap power_meter mushroom_1up star sand dirt transparent_star white_particle wooden_signpost tree +COINS_DIRS := mist explosion butterfly coin warp_pipe door bowser_key flame blue_fish pebble leaves warp_collision mario_cap breath_meter power_meter mushroom_1up star sand dirt transparent_star white_particle wooden_signpost tree BUBBA_DIRS := bubba wiggler wiggler_body_part lakitu_enemy spiny_egg spiny SKEETER_DIRS := skeeter seaweed water_mine cyan_fish bub water_ring treasure_chest KLEPTO_DIRS := klepto eyerok pokey tornado diff --git a/actors/breath_meter/breath_meter_five_segments.rgba16.png b/actors/breath_meter/breath_meter_five_segments.rgba16.png new file mode 100644 index 00000000..7c99cf9e Binary files /dev/null and b/actors/breath_meter/breath_meter_five_segments.rgba16.png differ diff --git a/actors/breath_meter/breath_meter_four_segments.rgba16.png b/actors/breath_meter/breath_meter_four_segments.rgba16.png new file mode 100644 index 00000000..03ce6917 Binary files /dev/null and b/actors/breath_meter/breath_meter_four_segments.rgba16.png differ diff --git a/actors/breath_meter/breath_meter_full.rgba16.png b/actors/breath_meter/breath_meter_full.rgba16.png new file mode 100644 index 00000000..5a0c4292 Binary files /dev/null and b/actors/breath_meter/breath_meter_full.rgba16.png differ diff --git a/actors/breath_meter/breath_meter_left_side.rgba16.png b/actors/breath_meter/breath_meter_left_side.rgba16.png new file mode 100644 index 00000000..446d6805 Binary files /dev/null and b/actors/breath_meter/breath_meter_left_side.rgba16.png differ diff --git a/actors/breath_meter/breath_meter_one_segment.rgba16.png b/actors/breath_meter/breath_meter_one_segment.rgba16.png new file mode 100644 index 00000000..078c0e4e Binary files /dev/null and b/actors/breath_meter/breath_meter_one_segment.rgba16.png differ diff --git a/actors/breath_meter/breath_meter_right_side.rgba16.png b/actors/breath_meter/breath_meter_right_side.rgba16.png new file mode 100644 index 00000000..cf09496c Binary files /dev/null and b/actors/breath_meter/breath_meter_right_side.rgba16.png differ diff --git a/actors/breath_meter/breath_meter_seven_segments.rgba16.png b/actors/breath_meter/breath_meter_seven_segments.rgba16.png new file mode 100644 index 00000000..13b3e50d Binary files /dev/null and b/actors/breath_meter/breath_meter_seven_segments.rgba16.png differ diff --git a/actors/breath_meter/breath_meter_six_segments.rgba16.png b/actors/breath_meter/breath_meter_six_segments.rgba16.png new file mode 100644 index 00000000..a4ead431 Binary files /dev/null and b/actors/breath_meter/breath_meter_six_segments.rgba16.png differ diff --git a/actors/breath_meter/breath_meter_three_segments.rgba16.png b/actors/breath_meter/breath_meter_three_segments.rgba16.png new file mode 100644 index 00000000..0980cefe Binary files /dev/null and b/actors/breath_meter/breath_meter_three_segments.rgba16.png differ diff --git a/actors/breath_meter/breath_meter_two_segments.rgba16.png b/actors/breath_meter/breath_meter_two_segments.rgba16.png new file mode 100644 index 00000000..5ef15b11 Binary files /dev/null and b/actors/breath_meter/breath_meter_two_segments.rgba16.png differ diff --git a/actors/breath_meter/model.inc.c b/actors/breath_meter/model.inc.c new file mode 100644 index 00000000..1e709e9c --- /dev/null +++ b/actors/breath_meter/model.inc.c @@ -0,0 +1,130 @@ +#ifdef BREATH_METER +// Breath Meter HUD + +// 0x030233E0 +ALIGNED8 static const Texture texture_breath_meter_left_side[] = { +#include "actors/breath_meter/breath_meter_left_side.rgba16.inc.c" +}; + +// 0x030243E0 +ALIGNED8 static const Texture texture_breath_meter_right_side[] = { +#include "actors/breath_meter/breath_meter_right_side.rgba16.inc.c" +}; + +// 0x030253E0 +ALIGNED8 static const Texture texture_breath_meter_full[] = { +#include "actors/breath_meter/breath_meter_full.rgba16.inc.c" +}; + +// 0x03025BE0 +ALIGNED8 static const Texture texture_breath_meter_seven_segments[] = { +#include "actors/breath_meter/breath_meter_seven_segments.rgba16.inc.c" +}; + +// 0x030263E0 +ALIGNED8 static const Texture texture_breath_meter_six_segments[] = { +#include "actors/breath_meter/breath_meter_six_segments.rgba16.inc.c" +}; + +// 0x03026BE0 +ALIGNED8 static const Texture texture_breath_meter_five_segments[] = { +#include "actors/breath_meter/breath_meter_five_segments.rgba16.inc.c" +}; + +// 0x030273E0 +ALIGNED8 static const Texture texture_breath_meter_four_segments[] = { +#include "actors/breath_meter/breath_meter_four_segments.rgba16.inc.c" +}; + +// 0x03027BE0 +ALIGNED8 static const Texture texture_breath_meter_three_segments[] = { +#include "actors/breath_meter/breath_meter_three_segments.rgba16.inc.c" +}; + +// 0x030283E0 +ALIGNED8 static const Texture texture_breath_meter_two_segments[] = { +#include "actors/breath_meter/breath_meter_two_segments.rgba16.inc.c" +}; + +// 0x03028BE0 +ALIGNED8 static const Texture texture_breath_meter_one_segments[] = { +#include "actors/breath_meter/breath_meter_one_segment.rgba16.inc.c" +}; + +// 0x030293E0 +const Texture *const breath_meter_segments_lut[] = { + texture_breath_meter_one_segments, + texture_breath_meter_two_segments, + texture_breath_meter_three_segments, + texture_breath_meter_four_segments, + texture_breath_meter_five_segments, + texture_breath_meter_six_segments, + texture_breath_meter_seven_segments, + texture_breath_meter_full, +}; + +// 0x03029400 +static const Vtx vertex_breath_meter_base[] = { + {{{ -32, -32, 0}, 0, { 0, 2016}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 0, -32, 0}, 0, { 992, 2016}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 0, 32, 0}, 0, { 992, 0}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ -32, 32, 0}, 0, { 0, 0}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 0, -32, 0}, 0, { 1, 2016}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 32, -32, 0}, 0, { 1024, 2016}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 32, 32, 0}, 0, { 1024, 0}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 0, 32, 0}, 0, { 1, 0}, {0xff, 0xff, 0xff, 0xff}}}, +}; + +// 0x03029480 - 0x03029530 +const Gfx dl_breath_meter_base[] = { + gsDPPipeSync(), + gsSPClearGeometryMode(G_LIGHTING), + gsDPSetCombineMode(G_CC_DECALRGBA, G_CC_DECALRGBA), + gsDPSetRenderMode(G_RM_TEX_EDGE, G_RM_TEX_EDGE2), + gsDPSetTextureFilter(G_TF_POINT), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsSPVertex(vertex_breath_meter_base, 8, 0), + gsDPSetTile(G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, (G_TX_WRAP | G_TX_NOMIRROR), G_TX_NOMASK, G_TX_NOLOD, (G_TX_WRAP | G_TX_NOMIRROR), G_TX_NOMASK, G_TX_NOLOD), + gsDPTileSync(), + gsDPSetTile(G_IM_FMT_RGBA, G_IM_SIZ_16b, 8, 0, G_TX_RENDERTILE, 0, G_TX_CLAMP, 6, G_TX_NOLOD, G_TX_CLAMP, 5, G_TX_NOLOD), + gsDPSetTileSize(0, 0, 0, ((32 - 1) << G_TEXTURE_IMAGE_FRAC), ((64 - 1) << G_TEXTURE_IMAGE_FRAC)), + gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, texture_breath_meter_left_side), + gsDPLoadSync(), + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, 32 * 64 - 1, CALC_DXT(32, G_IM_SIZ_16b_BYTES)), + gsSP2Triangles( 0, 1, 2, 0x0, 0, 2, 3, 0x0), + gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, texture_breath_meter_right_side), + gsDPLoadSync(), + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, 32 * 64 - 1, CALC_DXT(32, G_IM_SIZ_16b_BYTES)), + gsSP2Triangles( 4, 5, 6, 0x0, 4, 6, 7, 0x0), + gsSPEndDisplayList(), +}; + +// 0x03029530 +static const Vtx vertex_breath_meter_health_segments[] = { + {{{ -16, -16, 0}, 0, { 0, 992}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 16, -16, 0}, 0, { 992, 992}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 16, 16, 0}, 0, { 992, 0}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ -16, 16, 0}, 0, { 0, 0}, {0xff, 0xff, 0xff, 0xff}}}, +}; + +// 0x03029570 - 0x030295A0 +const Gfx dl_breath_meter_health_segments_begin[] = { + gsDPPipeSync(), + gsSPVertex(vertex_breath_meter_health_segments, 4, 0), + gsDPTileSync(), + gsDPSetTile(G_IM_FMT_RGBA, G_IM_SIZ_16b, 8, 0, G_TX_RENDERTILE, 0, G_TX_CLAMP, 5, G_TX_NOLOD, G_TX_CLAMP, 5, G_TX_NOLOD), + gsDPSetTileSize(0, 0, 0, ((32 - 1) << G_TEXTURE_IMAGE_FRAC), ((32 - 1) << G_TEXTURE_IMAGE_FRAC)), + gsSPEndDisplayList(), +}; + +// 0x030295A0 - 0x030295D8 +const Gfx dl_breath_meter_health_segments_end[] = { + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsSPSetGeometryMode(G_LIGHTING), + gsDPSetRenderMode(G_RM_OPA_SURF, G_RM_OPA_SURF2), + gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE), + gsDPSetTextureFilter(G_TF_BILERP), + gsSPEndDisplayList(), +}; +#endif diff --git a/actors/common1.c b/actors/common1.c index 702fd215..b34bb61f 100644 --- a/actors/common1.c +++ b/actors/common1.c @@ -56,6 +56,9 @@ UNUSED static const u64 binid_11 = 11; UNUSED static const u64 binid_12 = 12; #include "power_meter/model.inc.c" +#ifdef BREATH_METER +#include "breath_meter/model.inc.c" +#endif UNUSED static const u64 binid_13 = 13; UNUSED static const u64 binid_14 = 14; diff --git a/actors/common1.h b/actors/common1.h index a6663a19..b9070c0a 100644 --- a/actors/common1.h +++ b/actors/common1.h @@ -195,6 +195,14 @@ extern const Gfx dl_power_meter_base[]; extern const Gfx dl_power_meter_health_segments_begin[]; extern const Gfx dl_power_meter_health_segments_end[]; +#ifdef BREATH_METER +// breath_meter +extern const Texture *const breath_meter_segments_lut[]; +extern const Gfx dl_breath_meter_base[]; +extern const Gfx dl_breath_meter_health_segments_begin[]; +extern const Gfx dl_breath_meter_health_segments_end[]; +#endif + // sand extern const Gfx sand_seg3_dl_0302BCD0[]; diff --git a/include/config.h b/include/config.h index 18422ac1..c6e5a01a 100644 --- a/include/config.h +++ b/include/config.h @@ -28,6 +28,8 @@ // -- GAME SETTINGS -- // Disable lives and hide the lives counter #define DISABLE_LIVES +// Air/Breath meter is separate from health meter when underwater +//#define BREATH_METER // Number of coins to spawn the "100 coin" star. If you remove the define altogether, then there won't be a 100 coin star at all. #define X_COIN_STAR 100 // Stars don't kick you out of the level diff --git a/include/types.h b/include/types.h index dcce4d0b..4037586a 100644 --- a/include/types.h +++ b/include/types.h @@ -377,6 +377,10 @@ struct MarioState /*0xBC*/ f32 peakHeight; /*0xC0*/ f32 quicksandDepth; /*0xC4*/ f32 windGravity; +#ifdef BREATH_METER + s16 breath; + u8 breathCounter; +#endif }; #endif // TYPES_H diff --git a/src/engine/math_util.c b/src/engine/math_util.c index d2a3b0c9..ec1e88cd 100644 --- a/src/engine/math_util.c +++ b/src/engine/math_util.c @@ -554,7 +554,6 @@ void vec3f_get_dist_and_angle(Vec3f from, Vec3f to, f32 *dist, s16 *pitch, s16 * register f32 x = to[0] - from[0]; register f32 y = to[1] - from[1]; register f32 z = to[2] - from[2]; - *dist = sqrtf(sqr(x) + sqr(y) + sqr(z)); *pitch = atan2s(sqrtf(sqr(x) + sqr(z)), y); *yaw = atan2s(z, x); @@ -570,6 +569,21 @@ void vec3f_set_dist_and_angle(Vec3f from, Vec3f to, f32 dist, s32 pitch, s32 yaw to[2] = from[2] + dist * coss(pitch) * coss(yaw); } +s32 approach_s16(s32 current, s32 target, s32 inc, s32 dec) { + s16 dist = (target - current); + if (dist >= 0) { // target >= current + current = ((dist > inc) ? (current + inc) : target); + } else { // target < current + current = ((dist < -dec) ? (current - dec) : target); + } + return current; +} + +Bool32 approach_s16_bool(s16 *current, s32 target, s32 inc, s32 dec) { + *current = approach_s16(*current, target, inc, dec); + return !(*current == target); +} + /** * Return the value 'current' after it tries to approach target, going up at * most 'inc' and going down at most 'dec'. @@ -584,6 +598,11 @@ s32 approach_s32(s32 current, s32 target, s32 inc, s32 dec) { return current; } +Bool32 approach_s32_bool(s32 *current, s32 target, s32 inc, s32 dec) { + *current = approach_s32(*current, target, inc, dec); + return !(*current == target); +} + /** * Return the value 'current' after it tries to approach target, going up at * most 'inc' and going down at most 'dec'. @@ -598,6 +617,28 @@ f32 approach_f32(f32 current, f32 target, f32 inc, f32 dec) { return current; } +Bool32 approach_f32_bool(f32 *current, f32 target, f32 inc, f32 dec) { + *current = approach_f32(*current, target, inc, dec); + return !(*current == target); +} + +s32 approach_f32_signed(f32 *current, f32 target, f32 inc) { + s32 reachedTarget = FALSE; + *current += inc; + if (inc >= 0.0f) { + if (*current > target) { + *current = target; + reachedTarget = TRUE; + } + } else { + if (*current < target) { + *current = target; + reachedTarget = TRUE; + } + } + return reachedTarget; +} + /** * Similar to approach_s32, but converts to s16 and allows for overflow between 32767 and -32768 */ diff --git a/src/engine/math_util.h b/src/engine/math_util.h index b4224fa9..787e3ffd 100644 --- a/src/engine/math_util.h +++ b/src/engine/math_util.h @@ -374,8 +374,19 @@ void mtxf_rotate_xy(Mtx *mtx, s32 angle); void get_pos_from_transform_mtx(Vec3f dest, Mat4 objMtx, Mat4 camMtx); void vec3f_get_dist_and_angle(Vec3f from, Vec3f to, f32 *dist, s16 *pitch, s16 *yaw); void vec3f_set_dist_and_angle(Vec3f from, Vec3f to, f32 dist, s32 pitch, s32 yaw); +s32 approach_s16(s32 current, s32 target, s32 inc, s32 dec); s32 approach_s32(s32 current, s32 target, s32 inc, s32 dec); f32 approach_f32(f32 current, f32 target, f32 inc, f32 dec); +Bool32 approach_s16_bool(s16 *current, s32 target, s32 inc, s32 dec); +Bool32 approach_s32_bool(s32 *current, s32 target, s32 inc, s32 dec); +Bool32 approach_f32_bool(f32 *current, f32 target, f32 inc, f32 dec); +#define approach_s16_symmetric(current, target, inc) approach_s16((current), (target), (inc), (inc)) +#define approach_s32_symmetric(current, target, inc) approach_s32((current), (target), (inc), (inc)) +#define approach_f32_symmetric(current, target, inc) approach_f32((current), (target), (inc), (inc)) +#define approach_s16_symmetric_bool(current, target, inc) approach_s16_bool((current), (target), (inc), (inc)) +#define approach_s32_symmetric_bool(current, target, inc) approach_s32_bool((current), (target), (inc), (inc)) +#define approach_f32_symmetric_bool(current, target, inc) approach_f32_bool((current), (target), (inc), (inc)) +s32 approach_f32_signed(f32 *current, f32 target, f32 inc); s32 approach_angle(s32 current, s32 target, s32 inc); s16 atan2s(f32 y, f32 x); f32 atan2f(f32 a, f32 b); diff --git a/src/game/behaviors/recovery_heart.inc.c b/src/game/behaviors/recovery_heart.inc.c index 6bebfaa3..32f1de05 100644 --- a/src/game/behaviors/recovery_heart.inc.c +++ b/src/game/behaviors/recovery_heart.inc.c @@ -30,7 +30,10 @@ void bhv_recovery_heart_loop(void) { } if ((o->oSpinningHeartTotalSpin += o->oAngleVelYaw) >= 0x10000) { - gMarioStates[0].healCounter += 4; + gMarioState->healCounter += 4; +#ifdef BREATH_METER + gMarioState->breathCounter += 4; +#endif o->oSpinningHeartTotalSpin -= 0x10000; } diff --git a/src/game/camera.c b/src/game/camera.c index 7d71d17e..ed046c7d 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -3793,42 +3793,21 @@ s32 collide_with_walls(Vec3f pos, f32 offsetY, f32 radius) { * Compare a vector to a position, return TRUE if they match. */ s32 vec3f_compare(Vec3f pos, f32 posX, f32 posY, f32 posZ) { - s32 equal = FALSE; - - if (pos[0] == posX && pos[1] == posY && pos[2] == posZ) { - equal = TRUE; - } - return equal; + return (pos[0] == posX && pos[1] == posY && pos[2] == posZ); } -s32 clamp_pitch(Vec3f from, Vec3f to, s16 maxPitch, s16 minPitch) { - s32 outOfRange = 0; - s16 pitch; - s16 yaw; +void clamp_pitch(Vec3f from, Vec3f to, s16 maxPitch, s16 minPitch) { + s16 pitch, yaw; f32 dist; - vec3f_get_dist_and_angle(from, to, &dist, &pitch, &yaw); - if (pitch > maxPitch) { - pitch = maxPitch; - outOfRange++; - } - if (pitch < minPitch) { - pitch = minPitch; - outOfRange++; - } + pitch = CLAMP(pitch, minPitch, maxPitch); vec3f_set_dist_and_angle(from, to, dist, pitch, yaw); - return outOfRange; } s32 is_within_100_units_of_mario(f32 posX, f32 posY, f32 posZ) { - s32 isCloseToMario = 0; Vec3f pos; - vec3f_set(pos, posX, posY, posZ); - if (calc_abs_dist(sMarioCamState->pos, pos) < 100.f) { - isCloseToMario = 1; - } - return isCloseToMario; + return (calc_abs_dist(sMarioCamState->pos, pos) < 100.f); } s32 set_or_approach_f32_asymptotic(f32 *dst, f32 goal, f32 scale) { @@ -3837,11 +3816,7 @@ s32 set_or_approach_f32_asymptotic(f32 *dst, f32 goal, f32 scale) { } else { *dst = goal; } - if (*dst == goal) { - return FALSE; - } else { - return TRUE; - } + return (*dst != goal); } /** @@ -3854,11 +3829,7 @@ s32 approach_f32_asymptotic_bool(f32 *current, f32 target, f32 multiplier) { multiplier = 1.f; } *current = *current + (target - *current) * multiplier; - if (*current == target) { - return FALSE; - } else { - return TRUE; - } + return (*current != target); } /** @@ -3885,11 +3856,7 @@ s32 approach_s16_asymptotic_bool(s16 *current, s16 target, s16 divisor) { temp += target; *current = temp; } - if (*current == target) { - return FALSE; - } else { - return TRUE; - } + return (*current != target); } /** diff --git a/src/game/camera.h b/src/game/camera.h index da826ee4..6b3518f5 100644 --- a/src/game/camera.h +++ b/src/game/camera.h @@ -696,7 +696,7 @@ void shake_camera_handheld(Vec3f pos, Vec3f focus); s32 find_c_buttons_pressed(u16 currentState, u16 buttonsPressed, u16 buttonsDown); s32 update_camera_hud_status(struct Camera *c); s32 collide_with_walls(Vec3f pos, f32 offsetY, f32 radius); -s32 clamp_pitch(Vec3f from, Vec3f to, s16 maxPitch, s16 minPitch); +void clamp_pitch(Vec3f from, Vec3f to, s16 maxPitch, s16 minPitch); s32 is_within_100_units_of_mario(f32 posX, f32 posY, f32 posZ); s32 set_or_approach_f32_asymptotic(f32 *dst, f32 goal, f32 scale); s32 approach_f32_asymptotic_bool(f32 *current, f32 target, f32 multiplier); diff --git a/src/game/hud.c b/src/game/hud.c index 4f99deb2..71ebb376 100644 --- a/src/game/hud.c +++ b/src/game/hud.c @@ -14,6 +14,7 @@ #include "save_file.h" #include "print.h" #include "engine/surface_load.h" +#include "engine/math_util.h" #include "puppycam2.h" #include "puppyprint.h" @@ -25,6 +26,23 @@ * cannon reticle, and the unused keys. **/ +#define HUD_POWER_METER_X 140 +#define HUD_POWER_METER_EMPHASIZED_Y 166 +#define HUD_POWER_METER_Y 200 +#define HUD_POWER_METER_HIDDEN_Y 300 + +#ifdef BREATH_METER +// #ifdef DISABLE_LIVES +// #define HUD_BREATH_METER_X 64 +// #define HUD_BREATH_METER_Y 200 +// #define HUD_BREATH_METER_HIDDEN_Y 300 +// #else +#define HUD_BREATH_METER_X 40 +#define HUD_BREATH_METER_Y 32 +#define HUD_BREATH_METER_HIDDEN_Y -20 +// #endif +#endif + // ------------- FPS COUNTER --------------- // To use it, call print_fps(x,y); every frame. #define FRAMETIME_COUNT 30 @@ -41,9 +59,9 @@ f32 calculate_and_update_fps() { frameTimes[curFrameTimeIndex] = newTime; curFrameTimeIndex++; - if (curFrameTimeIndex >= FRAMETIME_COUNT) + if (curFrameTimeIndex >= FRAMETIME_COUNT) { curFrameTimeIndex = 0; - + } return ((f32)FRAMETIME_COUNT * 1000000.0f) / (s32)OS_CYCLES_TO_USEC(newTime - oldTime); } @@ -66,7 +84,6 @@ struct PowerMeterHUD { s8 animation; s16 x; s16 y; - f32 unused; }; struct CameraHUD { @@ -79,9 +96,8 @@ static s16 sPowerMeterStoredHealth; static struct PowerMeterHUD sPowerMeterHUD = { POWER_METER_HIDDEN, - 140, - 166, - 1.0, + HUD_POWER_METER_X, + HUD_POWER_METER_HIDDEN_Y, }; // Power Meter timer that keeps counting when it's visible. @@ -89,12 +105,22 @@ static struct PowerMeterHUD sPowerMeterHUD = { // when the power meter is hidden. s32 sPowerMeterVisibleTimer = 0; +#ifdef BREATH_METER +static s16 sBreathMeterStoredValue; +static struct PowerMeterHUD sBreathMeterHUD = { + BREATH_METER_HIDDEN, + HUD_BREATH_METER_X, + HUD_BREATH_METER_HIDDEN_Y, +}; +s32 sBreathMeterVisibleTimer = 0; +#endif + static struct CameraHUD sCameraHUD = { CAM_STATUS_NONE }; /** * Renders a rgba16 16x16 glyph texture from a table list. */ -void render_hud_tex_lut(s32 x, s32 y, u8 *texture) { +void render_hud_tex_lut(s32 x, s32 y, Texture *texture) { gDPPipeSync(gDisplayListHead++); gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, texture); gSPDisplayList(gDisplayListHead++, &dl_hud_img_load_tex_block); @@ -105,17 +131,17 @@ void render_hud_tex_lut(s32 x, s32 y, u8 *texture) { /** * Renders a rgba16 8x8 glyph texture from a table list. */ -void render_hud_small_tex_lut(s32 x, s32 y, u8 *texture) { - gDPSetTile(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, - G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD); - gDPTileSync(gDisplayListHead++); - gDPSetTile(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 2, 0, G_TX_RENDERTILE, 0, - G_TX_CLAMP, 3, G_TX_NOLOD, G_TX_CLAMP, 3, G_TX_NOLOD); - gDPSetTileSize(gDisplayListHead++, G_TX_RENDERTILE, 0, 0, (8 - 1) << G_TEXTURE_IMAGE_FRAC, (8 - 1) << G_TEXTURE_IMAGE_FRAC); - gDPPipeSync(gDisplayListHead++); - gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, texture); - gDPLoadSync(gDisplayListHead++); - gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 8 * 8 - 1, CALC_DXT(8, G_IM_SIZ_16b_BYTES)); +void render_hud_small_tex_lut(s32 x, s32 y, Texture *texture) { + gDPSetTile( gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD); + gDPTileSync( gDisplayListHead++); + gDPSetTile( gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 2, 0, G_TX_RENDERTILE, 0, + G_TX_CLAMP, 3, G_TX_NOLOD, G_TX_CLAMP, 3, G_TX_NOLOD); + gDPSetTileSize( gDisplayListHead++, G_TX_RENDERTILE, 0, 0, (8 - 1) << G_TEXTURE_IMAGE_FRAC, (8 - 1) << G_TEXTURE_IMAGE_FRAC); + gDPPipeSync( gDisplayListHead++); + gDPSetTextureImage( gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, texture); + gDPLoadSync( gDisplayListHead++); + gDPLoadBlock( gDisplayListHead++, G_TX_LOADTILE, 0, 0, 8 * 8 - 1, CALC_DXT(8, G_IM_SIZ_16b_BYTES)); gSPTextureRectangle(gDisplayListHead++, x << 2, y << 2, (x + 7) << 2, (y + 7) << 2, G_TX_RENDERTILE, 0, 0, 4 << 10, 1 << 10); } @@ -124,17 +150,14 @@ void render_hud_small_tex_lut(s32 x, s32 y, u8 *texture) { * Renders power meter health segment texture using a table list. */ void render_power_meter_health_segment(s16 numHealthWedges) { - u8 *(*healthLUT)[]; - + Texture *(*healthLUT)[]; healthLUT = segmented_to_virtual(&power_meter_health_segments_lut); - - gDPPipeSync(gDisplayListHead++); - gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, - (*healthLUT)[numHealthWedges - 1]); - gDPLoadSync(gDisplayListHead++); - gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 32 * 32 - 1, CALC_DXT(32, G_IM_SIZ_16b_BYTES)); - gSP1Triangle(gDisplayListHead++, 0, 1, 2, 0); - gSP1Triangle(gDisplayListHead++, 0, 2, 3, 0); + gDPPipeSync( gDisplayListHead++); + gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, (*healthLUT)[numHealthWedges - 1]); + gDPLoadSync( gDisplayListHead++); + gDPLoadBlock( gDisplayListHead++, G_TX_LOADTILE, 0, 0, 32 * 32 - 1, CALC_DXT(32, G_IM_SIZ_16b_BYTES)); + gSP1Triangle( gDisplayListHead++, 0, 1, 2, 0); + gSP1Triangle( gDisplayListHead++, 0, 2, 3, 0); } /** @@ -142,14 +165,8 @@ void render_power_meter_health_segment(s16 numHealthWedges) { * That includes the "POWER" base and the colored health segment textures. */ void render_dl_power_meter(s16 numHealthWedges) { - Mtx *mtx; - - mtx = alloc_display_list(sizeof(Mtx)); - - if (mtx == NULL) { - return; - } - + Mtx *mtx = alloc_display_list(sizeof(Mtx)); + if (mtx == NULL) return; guTranslate(mtx, (f32) sPowerMeterHUD.x, (f32) sPowerMeterHUD.y, 0); gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx++), @@ -170,8 +187,7 @@ void render_dl_power_meter(s16 numHealthWedges) { * Checks its timer to later change into deemphasizing mode. */ void animate_power_meter_emphasized(void) { - s16 hudDisplayFlags; - hudDisplayFlags = gHudDisplay.flags; + s16 hudDisplayFlags = gHudDisplay.flags; if (!(hudDisplayFlags & HUD_DISPLAY_FLAG_EMPHASIZE_POWER)) { if (sPowerMeterVisibleTimer == 45.0) { @@ -188,23 +204,12 @@ void animate_power_meter_emphasized(void) { */ static void animate_power_meter_deemphasizing(void) { s16 speed = 5; - - if (sPowerMeterHUD.y >= 181) { - speed = 3; - } - - if (sPowerMeterHUD.y >= 191) { - speed = 2; - } - - if (sPowerMeterHUD.y >= 196) { - speed = 1; - } - + if (sPowerMeterHUD.y > (HUD_POWER_METER_Y - 20)) speed = 3; + if (sPowerMeterHUD.y > (HUD_POWER_METER_Y - 10)) speed = 2; + if (sPowerMeterHUD.y > (HUD_POWER_METER_Y - 5)) speed = 1; sPowerMeterHUD.y += speed; - - if (sPowerMeterHUD.y >= 201) { - sPowerMeterHUD.y = 200; + if (sPowerMeterHUD.y > HUD_POWER_METER_Y) { + sPowerMeterHUD.y = HUD_POWER_METER_Y; sPowerMeterHUD.animation = POWER_METER_VISIBLE; } } @@ -215,7 +220,7 @@ static void animate_power_meter_deemphasizing(void) { */ static void animate_power_meter_hiding(void) { sPowerMeterHUD.y += 20; - if (sPowerMeterHUD.y >= 301) { + if (sPowerMeterHUD.y > HUD_POWER_METER_HIDDEN_Y) { sPowerMeterHUD.animation = POWER_METER_HIDDEN; sPowerMeterVisibleTimer = 0; } @@ -228,31 +233,29 @@ void handle_power_meter_actions(s16 numHealthWedges) { // Show power meter if health is not full, less than 8 if (numHealthWedges < 8 && sPowerMeterStoredHealth == 8 && sPowerMeterHUD.animation == POWER_METER_HIDDEN) { sPowerMeterHUD.animation = POWER_METER_EMPHASIZED; - sPowerMeterHUD.y = 166; + sPowerMeterHUD.y = HUD_POWER_METER_EMPHASIZED_Y; } - // Show power meter if health is full, has 8 if (numHealthWedges == 8 && sPowerMeterStoredHealth == 7) { sPowerMeterVisibleTimer = 0; } - // After health is full, hide power meter if (numHealthWedges == 8 && sPowerMeterVisibleTimer > 45.0) { sPowerMeterHUD.animation = POWER_METER_HIDING; } - // Update to match health value sPowerMeterStoredHealth = numHealthWedges; - +#ifndef BREATH_METER // If Mario is swimming, keep power meter visible if (gPlayerCameraState->action & ACT_FLAG_SWIMMING) { if (sPowerMeterHUD.animation == POWER_METER_HIDDEN || sPowerMeterHUD.animation == POWER_METER_EMPHASIZED) { sPowerMeterHUD.animation = POWER_METER_DEEMPHASIZING; - sPowerMeterHUD.y = 166; + sPowerMeterHUD.y = HUD_POWER_METER_EMPHASIZED_Y; } sPowerMeterVisibleTimer = 0; } +#endif } /** @@ -262,40 +265,114 @@ void handle_power_meter_actions(s16 numHealthWedges) { */ void render_hud_power_meter(void) { s16 shownHealthWedges = gHudDisplay.wedges; - - if (sPowerMeterHUD.animation != POWER_METER_HIDING) { - handle_power_meter_actions(shownHealthWedges); - } - - if (sPowerMeterHUD.animation == POWER_METER_HIDDEN) { - return; - } - + if (sPowerMeterHUD.animation != POWER_METER_HIDING) handle_power_meter_actions(shownHealthWedges); + if (sPowerMeterHUD.animation == POWER_METER_HIDDEN) return; switch (sPowerMeterHUD.animation) { - case POWER_METER_EMPHASIZED: - animate_power_meter_emphasized(); - break; - case POWER_METER_DEEMPHASIZING: - animate_power_meter_deemphasizing(); - break; - case POWER_METER_HIDING: - animate_power_meter_hiding(); - break; - default: - break; + case POWER_METER_EMPHASIZED: animate_power_meter_emphasized(); break; + case POWER_METER_DEEMPHASIZING: animate_power_meter_deemphasizing(); break; + case POWER_METER_HIDING: animate_power_meter_hiding(); break; + default: break; } - render_dl_power_meter(shownHealthWedges); - - sPowerMeterVisibleTimer += 1; + sPowerMeterVisibleTimer++; } -#ifdef VERSION_JP -#define HUD_TOP_Y 210 -#else -#define HUD_TOP_Y 209 +#ifdef BREATH_METER +/** + * Renders breath meter health segment texture using a table list. + */ +void render_breath_meter_segment(s16 numBreathWedges) { + Texture *(*breathLUT)[]; + breathLUT = segmented_to_virtual(&breath_meter_segments_lut); + gDPPipeSync( gDisplayListHead++); + gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, (*breathLUT)[numBreathWedges - 1]); + gDPLoadSync( gDisplayListHead++); + gDPLoadBlock( gDisplayListHead++, G_TX_LOADTILE, 0, 0, ((32 * 32) - 1), CALC_DXT(32, G_IM_SIZ_16b_BYTES)); + gSP1Triangle( gDisplayListHead++, 0, 1, 2, 0); + gSP1Triangle( gDisplayListHead++, 0, 2, 3, 0); +} + +/** + * Renders breath meter display lists. + * That includes the base and the colored segment textures. + */ +void render_dl_breath_meter(s16 numBreathWedges) { + Mtx *mtx = alloc_display_list(sizeof(Mtx)); + if (mtx == NULL) return; + guTranslate(mtx, ((f32) sBreathMeterHUD.x), ((f32) sBreathMeterHUD.y), 0); + gSPMatrix( gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx++), + (G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH)); + gSPDisplayList( gDisplayListHead++, &dl_breath_meter_base); + if (numBreathWedges != 0) { + gSPDisplayList(gDisplayListHead++, &dl_breath_meter_health_segments_begin); + render_breath_meter_segment(numBreathWedges); + gSPDisplayList(gDisplayListHead++, &dl_breath_meter_health_segments_end); + } + gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); +} + +/** + * Breath meter animation called after emphasized mode. + * Moves breath meter y pos speed until it's visible. + */ +static void animate_breath_meter_sliding_in(void) { + approach_s16_symmetric_bool(&sBreathMeterHUD.y, HUD_BREATH_METER_Y, 5); + if (sBreathMeterHUD.y == HUD_BREATH_METER_Y) { + sBreathMeterHUD.animation = BREATH_METER_VISIBLE; + } +} + +/** + * Breath meter animation called when there's 8 health segments. + * Moves breath meter y pos quickly until it's hidden. + */ +static void animate_breath_meter_sliding_out(void) { + approach_s16_symmetric_bool(&sBreathMeterHUD.y, HUD_BREATH_METER_HIDDEN_Y, 20); + if (sBreathMeterHUD.y == HUD_BREATH_METER_HIDDEN_Y) { + sBreathMeterHUD.animation = BREATH_METER_HIDDEN; + } +} + +/** + * Handles breath meter actions depending of the health segments values. + */ +void handle_breath_meter_actions(s16 numBreathWedges) { + // Show breath meter if health is not full, less than 8 + if ((numBreathWedges < 8) && (sBreathMeterStoredValue == 8) && sBreathMeterHUD.animation == BREATH_METER_HIDDEN) { + sBreathMeterHUD.animation = BREATH_METER_SHOWING; + // sBreathMeterHUD.y = HUD_BREATH_METER_Y; + } + // Show breath meter if breath is full, has 8 + if ((numBreathWedges == 8) && (sBreathMeterStoredValue == 7)) sBreathMeterVisibleTimer = 0; + // After breath is full, hide breath meter + if ((numBreathWedges == 8) && (sBreathMeterVisibleTimer > 45)) sBreathMeterHUD.animation = BREATH_METER_HIDING; + // Update to match breath value + sBreathMeterStoredValue = numBreathWedges; + // If Mario is swimming, keep breath meter visible + if (gPlayerCameraState->action & ACT_FLAG_SWIMMING) { + if (sBreathMeterHUD.animation == BREATH_METER_HIDDEN) { + sBreathMeterHUD.animation = BREATH_METER_SHOWING; + } + sBreathMeterVisibleTimer = 0; + } +} + +void render_hud_breath_meter(void) { + s16 shownBreathAmount = gHudDisplay.breath; + if (sBreathMeterHUD.animation != BREATH_METER_HIDING) handle_breath_meter_actions(shownBreathAmount); + if (sBreathMeterHUD.animation == BREATH_METER_HIDDEN) return; + switch (sBreathMeterHUD.animation) { + case BREATH_METER_SHOWING: animate_breath_meter_sliding_in(); break; + case BREATH_METER_HIDING: animate_breath_meter_sliding_out(); break; + default: break; + } + render_dl_breath_meter(shownBreathAmount); + sBreathMeterVisibleTimer++; +} #endif +#define HUD_TOP_Y 209 + /** * Renders the amount of lives Mario has. */ @@ -326,31 +403,17 @@ void render_hud_coins(void) { print_text_fmt_int(198, HUD_TOP_Y, "%d", gHudDisplay.coins); } -#ifdef VERSION_JP -#define HUD_STARS_X 73 -#else #define HUD_STARS_X 78 -#endif /** * Renders the amount of stars collected. * Disables "X" glyph when Mario has 100 stars or more. */ void render_hud_stars(void) { - s8 showX = 0; - - if (gHudFlash == 1 && gGlobalTimer & 0x08) { - return; - } - - if (gHudDisplay.stars < 100) { - showX = 1; - } - + if ((gHudFlash == 1) && (gGlobalTimer & 0x08)) return; + s8 showX = (gHudDisplay.stars < 100); print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(HUD_STARS_X), HUD_TOP_Y, "-"); // 'Star' glyph - if (showX == 1) { - print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(HUD_STARS_X) + 16, HUD_TOP_Y, "*"); // 'X' glyph - } + if (showX) print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(HUD_STARS_X) + 16, HUD_TOP_Y, "*"); // 'X' glyph print_text_fmt_int((showX * 14) + GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(HUD_STARS_X - 16), HUD_TOP_Y, "%d", gHudDisplay.stars); } @@ -361,7 +424,6 @@ void render_hud_stars(void) { */ void render_hud_keys(void) { s16 i; - for (i = 0; i < gHudDisplay.keys; i++) { print_text((i * 16) + 220, 142, "/"); // unused glyph - beta key } @@ -371,31 +433,20 @@ void render_hud_keys(void) { * Renders the timer when Mario start sliding in PSS. */ void render_hud_timer(void) { - u8 *(*hudLUT)[58]; - u16 timerValFrames; - u16 timerMins; - u16 timerSecs; - u16 timerFracSecs; - + Texture *(*hudLUT)[58]; hudLUT = segmented_to_virtual(&main_hud_lut); - timerValFrames = gHudDisplay.timer; + u16 timerValFrames = gHudDisplay.timer; #ifdef VERSION_EU switch (eu_get_language()) { - case LANGUAGE_ENGLISH: - print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 185, "TIME"); - break; - case LANGUAGE_FRENCH: - print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(155), 185, "TEMPS"); - break; - case LANGUAGE_GERMAN: - print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 185, "ZEIT"); - break; + case LANGUAGE_ENGLISH: print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 185, "TIME"); break; + case LANGUAGE_FRENCH: print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(155), 185, "TEMPS"); break; + case LANGUAGE_GERMAN: print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 185, "ZEIT"); break; } #endif - timerMins = timerValFrames / (30 * 60); - timerSecs = (timerValFrames - (timerMins * 1800)) / 30; + u16 timerMins = timerValFrames / (30 * 60); + u16 timerSecs = (timerValFrames - (timerMins * 1800)) / 30; - timerFracSecs = ((timerValFrames - (timerMins * 1800) - (timerSecs * 30)) & 0xFFFF) / 3; + u16 timerFracSecs = ((timerValFrames - (timerMins * 1800) - (timerSecs * 30)) & 0xFFFF) / 3; #ifndef VERSION_EU print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 185, "TIME"); #endif @@ -421,13 +472,10 @@ void set_hud_camera_status(s16 status) { * the camera status called, a defined glyph is rendered. */ void render_hud_camera_status(void) { - u8 *(*cameraLUT)[6]; - s32 x; - s32 y; - + Texture *(*cameraLUT)[6]; cameraLUT = segmented_to_virtual(&main_hud_camera_lut); - x = GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(54); - y = 205; + s32 x = GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(54); + s32 y = 205; if (sCameraHUD.status == CAM_STATUS_NONE) { return; @@ -453,7 +501,7 @@ void render_hud_camera_status(void) { render_hud_small_tex_lut(x + 4, y + 16, (*cameraLUT)[GLYPH_CAM_ARROW_DOWN]); break; case CAM_STATUS_C_UP: - render_hud_small_tex_lut(x + 4, y - 8, (*cameraLUT)[GLYPH_CAM_ARROW_UP]); + render_hud_small_tex_lut(x + 4, y - 8, (*cameraLUT)[GLYPH_CAM_ARROW_UP]); break; } @@ -476,14 +524,16 @@ void render_hud(void) { sPowerMeterHUD.animation = POWER_METER_HIDDEN; sPowerMeterStoredHealth = 8; sPowerMeterVisibleTimer = 0; +#ifdef BREATH_METER + sBreathMeterHUD.animation = BREATH_METER_HIDDEN; + sBreathMeterStoredValue = 8; + sBreathMeterVisibleTimer = 0; +#endif } else { #ifdef VERSION_EU // basically create_dl_ortho_matrix but guOrtho screen width is different - mtx = alloc_display_list(sizeof(*mtx)); - if (mtx == NULL) { - return; - } + if (mtx == NULL) return; create_dl_identity_matrix(); guOrtho(mtx, -16.0f, SCREEN_WIDTH + 16, 0, SCREEN_HEIGHT, -10.0f, 10.0f, 1.0f); gSPPerspNormalize(gDisplayListHead++, 0xFFFF); @@ -492,7 +542,6 @@ void render_hud(void) { #else create_dl_ortho_matrix(); #endif - if (gCurrentArea != NULL && gCurrentArea->camera->mode == CAMERA_MODE_INSIDE_CANNON) { render_hud_cannon_reticle(); } @@ -501,38 +550,27 @@ void render_hud(void) { render_hud_mario_lives(); } #endif - if (hudDisplayFlags & HUD_DISPLAY_FLAG_COIN_COUNT) { - render_hud_coins(); - } - - if (hudDisplayFlags & HUD_DISPLAY_FLAG_STAR_COUNT) { - render_hud_stars(); - } - - if (hudDisplayFlags & HUD_DISPLAY_FLAG_KEYS) { - render_hud_keys(); - } + if (hudDisplayFlags & HUD_DISPLAY_FLAG_COIN_COUNT ) render_hud_coins(); + if (hudDisplayFlags & HUD_DISPLAY_FLAG_STAR_COUNT ) render_hud_stars(); + if (hudDisplayFlags & HUD_DISPLAY_FLAG_KEYS ) render_hud_keys(); +#ifdef BREATH_METER + if (hudDisplayFlags & HUD_DISPLAY_FLAG_BREATH_METER) render_hud_breath_meter(); +#endif if (hudDisplayFlags & HUD_DISPLAY_FLAG_CAMERA_AND_POWER) { render_hud_power_meter(); - #ifdef PUPPYCAM - if (!gPuppyCam.enabled) - #endif +#ifdef PUPPYCAM + if (!gPuppyCam.enabled) { +#endif render_hud_camera_status(); +#ifdef PUPPYCAM + } +#endif } + if (hudDisplayFlags & HUD_DISPLAY_FLAG_TIMER) render_hud_timer(); - if (hudDisplayFlags & HUD_DISPLAY_FLAG_TIMER) { - render_hud_timer(); - } - - if (gSurfacePoolError & NOT_ENOUGH_ROOM_FOR_SURFACES) - { - print_text(10, 40, "SURFACE POOL FULL"); - } - if (gSurfacePoolError & NOT_ENOUGH_ROOM_FOR_NODES) - { - print_text(10, 60, "SURFACE NODE POOL FULL"); - } + if (gSurfacePoolError & NOT_ENOUGH_ROOM_FOR_SURFACES) print_text(10, 40, "SURFACE POOL FULL"); + if (gSurfacePoolError & NOT_ENOUGH_ROOM_FOR_NODES ) print_text(10, 60, "SURFACE NODE POOL FULL"); #ifdef CUSTOM_DEBUG if (gCustomDebugMode) { render_debug_mode(); diff --git a/src/game/hud.h b/src/game/hud.h index bfa44fa5..11586693 100644 --- a/src/game/hud.h +++ b/src/game/hud.h @@ -12,6 +12,15 @@ enum PowerMeterAnimation { POWER_METER_VISIBLE }; +#ifdef BREATH_METER +enum AirMeterAnimation { + BREATH_METER_HIDDEN, + BREATH_METER_SHOWING, + BREATH_METER_HIDING, + BREATH_METER_VISIBLE +}; +#endif + enum CameraHUDLut { GLYPH_CAM_CAMERA, GLYPH_CAM_MARIO_HEAD, diff --git a/src/game/interaction.c b/src/game/interaction.c index cf82c5c5..8dfef56a 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -736,13 +736,15 @@ void reset_mario_pitch(struct MarioState *m) { } } -u32 interact_coin(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { - m->numCoins += o->oDamageOrCoinValue; - m->healCounter += 4 * o->oDamageOrCoinValue; - - o->oInteractStatus = INT_STATUS_INTERACTED; +u32 interact_coin(struct MarioState *m, UNUSED u32 interactType, struct Object *obj) { + m->numCoins += obj->oDamageOrCoinValue; + m->healCounter += 4 * obj->oDamageOrCoinValue; +#ifdef BREATH_METER + m->breathCounter += (4 * obj->oDamageOrCoinValue); +#endif + obj->oInteractStatus = INT_STATUS_INTERACTED; #ifdef X_COIN_STAR - if (COURSE_IS_MAIN_COURSE(gCurrCourseNum) && m->numCoins - o->oDamageOrCoinValue < X_COIN_STAR + if (COURSE_IS_MAIN_COURSE(gCurrCourseNum) && m->numCoins - obj->oDamageOrCoinValue < X_COIN_STAR && m->numCoins >= X_COIN_STAR) { bhv_spawn_star_no_level_exit(6); } @@ -752,13 +754,16 @@ u32 interact_coin(struct MarioState *m, UNUSED u32 interactType, struct Object * queue_rumble_data(5, 80); } #endif - return FALSE; } -u32 interact_water_ring(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { - m->healCounter += 4 * o->oDamageOrCoinValue; - o->oInteractStatus = INT_STATUS_INTERACTED; +u32 interact_water_ring(struct MarioState *m, UNUSED u32 interactType, struct Object *obj) { +#ifdef BREATH_METER + m->breathCounter += (4 * obj->oDamageOrCoinValue); +#else + m->healCounter += (4 * obj->oDamageOrCoinValue); +#endif + obj->oInteractStatus = INT_STATUS_INTERACTED; return FALSE; } @@ -781,6 +786,9 @@ u32 interact_star_or_key(struct MarioState *m, UNUSED u32 interactType, struct O if (!noExit) { m->hurtCounter = 0; m->healCounter = 0; +#ifdef BREATH_METER + m->breathCounter = 0; +#endif if (m->capTimer > 1) { m->capTimer = 1; } diff --git a/src/game/level_update.c b/src/game/level_update.c index 840d65de..895868c6 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -904,8 +904,10 @@ void initiate_delayed_warp(void) { void update_hud_values(void) { if (gCurrCreditsEntry == NULL) { - s16 numHealthWedges = gMarioState->health > 0 ? gMarioState->health >> 8 : 0; - + s16 numHealthWedges = ((gMarioState->health > 0) ? (gMarioState->health >> 8) : 0); +#ifdef BREATH_METER + s16 numBreathWedges = ((gMarioState->breath > 0) ? (gMarioState->breath >> 8) : 0); +#endif if (gCurrCourseNum >= COURSE_MIN) { gHudDisplay.flags |= HUD_DISPLAY_FLAG_COIN_COUNT; } else { @@ -946,12 +948,19 @@ void update_hud_values(void) { play_sound(SOUND_MENU_POWER_METER, gGlobalSoundSource); } gHudDisplay.wedges = numHealthWedges; - if (gMarioState->hurtCounter > 0) { gHudDisplay.flags |= HUD_DISPLAY_FLAG_EMPHASIZE_POWER; } else { gHudDisplay.flags &= ~HUD_DISPLAY_FLAG_EMPHASIZE_POWER; } +#ifdef BREATH_METER + gHudDisplay.breath = numBreathWedges; + if (gMarioState->breath > 0) { + gHudDisplay.flags |= HUD_DISPLAY_FLAG_BREATH_METER; + } else { + gHudDisplay.flags &= ~HUD_DISPLAY_FLAG_BREATH_METER; + } +#endif } } diff --git a/src/game/level_update.h b/src/game/level_update.h index a8bd0e3c..440f2776 100644 --- a/src/game/level_update.h +++ b/src/game/level_update.h @@ -107,6 +107,9 @@ struct HudDisplay { /*0x08*/ s16 keys; /*0x0A*/ s16 flags; /*0x0C*/ u16 timer; +#ifdef BREATH_METER + u16 breath; +#endif }; extern struct HudDisplay gHudDisplay; @@ -120,6 +123,9 @@ enum HUDDisplayFlag { HUD_DISPLAY_FLAG_KEYS = (1 << 4), // 0x0010 HUD_DISPLAY_FLAG_UNKNOWN_0020 = (1 << 5), // 0x0020 HUD_DISPLAY_FLAG_TIMER = (1 << 6), // 0x0040 +#ifdef BREATH_METER + HUD_DISPLAY_FLAG_BREATH_METER = (1 << 14), // 0x4000 +#endif HUD_DISPLAY_FLAG_EMPHASIZE_POWER = (1 << 15), // 0x8000 HUD_DISPLAY_NONE = (0 << 0), // 0x0000 diff --git a/src/game/mario.c b/src/game/mario.c index 7cef3c9d..61a4fe01 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -1483,7 +1483,10 @@ void update_mario_health(struct MarioState *m) { } else { if ((m->action & ACT_FLAG_SWIMMING) && !(m->action & ACT_FLAG_INTANGIBLE)) { terrainIsSnow = (m->area->terrainType & TERRAIN_MASK) == TERRAIN_SNOW; - +#ifdef BREATH_METER + // when in snow terrains lose 3 health. + if ((m->pos[1] < (m->waterLevel - 140)) && terrainIsSnow) m->health -= 3; +#else // When Mario is near the water surface, recover health (unless in snow), // when in snow terrains lose 3 health. // If using the debug level select, do not lose any HP to water. @@ -1492,6 +1495,7 @@ void update_mario_health(struct MarioState *m) { } else if (!gDebugLevelSelect) { m->health -= (terrainIsSnow ? 3 : 1); } +#endif } } } @@ -1511,7 +1515,7 @@ void update_mario_health(struct MarioState *m) { if (m->health < 0x100) { m->health = 0xFF; } - +#ifndef BREATH_METER // Play a noise to alert the player when Mario is close to drowning. if (((m->action & ACT_GROUP_MASK) == ACT_GROUP_SUBMERGED) && (m->health < 0x300)) { play_sound(SOUND_MOVING_ALMOST_DROWNING, gGlobalSoundSource); @@ -1526,9 +1530,43 @@ void update_mario_health(struct MarioState *m) { gRumblePakTimer = 0; #endif } +#endif } } +#ifdef BREATH_METER +void update_mario_breath(struct MarioState *m) { + if (m->breath >= 0x100 && m->health >= 0x100) { + if (m->pos[1] < (m->waterLevel - 140) && !(m->flags & MARIO_METAL_CAP) && !(m->action & ACT_FLAG_INTANGIBLE)) { + m->breath--; + if (m->breath < 0x300) { + // Play a noise to alert the player when Mario is close to drowning. + play_sound(SOUND_MOVING_ALMOST_DROWNING, gGlobalSoundSource); +#if ENABLE_RUMBLE + if (gRumblePakTimer == 0) { + gRumblePakTimer = 36; + if (is_rumble_finished_and_queue_empty()) queue_rumble_data(3, 30); + } + } else { + gRumblePakTimer = 0; +#endif + } + } else if (!(m->input & INPUT_IN_POISON_GAS)) { + m->breath += 0x1A; + } + if (m->breathCounter > 0) { + m->breath += 0x40; + m->breathCounter--; + } + if (m->breath > 0x880) m->breath = 0x880; + if (m->breath < 0x100) { + m->breath = 0xFF; + m->health = 0xFF; + } + } +} +#endif + /** * Updates some basic info for camera usage. */ @@ -1772,6 +1810,9 @@ s32 execute_mario_action(UNUSED struct Object *o) { squish_mario_model(gMarioState); set_submerged_cam_preset_and_spawn_bubbles(gMarioState); update_mario_health(gMarioState); +#ifdef BREATH_METER + update_mario_breath(gMarioState); +#endif update_mario_info_for_cam(gMarioState); mario_update_hitbox_and_cap_model(gMarioState); @@ -1900,7 +1941,10 @@ void init_mario_from_save_file(void) { gMarioState->numLives = 4; gMarioState->health = 0x880; - +#ifdef BREATH_METER + gMarioState->breath = 0x880; + gHudDisplay.breath = 8; +#endif gMarioState->prevNumStarsForDialog = gMarioState->numStars; gMarioState->animYTrans = 0xBD; diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c index a10ed013..cef67c53 100644 --- a/src/game/mario_actions_cutscene.c +++ b/src/game/mario_actions_cutscene.c @@ -742,6 +742,9 @@ s32 act_eaten_by_bubba(struct MarioState *m) { play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED); set_mario_animation(m, MARIO_ANIM_A_POSE); m->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; +#ifdef BREATH_METER + m->breath = 0xFF; +#endif m->health = 0xFF; if (m->actionTimer++ == 60) { level_trigger_warp(m, WARP_OP_DEATH); @@ -1038,6 +1041,9 @@ s32 act_spawn_spin_landing(struct MarioState *m) { * particle flag that generates sparkles. */ s32 act_exit_airborne(struct MarioState *m) { +#ifdef BREATH_METER + m->breath = 0x880; +#endif if (15 < m->actionTimer++ && launch_mario_until_land(m, ACT_EXIT_LAND_SAVE_DIALOG, MARIO_ANIM_GENERAL_FALL, -32.0f)) { // heal Mario @@ -1050,6 +1056,9 @@ s32 act_exit_airborne(struct MarioState *m) { } s32 act_falling_exit_airborne(struct MarioState *m) { +#ifdef BREATH_METER + m->breath = 0x880; +#endif if (launch_mario_until_land(m, ACT_EXIT_LAND_SAVE_DIALOG, MARIO_ANIM_GENERAL_FALL, 0.0f)) { // heal Mario m->healCounter = 31; @@ -1144,8 +1153,10 @@ s32 act_exit_land_save_dialog(struct MarioState *m) { } s32 act_death_exit(struct MarioState *m) { - if (15 < m->actionTimer++ - && launch_mario_until_land(m, ACT_DEATH_EXIT_LAND, MARIO_ANIM_GENERAL_FALL, -32.0f)) { +#ifdef BREATH_METER + m->breath = 0x880; +#endif + if (15 < m->actionTimer++ && launch_mario_until_land(m, ACT_DEATH_EXIT_LAND, MARIO_ANIM_GENERAL_FALL, -32.0f)) { #ifdef VERSION_JP play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); #else @@ -1164,6 +1175,9 @@ s32 act_death_exit(struct MarioState *m) { } s32 act_unused_death_exit(struct MarioState *m) { +#ifdef BREATH_METER + m->breath = 0x880; +#endif if (launch_mario_until_land(m, ACT_FREEFALL_LAND_STOP, MARIO_ANIM_GENERAL_FALL, 0.0f)) { #ifdef VERSION_JP play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); @@ -1180,6 +1194,9 @@ s32 act_unused_death_exit(struct MarioState *m) { } s32 act_falling_death_exit(struct MarioState *m) { +#ifdef BREATH_METER + m->breath = 0x880; +#endif if (launch_mario_until_land(m, ACT_DEATH_EXIT_LAND, MARIO_ANIM_GENERAL_FALL, 0.0f)) { #ifdef VERSION_JP play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); @@ -1200,6 +1217,9 @@ s32 act_falling_death_exit(struct MarioState *m) { // waits 11 frames before actually executing, also has reduced fvel s32 act_special_exit_airborne(struct MarioState *m) { +#ifdef BREATH_METER + m->breath = 0x880; +#endif struct Object *marioObj = m->marioObj; play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED); @@ -1225,6 +1245,9 @@ s32 act_special_exit_airborne(struct MarioState *m) { } s32 act_special_death_exit(struct MarioState *m) { +#ifdef BREATH_METER + m->breath = 0x880; +#endif struct Object *marioObj = m->marioObj; if (m->actionTimer++ < 11) { diff --git a/src/game/obj_behaviors_2.c b/src/game/obj_behaviors_2.c index 74c3b7a2..ddd945b0 100644 --- a/src/game/obj_behaviors_2.c +++ b/src/game/obj_behaviors_2.c @@ -452,7 +452,7 @@ static s32 obj_smooth_turn(s16 *angleVel, s32 *angle, s16 targetAngle, f32 targe currentSpeed = absi(*angleVel); clamp_s16(¤tSpeed, minSpeed, maxSpeed); - *angle = approach_s16_symmetric(*angle, targetAngle, currentSpeed); + *angle = approach_angle(*angle, targetAngle, currentSpeed); return (s16)(*angle) == targetAngle; } diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index fcc81a9a..c47b212e 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -282,88 +282,15 @@ void cur_obj_forward_vel_approach_upward(f32 target, f32 increment) { } } -s32 approach_f32_signed(f32 *value, f32 target, f32 increment) { - s32 reachedTarget = FALSE; - - *value += increment; - - if (increment >= 0.0f) { - if (*value > target) { - *value = target; - reachedTarget = TRUE; - } - } else { - if (*value < target) { - *value = target; - reachedTarget = TRUE; - } - } - - return reachedTarget; -} - -f32 approach_f32_symmetric(f32 value, f32 target, f32 increment) { - f32 dist; - - if ((dist = target - value) >= 0.0f) { - if (dist > increment) { - value += increment; - } else { - value = target; - } - } else { - if (dist < -increment) { - value -= increment; - } else { - value = target; - } - } - - return value; -} - -s16 approach_s16_symmetric(s16 value, s16 target, s16 increment) { - s16 dist = target - value; - - if (dist >= 0) { - if (dist > increment) { - value += increment; - } else { - value = target; - } - } else { - if (dist < -increment) { - value -= increment; - } else { - value = target; - } - } - - return value; -} - s32 cur_obj_rotate_yaw_toward(s16 target, s16 increment) { - s16 startYaw; - - startYaw = (s16) o->oMoveAngleYaw; + s16 startYaw = (s16) o->oMoveAngleYaw; o->oMoveAngleYaw = approach_s16_symmetric(o->oMoveAngleYaw, target, increment); - if ((o->oAngleVelYaw = (s16)((s16) o->oMoveAngleYaw - startYaw)) == 0) { - return TRUE; - } else { - return FALSE; - } + return ((o->oAngleVelYaw = (s16)((s16) o->oMoveAngleYaw - startYaw)) == 0); } s16 obj_angle_to_object(struct Object *obj1, struct Object *obj2) { - f32 z1, x1, z2, x2; - s16 angle; - - z1 = obj1->oPosZ; z2 = obj2->oPosZ; // ordering of instructions.. - x1 = obj1->oPosX; x2 = obj2->oPosX; - - angle = atan2s(z2 - z1, x2 - x1); - return angle; + return atan2s((obj2->oPosZ - obj1->oPosZ), (obj2->oPosX - obj1->oPosX)); } s16 obj_turn_toward_object(struct Object *obj, struct Object *target, s16 angleIndex, s16 turnAmount) { @@ -376,12 +303,12 @@ s16 obj_turn_toward_object(struct Object *obj, struct Object *target, s16 angleI case O_FACE_ANGLE_PITCH_INDEX: a = target->oPosX - obj->oPosX; c = target->oPosZ - obj->oPosZ; - a = sqrtf(a * a + c * c); + a = sqrtf(sqr(a) + sqr(c)); b = -obj->oPosY; d = -target->oPosY; - targetAngle = atan2s(a, d - b); + targetAngle = atan2s(a, (d - b)); break; case O_MOVE_ANGLE_YAW_INDEX: @@ -391,7 +318,7 @@ s16 obj_turn_toward_object(struct Object *obj, struct Object *target, s16 angleI b = obj->oPosX; d = target->oPosX; - targetAngle = atan2s(c - a, d - b); + targetAngle = atan2s((c - a), (d - b)); break; } @@ -401,25 +328,16 @@ s16 obj_turn_toward_object(struct Object *obj, struct Object *target, s16 angleI } void obj_set_parent_relative_pos(struct Object *obj, s16 relX, s16 relY, s16 relZ) { - obj->oParentRelativePosX = relX; - obj->oParentRelativePosY = relY; - obj->oParentRelativePosZ = relZ; + vec3_set(&obj->oParentRelativePosVec, relX, relY, relZ); } void obj_set_pos(struct Object *obj, s16 x, s16 y, s16 z) { - obj->oPosX = x; - obj->oPosY = y; - obj->oPosZ = z; + vec3_set(&obj->oPosVec, x, y, z); } void obj_set_angle(struct Object *obj, s16 pitch, s16 yaw, s16 roll) { - obj->oFaceAnglePitch = pitch; - obj->oFaceAngleYaw = yaw; - obj->oFaceAngleRoll = roll; - - obj->oMoveAnglePitch = pitch; - obj->oMoveAngleYaw = yaw; - obj->oMoveAngleRoll = roll; + vec3_set(&obj->oFaceAngleVec, pitch, yaw, roll); + vec3_set(&obj->oMoveAngleVec, pitch, yaw, roll); } /* @@ -453,7 +371,7 @@ struct Object *spawn_object_rel_with_rot(struct Object *parent, u32 model, const struct Object *spawn_obj_with_transform_flags(struct Object *parent, s32 model, const BehaviorScript *behavior) { struct Object *newObj = spawn_object(parent, model, behavior); - newObj->oFlags |= OBJ_FLAG_UPDATE_TRANSFORM_FOR_THROW_MATRIX | OBJ_FLAG_SET_THROW_MATRIX_FROM_TRANSFORM; + newObj->oFlags |= (OBJ_FLAG_UPDATE_TRANSFORM_FOR_THROW_MATRIX | OBJ_FLAG_SET_THROW_MATRIX_FROM_TRANSFORM); return newObj; } @@ -508,8 +426,7 @@ struct Object *spawn_object_at_origin(struct Object *parent, UNUSED s32 unusedAr obj->header.gfx.areaIndex = parent->header.gfx.areaIndex; obj->header.gfx.activeAreaIndex = parent->header.gfx.areaIndex; - geo_obj_init((struct GraphNodeObject *) &obj->header.gfx, gLoadedGraphNodes[model], gVec3fZero, - gVec3sZero); + geo_obj_init((struct GraphNodeObject *) &obj->header.gfx, gLoadedGraphNodes[model], gVec3fZero, gVec3sZero); return obj; } @@ -575,9 +492,7 @@ struct Object *spawn_object_relative_with_scale(s16 behaviorParam, s16 relativeP } void cur_obj_move_using_vel(void) { - o->oPosX += o->oVelX; - o->oPosY += o->oVelY; - o->oPosZ += o->oVelZ; + vec3f_add(&o->oPosVec, &o->oVelVec); } void obj_copy_graph_y_offset(struct Object *dst, struct Object *src) { @@ -590,25 +505,16 @@ void obj_copy_pos_and_angle(struct Object *dst, struct Object *src) { } void obj_copy_pos(struct Object *dst, struct Object *src) { - dst->oPosX = src->oPosX; - dst->oPosY = src->oPosY; - dst->oPosZ = src->oPosZ; + vec3f_copy(&dst->oPosVec, &src->oPosVec); } void obj_copy_angle(struct Object *dst, struct Object *src) { - dst->oMoveAnglePitch = src->oMoveAnglePitch; - dst->oMoveAngleYaw = src->oMoveAngleYaw; - dst->oMoveAngleRoll = src->oMoveAngleRoll; - - dst->oFaceAnglePitch = src->oFaceAnglePitch; - dst->oFaceAngleYaw = src->oFaceAngleYaw; - dst->oFaceAngleRoll = src->oFaceAngleRoll; + vec3_copy(&dst->oMoveAngleVec, &src->oMoveAngleVec); + vec3_copy(&dst->oFaceAngleVec, &src->oFaceAngleVec); } void obj_set_gfx_pos_from_pos(struct Object *obj) { - obj->header.gfx.pos[0] = obj->oPosX; - obj->header.gfx.pos[1] = obj->oPosY; - obj->header.gfx.pos[2] = obj->oPosZ; + vec3f_copy(obj->header.gfx.pos, &obj->oPosVec); } void obj_init_animation(struct Object *obj, s32 animIndex) { diff --git a/src/game/object_helpers.h b/src/game/object_helpers.h index 7040dcfa..72fcbb9b 100644 --- a/src/game/object_helpers.h +++ b/src/game/object_helpers.h @@ -69,9 +69,6 @@ void obj_set_held_state(struct Object *obj, const BehaviorScript *heldBehavior); f32 lateral_dist_between_objects(struct Object *obj1, struct Object *obj2); f32 dist_between_objects(struct Object *obj1, struct Object *obj2); void cur_obj_forward_vel_approach_upward(f32 target, f32 increment); -s32 approach_f32_signed(f32 *value, f32 target, f32 increment); -f32 approach_f32_symmetric(f32 value, f32 target, f32 increment); -s16 approach_s16_symmetric(s16 value, s16 target, s16 increment); s32 cur_obj_rotate_yaw_toward(s16 target, s16 increment); s16 obj_angle_to_object(struct Object *obj1, struct Object *obj2); s16 obj_turn_toward_object(struct Object *obj, struct Object *target, s16 angleIndex, s16 turnAmount);