Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
640539f
port more ppcutil macros
eddynya Aug 16, 2026
851b6b5
import timerdisp
eddynya Aug 16, 2026
c8ffb38
add getter to gotostory
eddynya Aug 16, 2026
272978f
import new prefs
eddynya Aug 16, 2026
e3d3588
import new menus
eddynya Aug 16, 2026
c1d22ec
import mode.cpp/h
eddynya Aug 16, 2026
36976d8
added last_action_was_blank() functions to savest
eddynya Aug 16, 2026
5d21d43
imported and updated goal.cpp/h
eddynya Aug 16, 2026
a65bec6
import storyreset.cpp/h
eddynya Aug 16, 2026
1f46cba
fix unresolved external symbol error with gotostory
eddynya Aug 16, 2026
ab6a94b
Revert to using History struct in savest
eddynya Aug 16, 2026
4928a9e
savest: use mkb::selected_characters instead of mkb::active_monkey_id
eddynya Aug 16, 2026
ffd3399
update goal to use the new interacted_with_state() func
eddynya Aug 16, 2026
e2dbfbd
import deathcounter.cpp/h
eddynya Aug 16, 2026
6bd6181
import storytimer.cpp/h
eddynya Aug 16, 2026
a89263f
import ilmark changes
eddynya Aug 16, 2026
ea3e36d
update ilmark, validate to use the new func in savest
eddynya Aug 16, 2026
959b589
import menu_accel.cpp/h
eddynya Aug 16, 2026
5083c46
fix edge case bug with deathcounter + some cleanup
eddynya Aug 16, 2026
81243aa
add totals row to breakdown screen
eddynya Aug 17, 2026
de48a3b
small cleanup
eddynya Aug 17, 2026
f6128ec
update draw_timer to use format_time_to_buffer
eddynya Aug 17, 2026
df58f00
update run order in main
eddynya Aug 17, 2026
e7b4544
add hide reset message button to deathcounter menu + small things
eddynya Aug 17, 2026
204c470
cleanup storyreset some
eddynya Aug 18, 2026
5812ff3
revert to old validate/ilmark behavior on creating a state for 1f
eddynya Aug 18, 2026
7b0240e
make split_buf, seg_buf arrays smaller + remove testing comments
eddynya Aug 18, 2026
3784de5
port more ppcutil macros
eddynya Aug 16, 2026
59c22c1
import timerdisp
eddynya Aug 16, 2026
1960881
add getter to gotostory
eddynya Aug 16, 2026
61aa9bd
import new prefs
eddynya Aug 16, 2026
20de4e8
import new menus
eddynya Aug 16, 2026
fb20cf1
import mode.cpp/h
eddynya Aug 16, 2026
6bc2ada
added last_action_was_blank() functions to savest
eddynya Aug 16, 2026
69dc8d2
imported and updated goal.cpp/h
eddynya Aug 16, 2026
a2364c1
import storyreset.cpp/h
eddynya Aug 16, 2026
2b41734
fix unresolved external symbol error with gotostory
eddynya Aug 16, 2026
3100fe9
Revert to using History struct in savest
eddynya Aug 16, 2026
6733433
savest: use mkb::selected_characters instead of mkb::active_monkey_id
eddynya Aug 16, 2026
0bade0f
update goal to use the new interacted_with_state() func
eddynya Aug 16, 2026
7f00f7b
import deathcounter.cpp/h
eddynya Aug 16, 2026
af53183
import storytimer.cpp/h
eddynya Aug 16, 2026
3caac61
import ilmark changes
eddynya Aug 16, 2026
3e266b4
update ilmark, validate to use the new func in savest
eddynya Aug 16, 2026
d647e4a
import menu_accel.cpp/h
eddynya Aug 16, 2026
1334284
fix edge case bug with deathcounter + some cleanup
eddynya Aug 16, 2026
6862698
add totals row to breakdown screen
eddynya Aug 17, 2026
e2da8eb
small cleanup
eddynya Aug 17, 2026
23df108
update draw_timer to use format_time_to_buffer
eddynya Aug 17, 2026
fa27681
update run order in main
eddynya Aug 17, 2026
b644714
add hide reset message button to deathcounter menu + small things
eddynya Aug 17, 2026
ed5aaaa
cleanup storyreset some
eddynya Aug 18, 2026
1a80d44
revert to old validate/ilmark behavior on creating a state for 1f
eddynya Aug 18, 2026
46c8d17
make split_buf, seg_buf arrays smaller + remove testing comments
eddynya Aug 18, 2026
6dd2f5d
Merge branch '2026-renos-v2-import' of https://github.com/eddynya/SMB…
eddynya Aug 18, 2026
737c3e3
super duper wip idea dump for display stuff
eddynya Aug 19, 2026
fe291ae
fix includes and various small issues
eddynya Aug 20, 2026
58810af
savest: remove history struct
eddynya Aug 20, 2026
91b7be9
deathcounter, goal, ilmark, validate use updated state api
eddynya Aug 20, 2026
9ad68fd
fix state bug with gotostory and custom monkey
eddynya Aug 20, 2026
8611496
more wip textinfo stuff, simpler setup
eddynya Aug 22, 2026
2705b5c
test textinfo functions on storytimer, deathcounter
eddynya Aug 22, 2026
10e1d1a
allow signed times + different starting rows per slot
eddynya Aug 22, 2026
f1f8fb9
update cmseg, iw to use textinfo, partially update timer.cpp
eddynya Aug 22, 2026
1b1248c
add format_signed_time and format_subtick_time functions
eddynya Aug 22, 2026
127d33d
update draw_timer and add draw_subtick_timer in textinfo.cpp
eddynya Aug 22, 2026
5bd853c
update subtick/framesave displays in timer.cpp
eddynya Aug 22, 2026
25877af
clean up textinfo some
eddynya Aug 22, 2026
c904063
add debug_text_v to draw's header
eddynya Aug 22, 2026
93f4b5e
add unformatted option to format_time
eddynya Aug 22, 2026
6b9e0c0
textinfo: api allows for mod specific input (wip)
eddynya Aug 22, 2026
58c87ff
start updating il battle display stuff
eddynya Aug 22, 2026
45c7a94
finish up updating il battle display
eddynya Aug 23, 2026
bd92011
update names in textinfo and func calls
eddynya Aug 23, 2026
5486527
renames
eddynya Aug 23, 2026
92a054d
update the rest of storytimer
eddynya Aug 23, 2026
8de98f4
cleanup
eddynya Aug 23, 2026
30cea10
more cleanup
eddynya Aug 23, 2026
a9a2d58
start working on new textinfo update
eddynya Aug 24, 2026
ab4c84a
simplify AtRunEnd setting for story IL mark
eddynya Aug 24, 2026
cfcfdb8
update textinfo draw calls
eddynya Aug 24, 2026
0d6ea35
various cleanup
eddynya Aug 24, 2026
4087f0f
removed a lot of freecam::should_hide_hud() calls
eddynya Aug 24, 2026
7e9cb06
reset story run if you used cm segments
eddynya Aug 24, 2026
736f329
move run breakdown code to its own file
eddynya Aug 25, 2026
39db605
simplify should_display_timer() a bit
eddynya Aug 25, 2026
d8b0b19
update input display to use textinfo api
eddynya Aug 25, 2026
d130de8
add pref for right side sprite hiding
eddynya Aug 25, 2026
fe9bdd0
add hide_sprites.cpp/h
eddynya Aug 25, 2026
aca9527
update textinfo/inputdisp to use hide_sprites.cpp
eddynya Aug 25, 2026
26dc9d4
simplify hide_sprites a bit
eddynya Aug 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions src/mods/ballcolor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "mkb/mkb.h"

