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 00000000..ac8a4001 Binary files /dev/null and b/textures/segment2/custom_HUD_Clock.rgba16.png differ