From ce0ad0eff00ab51e1cb8e86c8a0d4987474dc410 Mon Sep 17 00:00:00 2001 From: Zoey Zolotova Date: Tue, 25 Feb 2025 00:47:37 +1100 Subject: [PATCH] Add course timer setting. --- bin/segment2.c | 6 ++- src/game/hud.c | 9 +++++ src/game/level_update.c | 31 ++++++++++++++ src/game/level_update.h | 4 +- src/game/mario.c | 13 ++++++ src/game/print.c | 4 ++ src/game/print.h | 1 + src/game/randomizer.c | 12 +++--- src/game/randomizer.h | 3 +- src/game/save_file.h | 5 ++- src/game/segment2.h | 2 +- src/menu/file_select.c | 38 +++++++++++++++++- textures/segment2/custom_HUD_Clock.rgba16.png | Bin 0 -> 889 bytes 13 files changed, 115 insertions(+), 13 deletions(-) create mode 100644 textures/segment2/custom_HUD_Clock.rgba16.png diff --git a/bin/segment2.c b/bin/segment2.c index 45ffd866..2409c0f6 100644 --- a/bin/segment2.c +++ b/bin/segment2.c @@ -235,6 +235,10 @@ ALIGNED8 static const Texture texture_hud_char_umlaut[] = { }; #endif +ALIGNED8 static const Texture texture_hud_char_clock[] = { +#include "textures/segment2/custom_HUD_Clock.rgba16.inc.c" +}; + #if defined(VERSION_JP) || defined(VERSION_SH) || defined(COMPLETE_EN_US_SEGMENT2) ALIGNED8 static const Texture texture_hud_char_exclamation[] = { #include "textures/segment2/segment2.04C00.rgba16.inc.c"// JP ! @@ -1916,7 +1920,7 @@ const Texture *const main_hud_lut[] = { 0x0, 0x0, 0x0, texture_hud_char_minus, texture_hud_char_multiply, texture_hud_char_coin, texture_hud_char_red_coin, texture_hud_char_silver_coin, texture_hud_char_mario_head, texture_hud_char_star, texture_hud_char_decimal_point, texture_hud_char_beta_key, - texture_hud_char_apostrophe, texture_hud_char_double_quote, texture_hud_char_umlaut, + texture_hud_char_apostrophe, texture_hud_char_double_quote, texture_hud_char_umlaut, texture_hud_char_clock, }; // Main small font print table 0x02008338-0x02008737 diff --git a/src/game/hud.c b/src/game/hud.c index 10fc7704..3dd2df4b 100644 --- a/src/game/hud.c +++ b/src/game/hud.c @@ -400,6 +400,11 @@ void render_hud_mario_lives(void) { print_text_fmt_int(GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(54), HUD_TOP_Y, "%d", gHudDisplay.lives); } +void render_hud_course_timer(void) { + print_text(GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(22), HUD_TOP_Y, "="); // 'Clock' glyph + print_text_fmt_int(GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(39), HUD_TOP_Y, "%d", gHudDisplay.courseTimer); +} + #ifdef VANILLA_STYLE_CUSTOM_DEBUG void render_debug_mode(void) { print_text(180, 40, "DEBUG MODE"); @@ -568,6 +573,10 @@ void render_hud(void) { // } #endif + if (hudDisplayFlags & HUD_DISPLAY_COURSE_TIMER && gHudDisplay.courseTimer >= 0 && gOptionsSettings.gameplay.s.courseTimer) { + render_hud_course_timer(); + } + if (hudDisplayFlags & HUD_DISPLAY_FLAG_COIN_COUNT) { render_hud_coins(); } diff --git a/src/game/level_update.c b/src/game/level_update.c index a94ace65..dae4ceda 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -486,14 +486,38 @@ void warp_area(void) { } } +static u16 sBonusCourseTimers[] = {0, 100, 200, 400}; +static u16 sCourseTimers[] = {0, 300, 500, 999}; + void course_check_lock(s8 prevCourse, s8 currCourse) { + u16 seconds = currCourse > COURSE_STAGES_MAX + ? sBonusCourseTimers[gOptionsSettings.gameplay.s.courseTimer] + : sCourseTimers[gOptionsSettings.gameplay.s.courseTimer]; if (prevCourse == COURSE_NONE && currCourse != COURSE_NONE) { if (currCourse != curFile.lastVisitedCourse) { + curFile.curCourseTimer = seconds * COURSE_TIMER_FACTOR; set_course_is_locked(curFile.lastVisitedCourse); } curFile.lastVisitedCourse = currCourse; save_file_do_save(gCurrSaveFileNum - 1); } + + // Going into the HMC sub-level + if (prevCourse == COURSE_HMC && currCourse != COURSE_HMC && currCourse != COURSE_NONE) { + u16 temp = curFile.hmcStoredCourseTimer; + curFile.hmcStoredCourseTimer = curFile.curCourseTimer; + curFile.curCourseTimer = temp; + if (curFile.curCourseTimer == 0) { + curFile.curCourseTimer = seconds * COURSE_TIMER_FACTOR; + } + } + + // Leaving the HMC sub-level + if (currCourse == COURSE_HMC && prevCourse != COURSE_HMC && prevCourse != COURSE_NONE) { + u16 temp = curFile.hmcStoredCourseTimer; + curFile.hmcStoredCourseTimer = curFile.curCourseTimer; + curFile.curCourseTimer = temp; + } } // used for warps between levels @@ -1034,6 +1058,13 @@ void update_hud_values(void) { gHudDisplay.stars = gMarioState->numStars; gHudDisplay.lives = gMarioState->numLives; gHudDisplay.keys = gMarioState->numKeys; + if (numHealthWedges) { + if (gCurrCourseNum != COURSE_NONE && !(IS_120_STAR && gMarioState->numStars >= CAP_SWITCH_THRESHOLD)) { + gHudDisplay.courseTimer = (curFile.curCourseTimer + COURSE_TIMER_FACTOR - 1) / COURSE_TIMER_FACTOR; + } else { + gHudDisplay.courseTimer = -1; + } + } if (numHealthWedges > gHudDisplay.wedges) { play_sound(SOUND_MENU_POWER_METER, gGlobalSoundSource); diff --git a/src/game/level_update.h b/src/game/level_update.h index 5f33ff6e..b63fe7c9 100644 --- a/src/game/level_update.h +++ b/src/game/level_update.h @@ -116,6 +116,7 @@ struct HudDisplay { /*0x08*/ s16 keys; /*0x0A*/ s16 flags; /*0x0C*/ u16 timer; + s16 courseTimer; #ifdef BREATH_METER u16 breath; #endif @@ -133,13 +134,14 @@ enum HUDDisplayFlag { HUD_DISPLAY_FLAG_KEYS = (1 << 4), // 0x0010 HUD_DISPLAY_FLAG_UNKNOWN_0020 = (1 << 5), // 0x0020 HUD_DISPLAY_FLAG_TIMER = (1 << 6), // 0x0040 + HUD_DISPLAY_COURSE_TIMER = (1 << 7), // 0x0080 #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 - HUD_DISPLAY_DEFAULT = (HUD_DISPLAY_FLAG_LIVES | HUD_DISPLAY_FLAG_COIN_COUNT | HUD_DISPLAY_FLAG_STAR_COUNT | HUD_DISPLAY_FLAG_CAMERA_AND_POWER | HUD_DISPLAY_FLAG_KEYS | HUD_DISPLAY_FLAG_UNKNOWN_0020) + HUD_DISPLAY_DEFAULT = (HUD_DISPLAY_FLAG_LIVES | HUD_DISPLAY_FLAG_COIN_COUNT | HUD_DISPLAY_FLAG_STAR_COUNT | HUD_DISPLAY_FLAG_CAMERA_AND_POWER | HUD_DISPLAY_FLAG_KEYS | HUD_DISPLAY_FLAG_UNKNOWN_0020 | HUD_DISPLAY_COURSE_TIMER) }; enum PlayModes { diff --git a/src/game/mario.c b/src/game/mario.c index ebf2ec1f..0632ebdf 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -1714,12 +1714,25 @@ u8 sDemonTimer = 0; u8 testSeq = SEQ_EVENT_MERRY_GO_ROUND; #endif +#include "buffers/buffers.h" +#define curFile gSaveBuffer.files[gCurrSaveFileNum - 1] + /** * Main function for executing Mario's behavior. Returns particleFlags. */ s32 execute_mario_action(UNUSED struct Object *obj) { s32 inLoop = TRUE; + if (!(gMarioState->action & ACT_FLAG_INTANGIBLE) && curFile.curCourseTimer > 0 && gCurrCourseNum != COURSE_NONE && !(IS_120_STAR && gMarioState->numStars >= CAP_SWITCH_THRESHOLD)) { + curFile.curCourseTimer--; + + if (curFile.curCourseTimer == 0 && gMarioState->health > 0x100) { + gHudDisplay.courseTimer = 0; + gMarioState->health = 0xFF; + save_file_erase(gCurrSaveFileNum - 1); + } + } + #ifdef BOWSER_3_TEST if (gPlayer1Controller->buttonPressed & L_TRIG) { initiate_warp(LEVEL_BOWSER_3, 1, 0x0A, 0); diff --git a/src/game/print.c b/src/game/print.c index 53fd8c90..f2094e61 100644 --- a/src/game/print.c +++ b/src/game/print.c @@ -356,6 +356,10 @@ s32 char_to_glyph_index(char c) { return GLYPH_BETA_KEY; // beta key, JP only. Reused for Ü in EU. } + if (c == '=') { + return GLYPH_CLOCK; + } + return GLYPH_SPACE; } diff --git a/src/game/print.h b/src/game/print.h index e4d4f2b2..bd73df1d 100644 --- a/src/game/print.h +++ b/src/game/print.h @@ -28,6 +28,7 @@ enum PrintfGlyphs { GLYPH_APOSTROPHE = 56, GLYPH_DOUBLE_QUOTE = 57, GLYPH_UMLAUT = 58, + GLYPH_CLOCK = 59, }; void print_text_fmt_int(s32 x, s32 y, const char *str, s32 n); diff --git a/src/game/randomizer.c b/src/game/randomizer.c index 42e0f72f..7fa707b4 100644 --- a/src/game/randomizer.c +++ b/src/game/randomizer.c @@ -241,12 +241,12 @@ s32 curPreset = 0; // }; struct OptionsSettings gPresets[6] = { - {{{0, /* pad */ 0, 1, 1, 1, 1, 0, 2, 1, 1, 1, 7, 0}}, {{2, 1, 1, 1, 2, 0, 1}}}, - {{{0, /* pad */ 0, 1, 1, 1, 1, 0, 2, 1, 1, 1, 10, 0}}, {{2, 1, 1, 1, 2, 0, 1}}}, - {{{0, /* pad */ 0, 1, 1, 1, 1, 0, 2, 1, 1, 1, 12, 0}}, {{2, 1, 1, 1, 2, 0, 1}}}, - {{{0, /* pad */ 1, 1, 1, 1, 1, 0, 2, 1, 1, 1, 7, 0}}, {{2, 1, 1, 1, 2, 0, 1}}}, - {{{0, /* pad */ 1, 1, 1, 1, 1, 0, 2, 1, 1, 1, 10, 0}}, {{2, 1, 1, 1, 2, 0, 1}}}, - {{{0, /* pad */ 1, 1, 1, 1, 1, 0, 2, 1, 1, 1, 12, 0}}, {{2, 1, 1, 1, 2, 0, 1}}}, + {{{0, /* pad */ 0, 0, 1, 1, 1, 1, 0, 2, 1, 1, 1, 7, 0}}, {{2, 1, 1, 1, 2, 0, 1}}}, + {{{0, /* pad */ 0, 0, 1, 1, 1, 1, 0, 2, 1, 1, 1, 10, 0}}, {{2, 1, 1, 1, 2, 0, 1}}}, + {{{0, /* pad */ 0, 0, 1, 1, 1, 1, 0, 2, 1, 1, 1, 12, 0}}, {{2, 1, 1, 1, 2, 0, 1}}}, + {{{0, /* pad */ 0, 1, 1, 1, 1, 1, 0, 2, 1, 1, 1, 7, 0}}, {{2, 1, 1, 1, 2, 0, 1}}}, + {{{0, /* pad */ 0, 1, 1, 1, 1, 1, 0, 2, 1, 1, 1, 10, 0}}, {{2, 1, 1, 1, 2, 0, 1}}}, + {{{0, /* pad */ 0, 1, 1, 1, 1, 1, 0, 2, 1, 1, 1, 12, 0}}, {{2, 1, 1, 1, 2, 0, 1}}}, }; unsigned char textVersion2[] = { TEXT_CURR_VERSION }; diff --git a/src/game/randomizer.h b/src/game/randomizer.h index 241809d0..31025c35 100644 --- a/src/game/randomizer.h +++ b/src/game/randomizer.h @@ -80,8 +80,9 @@ struct nodeInfo { struct OptionsSettings { union { struct { - u32 padding:13; // to make sure settings ids are small + u32 padding:11; // to make sure settings ids are small + u32 courseTimer:2; u32 hardcoreIronmario:1; u32 keepStructure:1; u32 randomLevelWarp:1; diff --git a/src/game/save_file.h b/src/game/save_file.h index 6cd4ad77..cad35062 100644 --- a/src/game/save_file.h +++ b/src/game/save_file.h @@ -20,6 +20,8 @@ #define NUM_SAVE_FILES 4 +#define COURSE_TIMER_FACTOR 20 + struct SaveBlockSignature { u16 magic; u16 chksum; @@ -51,7 +53,8 @@ struct SaveFile { u8 courseCoinScores[COURSE_STAGES_COUNT]; // 120 bits - u32 pad; + u16 curCourseTimer; + u16 hmcStoredCourseTimer; struct SaveBlockSignature signature; // 32 bits }; diff --git a/src/game/segment2.h b/src/game/segment2.h index 4075bc04..9787e810 100644 --- a/src/game/segment2.h +++ b/src/game/segment2.h @@ -45,7 +45,7 @@ extern Gfx dl_paintings_env_mapped_begin[]; extern Gfx dl_paintings_env_mapped_end[]; extern s16 seg2_painting_triangle_mesh[]; extern s16 seg2_painting_mesh_neighbor_tris[]; -extern Texture *main_hud_lut[58]; +extern Texture *main_hud_lut[59]; extern Gfx dl_hud_img_load_tex_block[]; extern Gfx dl_hud_img_load_tex_block_24[]; extern Gfx dl_hud_img_begin[]; diff --git a/src/menu/file_select.c b/src/menu/file_select.c index f5b2b116..91da0a0e 100644 --- a/src/menu/file_select.c +++ b/src/menu/file_select.c @@ -220,6 +220,7 @@ u8 OptionPage = 3; #define textCountPresets (sizeof(textsPresets) / 4) u8 gStarDoorReqLUT[] = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 119}; +u16 gCourseTimerLUT[] = {0, 300, 500, 999}; unsigned char *pages[] = { textAestheticOptions, textObjOptions, textWarpOptions, testPresets, textGPMOptions, textIronmarioOptions }; @@ -2442,18 +2443,31 @@ char *textsIronmario[] = { "HARDCORE IRONMARIO", "DOOR INDICATORS", "INVINCIBILITY FRAMES", + "COURSE TIMER", }; -#define textCountIronmario (sizeof(textsModes) / 4) +#define textCountIronmario (sizeof(textsIronmario) / 4) static void page_ironmario() { - for (int i = 0; i < textCountIronmario; i++) { + int i = 0; + for (i = 0; i < 3; i++) { if (check_clicked_text(180, OPTIONS_Y(i), i)) { IRONMARIO_VARS_SET(i, 0) } else if (check_clicked_text(222, OPTIONS_Y(i), i)) { IRONMARIO_VARS_SET(i, 1) } } + + s32 temp = gOptionsSettings.gameplay.s.courseTimer; + if (check_clicked_text(222, OPTIONS_Y(i), 0)) { + temp++; + } else if (check_clicked_text(176, OPTIONS_Y(i), 0)) { + temp--; + } + temp = (temp + 4) % 4; + gOptionsSettings.gameplay.s.courseTimer = temp; + + i++; } static void page_presets() { @@ -2723,6 +2737,14 @@ frames upon spawning into a level.\n\ Despite affecting gameplay, this option\n\ does not affect the validity of the\n\ IronMario challenge.", 205, 5}, + + {"\ +Adds a timer for each course that\n\ +persists between stars. When the timer\n\ +expires, Mario will die. Bonus courses\n\ +have a shorter timer. If doing the 120\n\ +star challenge, the timer will be removed\n\ +once you reach 100 stars.", 205, 6}, }; void page_ironmario_print() { @@ -2732,6 +2754,18 @@ void page_ironmario_print() { options_page_print_on_off(gOptionsSettings.cosmetic.s.doorIndicators, OPTIONS_Y(1), 190, 222); options_page_print_on_off(gOptionsSettings.cosmetic.s.iframes, OPTIONS_Y(2), 190, 222); + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, sTextBaseAlpha); + + print_generic_string(190, OPTIONS_Y(3), textPlus); + if (gOptionsSettings.gameplay.s.courseTimer) { + u8 strCourseTimer[4]; + int_to_str(gCourseTimerLUT[gOptionsSettings.gameplay.s.courseTimer], strCourseTimer); + print_generic_string(203, OPTIONS_Y(3), strCourseTimer); + } else { + print_generic_string_ascii(205, OPTIONS_Y(3), "OFF"); + } + print_generic_string(230, OPTIONS_Y(3), textMinus); + handle_info_display(ironmarioInfo, textCountIronmario); gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, sTextBaseAlpha); diff --git a/textures/segment2/custom_HUD_Clock.rgba16.png b/textures/segment2/custom_HUD_Clock.rgba16.png new file mode 100644 index 0000000000000000000000000000000000000000..ac8a4001c019cf37d654915cf0db1294241161e0 GIT binary patch literal 889 zcmV-<1BU#GP)nt#MugI0r4W!pt&6xwHPlqnOeisrcE%ZJZgOWbzk7d= z>moLI_UCZ;_$>ZQW50CHaZ)2fnAM?DWgB3vFi^Bj;%diL1{$x_tE%0aC|Q<2a~K30 zL#3@%7vSDXVf?7u`Nna*iRzBHx{~*9TTMM5H|pb#=Vv|NZ+M=!`un-e4@z2Zy52J% zYTa00xl_FC0_qf3Ks=s<_a}m)>j_u6Fc%I7`V>1m=&hWzQ%{p8gzgPszVt03!!NEa#dwc2Y>m#gGIDGE| zM59)%y57b2m0Ys{uJ-r)8s_b}xh&Z%QYqp%rqybpwMHpL7>0Pm!|?G(SWxUy`VxQ* zkP1WJ>Pt^JFO0egkR%COYwGnnQc8?5XswBr!W$by0Rl7gWj>#_0R;HkbBo&d0Wij3 zjGzK1riZBR}8=JI%F096}5H`RSPyyUlckha$ z?Z1fQ7_Bw7ZIdJkQcBwGHl0d^@YatsATYLj>x(El_5k2Tc+~&o@$)HdUhX`XN2gL4 zV~FFJFbwH#ZojqR=KQ|vUaXc%Z~ap&-qC>X`-NOC_s-Ma z$pd3d_E-t7MNy=>w*CF$!NIQpR30)nc_y1ANkeOm=Xvdgg@wY>(o*AJWk9Jo&XUgE P00000NkvXXu0mjf1?sq7 literal 0 HcmV?d00001