#include "mkb/mkb2_ghidra.h"
#include "systems/heap.h"
#include "systems/pad.h"
#include "systems/pref.h"
Expand Down Expand Up @@ -56,29 +57,37 @@ static u8 convert_to_ape_color_id(u8 color_choice) {
return color_choice - 1;
}

// Set both active_monkey_id and selected_characters so savestates work when we use gotostory even
// if we haven't manually selected a monkey on the character select screen
void switch_monkey() {
switch (MonkeyType(pref::get(pref::Pref::MonkeyType))) {
case MonkeyType::Default: {
break;
}
case MonkeyType::Aiai: {
mkb::active_monkey_id[mkb::curr_player_idx] = 0;
mkb::selected_characters[mkb::curr_player_idx] = 0;
break;
}
case MonkeyType::Meemee: {
mkb::active_monkey_id[mkb::curr_player_idx] = 1;
mkb::selected_characters[mkb::curr_player_idx] = 1;
break;
}
case MonkeyType::Baby: {
mkb::active_monkey_id[mkb::curr_player_idx] = 2;
mkb::selected_characters[mkb::curr_player_idx] = 2;
break;
}
case MonkeyType::Gongon: {
mkb::active_monkey_id[mkb::curr_player_idx] = 3;
mkb::selected_characters[mkb::curr_player_idx] = 3;
break;
}
case MonkeyType::Random: {
mkb::active_monkey_id[mkb::curr_player_idx] = mkb::rand() % 4;
u8 monkey_id = mkb::rand() % 4;
mkb::active_monkey_id[mkb::curr_player_idx] = monkey_id;
mkb::selected_characters[mkb::curr_player_idx] = monkey_id;
break;
}
}
Expand Down Expand Up @@ -150,8 +159,7 @@ void tick() {
u32 blue = CLAMP(((mkb::rand() % 256) + bonus_brightness), 0, 0xff);
s_current_color = {static_cast<u8>(red), static_cast<u8>(green),
static_cast<u8>(blue), 0};
*reinterpret_cast<GXColor *>(relutil::relocate_addr(0x80472a34)) =
s_current_color;
*reinterpret_cast<GXColor *>(relutil::relocate_addr(0x80472a34)) = s_current_color;
}
break;
}
Expand Down
9 changes: 7 additions & 2 deletions src/mods/cmseg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "mods/freecam.h"
#include "systems/log.h"
#include "systems/pref.h"
#include "systems/textinfo.h"
#include "utils/draw.h"
#include "utils/macro_utils.h"
#include "utils/patch.h"
Expand Down Expand Up @@ -35,6 +36,9 @@ static s8 s_overwritten_starting_monkeys;

static u32 s_pbs[14];

using Slot = textinfo::Slot;
using Format = timerdisp::TimeFormat;

// static void debug_print_course(mkb::CourseCommand *course, u32 entry_count)
//{
// static const char *type_strs[] = {"COURSE_CMD_IF", "COURSE_CMD_THEN", "COURSE_CMD_INFO",
Expand Down Expand Up @@ -415,7 +419,7 @@ void tick() {
}

void disp() {
if (!pref::get(pref::Pref::CmTimer) || freecam::should_hide_hud()) return;
if (!pref::get(pref::Pref::CmTimer)) return;

if (s_state == State::SegActive || s_state == State::SegComplete) {
u32 seg = static_cast<u32>(s_seg_request);
Expand All @@ -424,7 +428,8 @@ void disp() {
color = draw::GOLD;
else
color = draw::WHITE;
timerdisp::draw_timer(static_cast<s32>(s_seg_time), "SEG:", 0, color, false);
textinfo::draw_timer(Slot::Right, color, "SEG:", static_cast<s32>(s_seg_time),
Format::AlwaysLeadNonHours);
}
}

Expand Down
159 changes: 159 additions & 0 deletions src/mods/deathcounter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#include "deathcounter.h"

#include "mkb/mkb.h"

#include "storyreset.h"
#include "systems/goal.h"
#include "systems/pref.h"
#include "systems/savest.h"
#include "systems/textinfo.h"
#include "utils/draw.h"
#include "utils/macro_utils.h"
#include "utils/mode.h"

namespace deathcounter {

constexpr u16 WORLD_COUNT = mode::WORLD_COUNT;

static u32 s_world_death_count[WORLD_COUNT] = {};
static savest::Action s_previous_frame_action = savest::Action::None;
// Flag to determine when we should/shouldn't increment the death counter
static bool s_can_incr_death_counter = false;

using Slot = textinfo::Slot;
using Format = timerdisp::TimeFormat;

u32 get_total_death_count() {
u32 total = 0;
for (u16 k = 0; k < WORLD_COUNT; k++) {
total += s_world_death_count[k];
}
return total;
}

u32 get_world_death_count(u16 world_idx) {
u16 clamped_idx = MIN(world_idx, WORLD_COUNT - 1); // clamp for safety
return s_world_death_count[clamped_idx];
}
Comment on lines +26 to +37

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these are only used for debug prints? Should be removed

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now these are used in storytimer.cpp to draw the breakdown screen (since I updated the breakdown screen to also include death count per world). Something I was thinking about doing was to completely remove the deathcounter dependency from storytimer, since they really should be independent things. (storytimer also currently needs to know if deathcounter is displaying the counter, but that dependency can also be removed after updating prac mod's display system).

So right now, the include chain looks like the right hand side of the image. But, I could make a new file called storybreakdown or something that's just dedicated to drawing the breakdown screen, and then storytimer and deathcounter could just be independent of each other (and the include chain would look like the left hand side of the image)

20260819_214304


void increment_world_death_counter() {
// Check the pref for count first stage deaths and if we're on the first stage
if (!pref::get(pref::Pref::CountFirstStageDeaths) &&
mode::get_storymode_total_clear_count() == 0) {
return;
}
s_world_death_count[mkb::scen_info.world] += 1; // death counter for the current world
s_can_incr_death_counter = false; // so we only increment once per death
}

void reset_flag() {
s_can_incr_death_counter = false;
}

void reset_death_counters() {
for (u16 k = 0; k < WORLD_COUNT; k++) {
s_world_death_count[k] = 0;
}
reset_flag();
}

bool loaded_state() {
return savest::get_last_action() == savest::Action::Load &&
s_previous_frame_action == savest::Action::None;
}

// When we're done holding the savestate button/when gameplay resumes
void update_flag_on_state_release() {
if (goal::is_gameplay_exact() && savest::get_last_action() == savest::Action::None) {
// As soon as we're done holding the load state button (or just any time we're controlling
// the monkey on the stage), we're allowed to die
s_can_incr_death_counter = true;
}
}

bool should_count_as_normal_death() {
bool retried_without_clearing =
mode::is_spin_in_init(mkb::sub_mode) && s_can_incr_death_counter;
bool left_stage_without_clearing =
mode::is_stage_exit_submode(mkb::sub_mode) && s_can_incr_death_counter;
// Need to also check the flag for death init submodes in case we fall out and let the animation
// play out (ie we enter 2 "death init" submodes before resuming gameplay)
bool died = mode::is_death_init(mkb::sub_mode) && s_can_incr_death_counter;
return retried_without_clearing || left_stage_without_clearing || died;
}

bool should_count_as_savestate_death() {
return loaded_state() && s_can_incr_death_counter;
}

void count_deaths() {
if (goal::is_postgoal_exact()) {
s_can_incr_death_counter = false;
}

if (should_count_as_normal_death() || should_count_as_savestate_death()) {
increment_world_death_counter();
}
}

void tick() {
if (storyreset::should_reset_run()) {
reset_death_counters();
}

// Whenever entering a new stage, reset our flag
if (mode::is_spin_in_first_init(mkb::sub_mode)) {
reset_flag();
}

update_flag_on_state_release();
count_deaths();

// Only after we're done doing death checks for this frame do we update s_previous_frame_action
s_previous_frame_action = savest::get_last_action();
}

bool should_display_death_counter() {
u8 pref = pref::get(pref::Pref::DeathCounterDisplayOptions);

using DeathCounterOptions = storyreset::StoryDisplayOptions;

switch (DeathCounterOptions(pref)) {
case DeathCounterOptions::AlwaysShow:
return true;
case DeathCounterOptions::BetweenWorlds:
return goal::is_between_worlds();
case DeathCounterOptions::EndOfRun:
return goal::is_run_complete();
case DeathCounterOptions::DontShow:
return false;
default:
// Unreachable
return false;
}
}

bool should_not_display_counter_at_all() {
if (!mode::is_main_game_mode_story(mkb::main_game_mode)) {
// If we're in the menus outside of a story mode run due to an accidental exit game, we
// still want to be able to display the counter if we haven't reset it yet
return !storyreset::is_run_active();
} else {
return false;
}
}

void disp() {
if (should_not_display_counter_at_all()) {
return;
}

if (should_display_death_counter()) {
// Technically not a timer, but we can still use this function without specifying any
// special formatting
textinfo::draw_timer(Slot::Left, draw::WHITE, "Deaths:", get_total_death_count(),
Format::Unformatted);
}
}

} // namespace deathcounter
14 changes: 14 additions & 0 deletions src/mods/deathcounter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include "mkb/mkb.h"

namespace deathcounter {

u32 get_total_death_count();
u32 get_world_death_count(u16 world_idx);
Comment on lines +7 to +8

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove if only used for debug prints

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These get used by the story breakdown screen since death counts for each world + total death count get displayed there

bool should_display_death_counter();

void tick();
void disp();

} // namespace deathcounter
8 changes: 8 additions & 0 deletions src/mods/freecam.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "systems/binds.h"
#include "systems/pad.h"
#include "systems/pref.h"
#include "systems/textinfo.h"
#include "utils/draw.h"
#include "utils/macro_utils.h"
#include "utils/patch.h"
Expand Down Expand Up @@ -169,6 +170,13 @@ void tick() {
pref::save();
}
}

// Handle hiding all ui elements coming from textinfo
if (should_hide_hud()) {
textinfo::set_drawing_state(false);
} else {
textinfo::set_drawing_state(true);
}
}

} // namespace freecam
10 changes: 4 additions & 6 deletions src/mods/gotostory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@

namespace gotostory {

enum class State {
Default,
LoadMenuReq,
LoadStoryReq,
};

static State s_state = State::Default;

State get_gotostory_state() {
return s_state;
}

void load_storymode() {
if (mkb::main_mode == mkb::MD_SEL) {
s_state = State::LoadStoryReq;
Expand Down
7 changes: 7 additions & 0 deletions src/mods/gotostory.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

namespace gotostory {

enum class State {
Default,
LoadMenuReq,
LoadStoryReq,
};

void tick();
void load_storymode();
State get_gotostory_state();

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way you can use mkb modes/submodes instead of gotostory state?


} // namespace gotostory
Loading