mirror of
https://github.com/BanjoRecomp/BanjoRecomp
synced 2026-05-23 14:41:41 -04:00
Note saving (#30)
* WIP note saving implementation * Fix note saving affecting other files * Prevent note collection in demo playback for safety * Cache note saving enabled while in the lair or file select to prevent it changing in levels with notes * Prevent "Grunty's magic" note dialog from being shown if note saving is enabled * Implement dynamically spawned note saving * Properly clear loaded save extension data when score status is cleared * Hook up menu for note saving
This commit is contained in:
@@ -208,7 +208,7 @@
|
||||
data-checked="analog_camera_invert_mode"
|
||||
value="InvertNone"
|
||||
id="analog_camera_inversion_none"
|
||||
style="nav-up: #analog_cam_enabled;"
|
||||
style="nav-up: #analog_cam_enabled; nav-down: #note_saving_enabled"
|
||||
/>
|
||||
<label class="config-option__tab-label" for="analog_camera_inversion_none">None</label>
|
||||
|
||||
@@ -220,7 +220,7 @@
|
||||
data-checked="analog_camera_invert_mode"
|
||||
value="InvertX"
|
||||
id="analog_camera_inversion_x"
|
||||
style="nav-up: #analog_cam_disabled;"
|
||||
style="nav-up: #analog_cam_disabled; nav-down: #note_saving_disabled"
|
||||
/>
|
||||
<label class="config-option__tab-label" for="analog_camera_inversion_x">Invert X</label>
|
||||
|
||||
@@ -232,7 +232,7 @@
|
||||
data-checked="analog_camera_invert_mode"
|
||||
value="InvertY"
|
||||
id="analog_camera_inversion_y"
|
||||
style="nav-up: #analog_cam_disabled;"
|
||||
style="nav-up: #analog_cam_disabled; nav-down: #note_saving_disabled"
|
||||
/>
|
||||
<label class="config-option__tab-label" for="analog_camera_inversion_y">Invert Y</label>
|
||||
|
||||
@@ -244,11 +244,41 @@
|
||||
data-checked="analog_camera_invert_mode"
|
||||
value="InvertBoth"
|
||||
id="analog_camera_inversion_both"
|
||||
style="nav-up: #analog_cam_disabled;"
|
||||
style="nav-up: #analog_cam_disabled; nav-down: #note_saving_disabled"
|
||||
/>
|
||||
<label class="config-option__tab-label" for="analog_camera_inversion_both">Invert Both</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- note saving -->
|
||||
<div class="config-option" data-event-mouseover="set_cur_config_index(10)">
|
||||
<label class="config-option__title">Note Saving</label>
|
||||
<div class="config-option__list">
|
||||
<input
|
||||
type="radio"
|
||||
data-event-blur="set_cur_config_index(-1)"
|
||||
data-event-focus="set_cur_config_index(10)"
|
||||
name="note_saving_mode"
|
||||
data-checked="note_saving_mode"
|
||||
value="On"
|
||||
id="note_saving_enabled"
|
||||
style="nav-up: #camera_inversion_none"
|
||||
/>
|
||||
<label class="config-option__tab-label" for="note_saving_enabled">On</label>
|
||||
|
||||
<input
|
||||
type="radio"
|
||||
data-event-blur="set_cur_config_index(-1)"
|
||||
data-event-focus="set_cur_config_index(10)"
|
||||
name="note_saving_mode"
|
||||
data-checked="note_saving_mode"
|
||||
value="Off"
|
||||
id="note_saving_disabled"
|
||||
style="nav-up: #camera_inversion_x"
|
||||
/>
|
||||
<label class="config-option__tab-label" for="note_saving_disabled">Off</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Descriptions -->
|
||||
<div class="config__wrapper">
|
||||
@@ -290,6 +320,9 @@
|
||||
<p data-if="cur_config_index == 9">
|
||||
Inverts the camera controls for the analog camera if it's enabled. <b>None</b> is the default.
|
||||
</p>
|
||||
<p data-if="cur_config_index == 10">
|
||||
Note saving DESCRIPTION TODO.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -59,6 +59,20 @@ namespace banjo {
|
||||
AnalogCamMode get_analog_cam_mode();
|
||||
void set_analog_cam_mode(AnalogCamMode mode);
|
||||
|
||||
enum class NoteSavingMode {
|
||||
On,
|
||||
Off,
|
||||
OptionCount
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(banjo::NoteSavingMode, {
|
||||
{banjo::NoteSavingMode::On, "On"},
|
||||
{banjo::NoteSavingMode::Off, "Off"}
|
||||
});
|
||||
|
||||
NoteSavingMode get_note_saving_mode();
|
||||
void set_note_saving_mode(NoteSavingMode mode);
|
||||
|
||||
void open_quit_game_prompt();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "patches.h"
|
||||
#include "ultra_extensions.h"
|
||||
#include "misc_funcs.h"
|
||||
#include "note_saving.h"
|
||||
#include "core1/core1.h"
|
||||
#include "core1/vimgr.h"
|
||||
|
||||
@@ -94,6 +95,9 @@ RECOMP_PATCH void game_draw(s32 arg0){
|
||||
// @recomp Advance the frame used as reference by the dynamic camera target changes for analog camera.
|
||||
recomp_advance_dynamic_camera_targets();
|
||||
|
||||
// @recomp Update note saving state.
|
||||
note_saving_update();
|
||||
|
||||
// @recomp Track the original values.
|
||||
Mtx* mtx_start = mtx;
|
||||
Vtx* vtx_start = vtx;
|
||||
|
||||
+13
-12
@@ -1,24 +1,25 @@
|
||||
#include "patches.h"
|
||||
#include "note_saving.h"
|
||||
#include "transform_ids.h"
|
||||
#include "bk_api.h"
|
||||
#include "misc_funcs.h"
|
||||
|
||||
#include "core1/core1.h"
|
||||
#include "core1/main.h"
|
||||
#include "core1/vimgr.h"
|
||||
|
||||
RECOMP_DECLARE_EVENT(recomp_on_init());
|
||||
|
||||
void core1_init();
|
||||
void sns_write_payload_over_heap();
|
||||
void mainLoop();
|
||||
// @recomp Patched to perform some initialization after core2 has been loaded.
|
||||
RECOMP_PATCH void dummy_func_8025AFB0(void) {
|
||||
// @recomp Initialize note saving data before the init event is run.
|
||||
// This will allow mods to override it as necessary.
|
||||
init_note_saving();
|
||||
|
||||
RECOMP_PATCH void mainThread_entry(void *arg) {
|
||||
// @recomp Register actor extension data and call the init event.
|
||||
recomp_on_init();
|
||||
|
||||
core1_init();
|
||||
sns_write_payload_over_heap();
|
||||
|
||||
while (1) {
|
||||
// @recomp Reset the tracked projection IDs.
|
||||
reset_projection_ids();
|
||||
mainLoop();
|
||||
}
|
||||
// @recomp Calculate the note start indices for each map after the init event.
|
||||
// This allows the start indices to account for any changes made by mods.
|
||||
calculate_map_start_note_indices();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "functions.h"
|
||||
#include "bk_api.h"
|
||||
#include "object_extension_funcs.h"
|
||||
#include "note_saving.h"
|
||||
|
||||
extern ActorMarker *D_8036E7C8;
|
||||
extern u8 D_80383428[0x1C];
|
||||
@@ -96,6 +97,7 @@ void hotpatch_intro_opa_map_model(BKModelBin* model_bin) {
|
||||
// This includes:
|
||||
// * Resetting all extended marker data and skip interpolation for the next frame.
|
||||
// * Hotpatching the map model for the title cutscene to fix ultrawide effects.
|
||||
// * Resetting the spawned static note count.
|
||||
RECOMP_PATCH void func_803329AC(void){
|
||||
s32 i;
|
||||
|
||||
@@ -122,4 +124,7 @@ RECOMP_PATCH void func_803329AC(void){
|
||||
// as the marker ID tracking gets reset here.
|
||||
recomp_clear_all_object_data(EXTENSION_TYPE_MARKER);
|
||||
set_all_interpolation_skipped(TRUE);
|
||||
}
|
||||
|
||||
// @recomp Run note saving map load code.
|
||||
note_saving_on_map_load();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "functions.h"
|
||||
#include "object_extension_funcs.h"
|
||||
#include "bk_api.h"
|
||||
#include "note_saving.h"
|
||||
|
||||
// Array of handles for ActorMarker instances.
|
||||
// Normally the game only has at most 0xE0 ActorMarker instances, but this is larger to account for mods increasing
|
||||
@@ -15,6 +16,8 @@ extern u8 D_80383428[0x1C];
|
||||
void func_8032F3D4(s32 arg0[3], ActorMarker *marker, s32 arg2);
|
||||
ActorMarker * func_80332A60(void);
|
||||
|
||||
extern Actor *suLastBaddie;
|
||||
|
||||
// @recomp Patched to create extension data for the marker.
|
||||
RECOMP_PATCH ActorMarker * marker_init(s32 *pos, MarkerDrawFunc draw_func, int arg2, int marker_id, int arg4){
|
||||
ActorMarker * marker = func_80332A60();
|
||||
@@ -64,6 +67,11 @@ RECOMP_PATCH ActorMarker * marker_init(s32 *pos, MarkerDrawFunc draw_func, int a
|
||||
// @recomp Set the marker's handle.
|
||||
u32 index = marker - D_8036E7C8;
|
||||
marker_handles[index] = recomp_create_object_data(EXTENSION_TYPE_MARKER, marker_id);
|
||||
|
||||
// @recomp If this is a note marker, initialize it. Make sure that this marker belongs to suLastBaddie.
|
||||
if (marker->id == MARKER_5F_MUSIC_NOTE && suLastBaddie && suLastBaddie->actor_info->markerId == marker_id) {
|
||||
note_saving_handle_dynamic_note(suLastBaddie, marker);
|
||||
}
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
@@ -8,5 +8,6 @@ DECLARE_FUNC(void, recomp_puts, const char* data, u32 size);
|
||||
DECLARE_FUNC(void, recomp_exit);
|
||||
DECLARE_FUNC(void, recomp_error, const char* str);
|
||||
DECLARE_FUNC(u64, recomp_xxh3, void* data, u32 size);
|
||||
DECLARE_FUNC(s32, recomp_get_note_saving_enabled);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,462 @@
|
||||
#include "patches.h"
|
||||
#include "bk_api.h"
|
||||
#include "misc_funcs.h"
|
||||
#include "save_extension.h"
|
||||
#include "note_saving.h"
|
||||
|
||||
#include "functions.h"
|
||||
#include "prop.h"
|
||||
#include "core2/timedfunc.h"
|
||||
|
||||
// Vanilla declarations.
|
||||
typedef struct map_info{
|
||||
s16 map_id;
|
||||
s16 level_id;
|
||||
char* name;
|
||||
}MapInfo;
|
||||
|
||||
extern struct {
|
||||
s32 unk0;
|
||||
s32 unk4;
|
||||
u8 unk8[0x25];
|
||||
} gFileProgressFlags;
|
||||
|
||||
MapInfo * func_8030AD00(enum map_e map_id);
|
||||
enum map_e map_get(void);
|
||||
enum level_e level_get(void);
|
||||
void progressDialog_showDialogMaskZero(s32);
|
||||
void fxSparkle_musicNote(s16 position[3]);
|
||||
void item_inc(enum item_e item);
|
||||
void item_adjustByDiffWithoutHud(enum item_e item, s32 diff);
|
||||
bool func_802FADD4(enum item_e item_id);
|
||||
s32 item_getCount(enum item_e item);
|
||||
s32 jiggyscore_leveltotal(s32 lvl);
|
||||
void itemPrint_reset(void);
|
||||
s32 bitfield_get_bit(u8 *array, s32 index);
|
||||
|
||||
extern s32 D_80385F30[0x2C];
|
||||
extern s32 D_80385FE8;
|
||||
|
||||
extern struct {
|
||||
Cube *cubes;
|
||||
f32 margin;
|
||||
s32 min[3];
|
||||
s32 max[3];
|
||||
s32 stride[2];
|
||||
s32 cubeCnt;
|
||||
s32 unk2C;
|
||||
s32 width[3];
|
||||
Cube *unk3C; // fallback cube?
|
||||
Cube *unk40; // some other fallback cube?
|
||||
s32 unk44; // index of some sort
|
||||
} sCubeList;
|
||||
|
||||
// New declarations.
|
||||
typedef struct {
|
||||
u16 static_note_count;
|
||||
u16 dynamic_note_count;
|
||||
u16 start_note_index;
|
||||
u16 level_id;
|
||||
} MapNoteData;
|
||||
|
||||
MapNoteData map_note_data[512]; // One entry per map, with room for extras in case any mods add additional maps.
|
||||
u16 level_note_counts[256]; // One entry per level, with room for extras in case any mods add additional levels.
|
||||
|
||||
typedef struct {
|
||||
u32 note_index;
|
||||
} NoteSavingExtensionData;
|
||||
|
||||
PropExtensionId note_saving_prop_extension_id;
|
||||
|
||||
// Note saving can only savely be changed while in the lair, so this value is only updated when in the lair.
|
||||
bool note_saving_enabled_cached = FALSE;
|
||||
// Override for allowing mods to disable note saving.
|
||||
bool note_saving_override_disabled = FALSE;
|
||||
|
||||
u32 spawned_static_note_count = 0;
|
||||
|
||||
// Per-map values containing the number of dynamic notes to despawn for the current session.
|
||||
// This is calculated when a new level is entered for every map in the level.
|
||||
u8 map_dynamic_note_despawn_counts[512];
|
||||
|
||||
bool recomp_in_demo_playback_game_mode();
|
||||
|
||||
void init_note_saving() {
|
||||
note_saving_prop_extension_id = bkrecomp_extend_prop_all(sizeof(NoteSavingExtensionData));
|
||||
|
||||
// Collected from map data.
|
||||
map_note_data[MAP_2_MM_MUMBOS_MOUNTAIN].static_note_count = 85;
|
||||
map_note_data[MAP_2_MM_MUMBOS_MOUNTAIN].dynamic_note_count = 5;
|
||||
map_note_data[MAP_5_TTC_BLUBBERS_SHIP].static_note_count = 8;
|
||||
map_note_data[MAP_6_TTC_NIPPERS_SHELL].static_note_count = 6;
|
||||
map_note_data[MAP_7_TTC_TREASURE_TROVE_COVE].static_note_count = 82;
|
||||
map_note_data[MAP_A_TTC_SANDCASTLE].static_note_count = 4;
|
||||
map_note_data[MAP_B_CC_CLANKERS_CAVERN].static_note_count = 72;
|
||||
map_note_data[MAP_C_MM_TICKERS_TOWER].static_note_count = 6;
|
||||
map_note_data[MAP_D_BGS_BUBBLEGLOOP_SWAMP].static_note_count = 83;
|
||||
map_note_data[MAP_D_BGS_BUBBLEGLOOP_SWAMP].dynamic_note_count = 5;
|
||||
map_note_data[MAP_E_MM_MUMBOS_SKULL].static_note_count = 4;
|
||||
map_note_data[MAP_10_BGS_MR_VILE].static_note_count = 6;
|
||||
map_note_data[MAP_11_BGS_TIPTUP].static_note_count = 6;
|
||||
map_note_data[MAP_12_GV_GOBIS_VALLEY].static_note_count = 70;
|
||||
map_note_data[MAP_13_GV_MEMORY_GAME].static_note_count = 4;
|
||||
map_note_data[MAP_14_GV_SANDYBUTTS_MAZE].static_note_count = 7;
|
||||
map_note_data[MAP_15_GV_WATER_PYRAMID].static_note_count = 4;
|
||||
map_note_data[MAP_16_GV_RUBEES_CHAMBER].static_note_count = 8;
|
||||
map_note_data[MAP_1A_GV_INSIDE_JINXY].static_note_count = 7;
|
||||
map_note_data[MAP_1B_MMM_MAD_MONSTER_MANSION].static_note_count = 47;
|
||||
map_note_data[MAP_1C_MMM_CHURCH].static_note_count = 10;
|
||||
map_note_data[MAP_1D_MMM_CELLAR].static_note_count = 4;
|
||||
map_note_data[MAP_21_CC_WITCH_SWITCH_ROOM].static_note_count = 6;
|
||||
map_note_data[MAP_22_CC_INSIDE_CLANKER].static_note_count = 16;
|
||||
map_note_data[MAP_23_CC_GOLDFEATHER_ROOM].static_note_count = 6;
|
||||
map_note_data[MAP_24_MMM_TUMBLARS_SHED].static_note_count = 4;
|
||||
map_note_data[MAP_25_MMM_WELL].static_note_count = 7;
|
||||
map_note_data[MAP_26_MMM_NAPPERS_ROOM].static_note_count = 8;
|
||||
map_note_data[MAP_27_FP_FREEZEEZY_PEAK].static_note_count = 82;
|
||||
map_note_data[MAP_29_MMM_NOTE_ROOM].static_note_count = 9;
|
||||
map_note_data[MAP_2D_MMM_BEDROOM].static_note_count = 4;
|
||||
map_note_data[MAP_2F_MMM_WATERDRAIN_BARREL].static_note_count = 5;
|
||||
map_note_data[MAP_30_MMM_MUMBOS_SKULL].static_note_count = 2;
|
||||
map_note_data[MAP_31_RBB_RUSTY_BUCKET_BAY].static_note_count = 43;
|
||||
map_note_data[MAP_34_RBB_ENGINE_ROOM].static_note_count = 16;
|
||||
map_note_data[MAP_35_RBB_WAREHOUSE].static_note_count = 4;
|
||||
map_note_data[MAP_37_RBB_CONTAINER_1].static_note_count = 8;
|
||||
map_note_data[MAP_38_RBB_CONTAINER_3].static_note_count = 4;
|
||||
map_note_data[MAP_39_RBB_CREW_CABIN].static_note_count = 4;
|
||||
map_note_data[MAP_3B_RBB_STORAGE_ROOM].static_note_count = 5;
|
||||
map_note_data[MAP_3C_RBB_KITCHEN].static_note_count = 5;
|
||||
map_note_data[MAP_3D_RBB_NAVIGATION_ROOM].static_note_count = 4;
|
||||
map_note_data[MAP_3F_RBB_CAPTAINS_CABIN].static_note_count = 3;
|
||||
map_note_data[MAP_40_CCW_HUB].static_note_count = 4;
|
||||
map_note_data[MAP_43_CCW_SPRING].static_note_count = 16;
|
||||
map_note_data[MAP_44_CCW_SUMMER].static_note_count = 16;
|
||||
map_note_data[MAP_45_CCW_AUTUMN].static_note_count = 37;
|
||||
map_note_data[MAP_46_CCW_WINTER].static_note_count = 16;
|
||||
map_note_data[MAP_48_FP_MUMBOS_SKULL].static_note_count = 6;
|
||||
map_note_data[MAP_4C_CCW_AUTUMN_MUMBOS_SKULL].static_note_count = 4;
|
||||
map_note_data[MAP_53_FP_CHRISTMAS_TREE].static_note_count = 12;
|
||||
map_note_data[MAP_5C_CCW_AUTUMN_ZUBBA_HIVE].static_note_count = 4;
|
||||
map_note_data[MAP_60_CCW_AUTUMN_NABNUTS_HOUSE].static_note_count = 3;
|
||||
map_note_data[MAP_8B_RBB_ANCHOR_ROOM].static_note_count = 4;
|
||||
}
|
||||
|
||||
RECOMP_EXPORT void bkrecomp_notesaving_clear_all_map_note_counts() {
|
||||
for (u32 i = 0; i < ARRLEN(map_note_data); i++) {
|
||||
map_note_data[i].static_note_count = 0;
|
||||
map_note_data[i].dynamic_note_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
RECOMP_EXPORT void bkrecomp_notesaving_set_map_static_note_count(u32 map_id, u16 static_note_count) {
|
||||
if (map_id >= ARRLEN(map_note_data)) {
|
||||
recomp_error("Mod error: Attempted to set static note count of an invalid map ID\n");
|
||||
}
|
||||
map_note_data[map_id].static_note_count = static_note_count;
|
||||
}
|
||||
|
||||
RECOMP_EXPORT void bkrecomp_notesaving_set_map_dynamic_note_count(u32 map_id, u16 dynamic_note_count) {
|
||||
if (map_id >= ARRLEN(map_note_data)) {
|
||||
recomp_error("Mod error: Attempted to set dynamic note count of an invalid map ID\n");
|
||||
}
|
||||
map_note_data[map_id].dynamic_note_count = dynamic_note_count;
|
||||
}
|
||||
|
||||
RECOMP_EXPORT void bkrecomp_notesaving_force_disabled(bool disabled) {
|
||||
note_saving_override_disabled = disabled;
|
||||
}
|
||||
|
||||
// Notes are always saved, but this function controls whether to use the saved data to prevent notes from spawning and adjust the note score.
|
||||
RECOMP_EXPORT s32 bkrecomp_note_saving_enabled() {
|
||||
return recomp_get_note_saving_enabled();
|
||||
}
|
||||
|
||||
void calculate_map_start_note_indices() {
|
||||
for (u32 map_id = 0; map_id < ARRLEN(map_note_data); map_id++) {
|
||||
MapNoteData* note_data = &map_note_data[map_id];
|
||||
MapInfo* info = func_8030AD00(map_id);
|
||||
if (info != NULL) {
|
||||
note_data->level_id = info->level_id;
|
||||
note_data->start_note_index = level_note_counts[info->level_id];
|
||||
level_note_counts[info->level_id] += note_data->static_note_count + note_data->dynamic_note_count;
|
||||
}
|
||||
else {
|
||||
note_data->level_id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s32 level_id_to_level_array_index(enum level_e level_id) {
|
||||
switch (level_id) {
|
||||
case LEVEL_1_MUMBOS_MOUNTAIN:
|
||||
return 0;
|
||||
case LEVEL_2_TREASURE_TROVE_COVE:
|
||||
return 1;
|
||||
case LEVEL_3_CLANKERS_CAVERN:
|
||||
return 2;
|
||||
case LEVEL_4_BUBBLEGLOOP_SWAMP:
|
||||
return 3;
|
||||
case LEVEL_5_FREEZEEZY_PEAK:
|
||||
return 4;
|
||||
case LEVEL_7_GOBIS_VALLEY:
|
||||
return 5;
|
||||
case LEVEL_A_MAD_MONSTER_MANSION:
|
||||
return 6;
|
||||
case LEVEL_9_RUSTY_BUCKET_BAY:
|
||||
return 7;
|
||||
case LEVEL_8_CLICK_CLOCK_WOOD:
|
||||
return 8;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_note_collected(enum map_e map_id, enum level_e level_id, u8 note_index) {
|
||||
if (map_id >= ARRLEN(map_note_data)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
s32 level_array_index = level_id_to_level_array_index(level_id);
|
||||
if (level_array_index == -1 || level_array_index >= ARRLEN(loaded_file_extension_data.level_notes)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
MapNoteData *note_data = &map_note_data[map_id];
|
||||
note_index += note_data->start_note_index;
|
||||
|
||||
u32 byte_index = note_index / 8;
|
||||
u32 bit_index = note_index % 8;
|
||||
|
||||
return (loaded_file_extension_data.level_notes[level_array_index].bytes[byte_index] & (1 << bit_index)) != 0;
|
||||
}
|
||||
|
||||
void set_note_collected(enum map_e map_id, enum level_e level_id, u8 note_index) {
|
||||
if (map_id >= ARRLEN(map_note_data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
s32 level_array_index = level_id_to_level_array_index(level_id);
|
||||
if (level_array_index == -1 || level_array_index >= ARRLEN(loaded_file_extension_data.level_notes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MapNoteData *note_data = &map_note_data[map_id];
|
||||
note_index += note_data->start_note_index;
|
||||
|
||||
u32 byte_index = note_index / 8;
|
||||
u32 bit_index = note_index % 8;
|
||||
|
||||
loaded_file_extension_data.level_notes[level_array_index].bytes[byte_index] |= (1 << bit_index);
|
||||
}
|
||||
|
||||
void collect_dynamic_note(enum map_e map_id, enum level_e level_id) {
|
||||
if (map_id < ARRLEN(map_note_data)) {
|
||||
MapNoteData *map_data = &map_note_data[map_id];
|
||||
s32 start_note_index = map_data->static_note_count + map_data->start_note_index;
|
||||
s32 map_dynamic_note_count = map_data->dynamic_note_count;
|
||||
|
||||
// Set the first unset dynamic note bit for this map.
|
||||
for (s32 i = 0; i < map_dynamic_note_count; i++) {
|
||||
s32 note_index = start_note_index + i;
|
||||
if (!is_note_collected(map_id, level_id, note_index)) {
|
||||
set_note_collected(map_id, level_id, note_index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s32 dynamic_note_collected_count(enum map_e map_id) {
|
||||
s32 ret = 0;
|
||||
if (map_id < ARRLEN(map_note_data)) {
|
||||
MapNoteData *map_data = &map_note_data[map_id];
|
||||
s32 start_note_index = map_data->static_note_count + map_data->start_note_index;
|
||||
s32 map_dynamic_note_count = map_data->dynamic_note_count;
|
||||
|
||||
// Check the dynamic note bits for this map.
|
||||
for (s32 i = 0; i < map_dynamic_note_count; i++) {
|
||||
s32 note_index = start_note_index + i;
|
||||
if (is_note_collected(map_id, map_data->level_id, note_index)) {
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void note_saving_on_map_load() {
|
||||
spawned_static_note_count = 0;
|
||||
|
||||
// Prevent the note score passed dialog from running if note saving is enabled.
|
||||
if (note_saving_enabled_cached) {
|
||||
// This flag controls whether Bottles will tell you when you pass your note score.
|
||||
levelSpecificFlags_set(LEVEL_FLAG_34_UNKNOWN, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
void note_saving_update() {
|
||||
// When in the lair or file select, update the cached note saving enabled state.
|
||||
if (level_get() == LEVEL_6_LAIR || map_get() == MAP_91_FILE_SELECT) {
|
||||
if (note_saving_override_disabled) {
|
||||
note_saving_enabled_cached = FALSE;
|
||||
}
|
||||
else {
|
||||
note_saving_enabled_cached = bkrecomp_note_saving_enabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void note_saving_handle_static_note(Cube *c, Prop *p) {
|
||||
|
||||
// If note saving is enabled, check if this note has been collected and remove it if so.
|
||||
if (note_saving_enabled_cached) {
|
||||
if (is_note_collected(map_get(), level_get(), spawned_static_note_count)) {
|
||||
// Clear the note's alive bit.
|
||||
p->spriteProp.unk8_4 = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
NoteSavingExtensionData* note_data = (NoteSavingExtensionData*)bkrecomp_get_extended_prop_data(c, p, note_saving_prop_extension_id);
|
||||
note_data->note_index = spawned_static_note_count;
|
||||
|
||||
spawned_static_note_count++;
|
||||
}
|
||||
|
||||
void note_saving_handle_dynamic_note(Actor *actor, ActorMarker *marker) {
|
||||
if (note_saving_enabled_cached) {
|
||||
s32 map_id = map_get();
|
||||
if (map_id < ARRLEN(map_dynamic_note_despawn_counts)) {
|
||||
if (map_dynamic_note_despawn_counts[map_id] > 0) {
|
||||
map_dynamic_note_despawn_counts[map_id]--;
|
||||
// Clear the note's alive bit so it doesn't draw for good measure.
|
||||
marker->propPtr->unk8_4 = FALSE;
|
||||
// Set the actor as despawned.
|
||||
actor->despawn_flag = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool prop_in_cube(Cube *c, Prop *p) {
|
||||
s32 prop_index = p - c->prop2Ptr;
|
||||
if (prop_index >= 0 && prop_index < c->prop2Cnt) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Cube *find_cube_for_prop(Prop *p) {
|
||||
for (s32 i = 0; i < sCubeList.cubeCnt; i++) {
|
||||
Cube *cur_cube = &sCubeList.cubes[i];
|
||||
if (prop_in_cube(cur_cube, p)) {
|
||||
return cur_cube;
|
||||
}
|
||||
}
|
||||
|
||||
if (prop_in_cube(sCubeList.unk3C, p)) {
|
||||
return sCubeList.unk3C;
|
||||
}
|
||||
|
||||
if (prop_in_cube(sCubeList.unk40, p)) {
|
||||
return sCubeList.unk40;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// @recomp Patched to track collected notes.
|
||||
RECOMP_PATCH void __baMarker_resolveMusicNoteCollision(Prop *arg0) {
|
||||
// @recomp Set that the note was collected if this isn't demo playback.
|
||||
if (!recomp_in_demo_playback_game_mode()) {
|
||||
// Check if this is an actor prop and collect a dynamic note if so.
|
||||
if (arg0->is_actor) {
|
||||
collect_dynamic_note(map_get(), level_get());
|
||||
}
|
||||
// Otherwise, make sure this is a sprite prop and use the prop data.
|
||||
else if (!arg0->is_3d) {
|
||||
Cube *prop_cube = find_cube_for_prop(arg0);
|
||||
if (prop_cube != NULL) {
|
||||
NoteSavingExtensionData* note_data = (NoteSavingExtensionData*)bkrecomp_get_extended_prop_data(prop_cube, arg0, note_saving_prop_extension_id);
|
||||
set_note_collected(map_get(), level_get(), note_data->note_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!func_802FADD4(ITEM_1B_VILE_VILE_SCORE)) {
|
||||
item_inc(ITEM_C_NOTE);
|
||||
} else {
|
||||
item_adjustByDiffWithoutHud(ITEM_C_NOTE, 1);
|
||||
}
|
||||
if (item_getCount(ITEM_C_NOTE) < 100) {
|
||||
coMusicPlayer_playMusic(COMUSIC_9_NOTE_COLLECTED, 16000);
|
||||
timedFunc_set_1(0.75f, progressDialog_showDialogMaskZero, FILEPROG_3_MUSIC_NOTE_TEXT);
|
||||
}
|
||||
fxSparkle_musicNote(arg0->unk4);
|
||||
}
|
||||
|
||||
s32 get_collected_note_count(enum level_e level) {
|
||||
s32 level_array_index = level_id_to_level_array_index(level);
|
||||
if (level_array_index == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 count = 0;
|
||||
for (int i = 0; i < ARRLEN(loaded_file_extension_data.level_notes[0].bytes); i++) {
|
||||
u8 cur_byte = loaded_file_extension_data.level_notes[level_array_index].bytes[i];
|
||||
count += __builtin_popcount(cur_byte);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// @recomp Patched to restore the saved note count when entering a level and reset the per-map collected dynamic note counts.
|
||||
RECOMP_PATCH void itemscore_levelReset(enum level_e level){
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 6; i++){
|
||||
D_80385F30[ITEM_0_HOURGLASS_TIMER + i] = 0;
|
||||
D_80385F30[ITEM_6_HOURGLASS + i] = 0;
|
||||
}
|
||||
|
||||
D_80385F30[ITEM_C_NOTE] = 0;
|
||||
D_80385F30[ITEM_E_JIGGY] = jiggyscore_leveltotal(level);
|
||||
D_80385F30[ITEM_12_JINJOS] = 0;
|
||||
D_80385F30[ITEM_17_AIR] = 3600;
|
||||
D_80385F30[ITEM_18_GOLD_BULLIONS] = 0;
|
||||
D_80385F30[ITEM_19_ORANGE] = 0;
|
||||
D_80385F30[ITEM_23_ACORNS] = 0;
|
||||
D_80385F30[ITEM_1A_PLAYER_VILE_SCORE] = 0;
|
||||
D_80385F30[ITEM_1B_VILE_VILE_SCORE] = 0;
|
||||
D_80385F30[ITEM_1F_GREEN_PRESENT] = 0;
|
||||
D_80385F30[ITEM_20_BLUE_PRESENT] = 0;
|
||||
D_80385F30[ITEM_21_RED_PRESENT] = 0;
|
||||
D_80385F30[ITEM_22_CATERPILLAR] = 0;
|
||||
itemPrint_reset();
|
||||
D_80385FE8 = 1;
|
||||
|
||||
// @recomp If note saving is currently enabled, set load the note count for the current level.
|
||||
if (note_saving_enabled_cached) {
|
||||
D_80385F30[ITEM_C_NOTE] = get_collected_note_count(level);
|
||||
}
|
||||
|
||||
// @recomp Set the number of dynamic notes to respawn for each map in the level.
|
||||
for (s32 map_id = 0; map_id < ARRLEN(map_note_data); map_id++) {
|
||||
MapNoteData *map_data = &map_note_data[map_id];
|
||||
if (map_data->level_id == level) {
|
||||
map_dynamic_note_despawn_counts[map_id] = dynamic_note_collected_count(map_id);
|
||||
}
|
||||
else {
|
||||
map_dynamic_note_despawn_counts[map_id] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @recomp Patched to return true for FILEPROG_99_PAST_50_NOTE_DOOR_TEXT if note saving is enabled.
|
||||
// That flag controls whether to show the "Grunty's magic stops you from taking the notes..." dialog.
|
||||
RECOMP_PATCH bool fileProgressFlag_get(enum file_progress_e index) {
|
||||
if (note_saving_enabled_cached && index == FILEPROG_99_PAST_50_NOTE_DOOR_TEXT) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return bitfield_get_bit(gFileProgressFlags.unk8, index);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef __NOTE_SAVING_H__
|
||||
#define __NOTE_SAVING_H__
|
||||
|
||||
#include "prop.h"
|
||||
|
||||
void init_note_saving();
|
||||
void calculate_map_start_note_indices();
|
||||
|
||||
// Notes are always saved, but this function controls whether to use the saved data to prevent notes from spawning and adjust the note score.
|
||||
bool note_saving_enabled();
|
||||
|
||||
void note_saving_on_map_load();
|
||||
void note_saving_update();
|
||||
void note_saving_handle_static_note(Cube *c, Prop *p);
|
||||
void note_saving_handle_dynamic_note(Actor *actor, ActorMarker *marker);
|
||||
|
||||
#endif
|
||||
@@ -20,6 +20,7 @@
|
||||
#define osCreateMesgQueue osCreateMesgQueue_recomp
|
||||
void osWriteBackDCacheAll(void);
|
||||
#define bzero bzero_recomp
|
||||
#define bcopy bcopy_recomp
|
||||
#define osDpSetStatus osDpSetStatus_recomp
|
||||
#define malloc malloc_recomp
|
||||
#define free free_recomp
|
||||
@@ -40,6 +41,10 @@ void osWriteBackDCacheAll(void);
|
||||
#pragma GCC diagnostic pop
|
||||
#include "rt64_extended_gbi.h"
|
||||
|
||||
#ifndef ARRLEN
|
||||
# define ARRLEN(x) ((s32)(sizeof(x) / sizeof(x[0])))
|
||||
#endif
|
||||
|
||||
#ifndef gEXFillRectangle
|
||||
#define gEXFillRectangle(cmd, lorigin, rorigin, ulx, uly, lrx, lry) \
|
||||
G_EX_COMMAND2(cmd, \
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "bk_api.h"
|
||||
#include "core2/coords.h"
|
||||
#include "core2/file.h"
|
||||
#include "note_saving.h"
|
||||
|
||||
// Max props per cube, limited by cube->prop2Cnt which is only 6 bits.
|
||||
#define CUBE_MAX_PROPS 63
|
||||
@@ -32,6 +33,7 @@ s32 func_803058C0(f32 arg0);
|
||||
void code_A5BC0_initCubePropActorProp(Cube*);
|
||||
void func_80332B2C(ActorMarker * arg0);
|
||||
void bitfield_free(s32 *arg0);
|
||||
void func_8032DE78(SpriteProp *sprite_prop, enum asset_e *sprite_id_ptr);
|
||||
|
||||
extern struct {
|
||||
Cube *cubes;
|
||||
@@ -178,10 +180,24 @@ RECOMP_PATCH void __code7AF80_initCubeFromFile(Cube *cube, File* file_ptr) {
|
||||
}
|
||||
}
|
||||
|
||||
// @recomp Initialize prop handles after loading the cube.
|
||||
// @recomp Initialize prop handles after loading the cube and handle note saving.
|
||||
for (u32 i = 0; i < cube->prop2Cnt; i++) {
|
||||
// TODO prop subtypes.
|
||||
cube_handle->prop_handles[i] = recomp_create_object_data(EXTENSION_TYPE_PROP, 0);
|
||||
|
||||
// Check if this prop is a sprite prop.
|
||||
Prop *prop = &cube->prop2Ptr[i];
|
||||
if (!prop->is_3d && !prop->is_actor) {
|
||||
// Check if this sprite prop is a musical note.
|
||||
|
||||
// Get the asset ID for this sprite prop.
|
||||
enum asset_e sprite_prop_asset_id;
|
||||
func_8032DE78(&prop->spriteProp, &sprite_prop_asset_id);
|
||||
|
||||
if (sprite_prop_asset_id == ASSET_6D6_MODEL_MUSIC_NOTE) {
|
||||
note_saving_handle_static_note(cube, prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef __SAVE_EXTENSION_H__
|
||||
#define __SAVE_EXTENSION_H__
|
||||
|
||||
typedef struct {
|
||||
u8 bytes[32]; // 32*8 = 256 bits per level, enough to account for mods with unused vanilla rooms.
|
||||
} LevelNotes;
|
||||
|
||||
// This struct must be 256 bytes to add up to 1536 bytes, which adds to the original 512 bytes of save data to equal exactly 2048 bytes.
|
||||
typedef struct {
|
||||
LevelNotes level_notes[9];
|
||||
u8 padding[32]; // Reserved for future use.
|
||||
} SaveFileExtensionData;
|
||||
|
||||
_Static_assert(sizeof(SaveFileExtensionData) == 320, "SaveExtensionData must be 256 bytes");
|
||||
|
||||
typedef struct {
|
||||
u8 padding[256];
|
||||
} SaveGlobalExtensionData;
|
||||
|
||||
_Static_assert(sizeof(SaveGlobalExtensionData) == 256, "save_global_extension_data must be 512 bytes");
|
||||
|
||||
extern SaveFileExtensionData loaded_file_extension_data;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,169 @@
|
||||
#include "patches.h"
|
||||
#include "save.h"
|
||||
#include "core1/eeprom.h"
|
||||
#include "save_extension.h"
|
||||
|
||||
// Vanilla declarations.
|
||||
extern SaveData gameFile_saveData[4]; //save_data
|
||||
extern s8 gameFile_GameIdToFileIdMap[4]; //gamenum to filenum
|
||||
extern s32 D_80383F04;
|
||||
|
||||
s32 savedata_8033CA2C(s32 filenum, SaveData *save_data);
|
||||
int savedata_8033CC98(s32 filenum, u8 *buffer);
|
||||
int savedata_8033CCD0(s32 filenum);
|
||||
void __gameFile_8033CE14(s32 gamenum);
|
||||
void savedata_clear(SaveData *savedata);
|
||||
void savedata_update_crc(void *buffer, u32 size);
|
||||
void saveData_load(SaveData *savedata);
|
||||
void saveData_create(SaveData *savedata);
|
||||
|
||||
void bsStoredState_clear(void);
|
||||
void func_8031FFAC(void);
|
||||
void item_setItemsStartCounts(void);
|
||||
void jiggyscore_clearAll(void);
|
||||
void honeycombscore_clear(void);
|
||||
void mumboscore_clear(void);
|
||||
void volatileFlag_clear(void);
|
||||
void func_802D6344(void);
|
||||
|
||||
|
||||
// New declarations.
|
||||
|
||||
// One entry for each file and the backup.
|
||||
SaveFileExtensionData save_file_extension_data[4];
|
||||
SaveFileExtensionData loaded_file_extension_data;
|
||||
|
||||
SaveGlobalExtensionData save_global_extension_data;
|
||||
|
||||
_Static_assert(sizeof(save_file_extension_data) + sizeof(save_global_extension_data) == 1536, "Save extension data must be 1536 bytes");
|
||||
|
||||
// Place save file extensions after the 4K eeprom range.
|
||||
#define SAVE_FILE_EXTENSION_OFFSET (EEPROM_BLOCK_SIZE * EEPROM_MAXBLOCKS)
|
||||
// Number of blocks per save file extension slot.
|
||||
#define SAVE_FILE_EXTENSION_DATA_BLOCK_COUNT (sizeof(SaveFileExtensionData) / EEPROM_BLOCK_SIZE)
|
||||
// Place the global extension data after teh save file extensions
|
||||
#define SAVE_GLOBAL_EXTENSION_OFFSET (SAVE_FILE_EXTENSION_OFFSET + sizeof(save_file_extension_data))
|
||||
|
||||
_Static_assert(sizeof(SaveFileExtensionData) % EEPROM_BLOCK_SIZE == 0, "SaveFileExtensionData size must be a multiple of EEPROM_BLOCK_SIZE");
|
||||
_Static_assert(SAVE_GLOBAL_EXTENSION_OFFSET % EEPROM_BLOCK_SIZE == 0, "SAVE_GLOBAL_EXTENSION_OFFSET must be a multiple of EEPROM_BLOCK_SIZE");
|
||||
|
||||
#define SAVE_FILE_EXTENSION_OFFSET_BLOCKS (SAVE_FILE_EXTENSION_OFFSET / EEPROM_BLOCK_SIZE)
|
||||
#define SAVE_GLOBAL_EXTENSION_OFFSET_BLOCKS (SAVE_GLOBAL_EXTENSION_OFFSET / EEPROM_BLOCK_SIZE)
|
||||
|
||||
// @recomp Patched to load the corresponding file number's extension data.
|
||||
RECOMP_PATCH int __gameFile_8033CD90(s32 filenum){
|
||||
int i;
|
||||
s32 tmp_v1;
|
||||
void *save_data_ptr;
|
||||
save_data_ptr = &gameFile_saveData[filenum];
|
||||
// @recomp Get a pointer to the extension data for this file number.
|
||||
SaveFileExtensionData *extension_ptr = &save_file_extension_data[filenum];
|
||||
i = 3;
|
||||
do{
|
||||
// @recomp Also load the extension data.
|
||||
eeprom_readBlocks(0, SAVE_FILE_EXTENSION_OFFSET_BLOCKS + SAVE_FILE_EXTENSION_DATA_BLOCK_COUNT * filenum, extension_ptr, SAVE_FILE_EXTENSION_DATA_BLOCK_COUNT);
|
||||
|
||||
// Read save data from eeprom for file
|
||||
tmp_v1 = savedata_8033CA2C(filenum, save_data_ptr);
|
||||
|
||||
if(!tmp_v1)
|
||||
break;
|
||||
i--;
|
||||
}while(i != 0);
|
||||
if(tmp_v1) {
|
||||
savedata_clear(save_data_ptr);
|
||||
// @recomp Also clear the extension data.
|
||||
bzero(save_data_ptr, sizeof(*save_data_ptr));
|
||||
}
|
||||
return tmp_v1;
|
||||
}
|
||||
|
||||
// @recomp Patched to save the corresponding file number's extension data.
|
||||
RECOMP_PATCH s32 gameFile_8033CFD4(s32 gamenum){
|
||||
s32 next;
|
||||
s32 filenum;
|
||||
u32 i = 3;
|
||||
s32 eeprom_error;
|
||||
SaveData *save_data;
|
||||
|
||||
|
||||
filenum = D_80383F04;
|
||||
next = gameFile_GameIdToFileIdMap[gamenum];
|
||||
gameFile_GameIdToFileIdMap[gamenum] = D_80383F04;
|
||||
bcopy(&gameFile_saveData[next], &gameFile_saveData[filenum], 0xF*8);
|
||||
// @recomp Also copy the extension data from the next slot.
|
||||
bcopy(&save_file_extension_data[next], &save_file_extension_data[filenum], sizeof(save_file_extension_data[0]));
|
||||
|
||||
save_data = gameFile_saveData + filenum;
|
||||
|
||||
// @recomp Get a pointer to the extension data for this file number.
|
||||
SaveFileExtensionData *extension_ptr = &save_file_extension_data[filenum];
|
||||
|
||||
save_data->slotIndex = gamenum + 1;
|
||||
savedata_update_crc(save_data, sizeof(SaveData));
|
||||
for(eeprom_error = 1; eeprom_error && i > 0; i--){//L8033D070
|
||||
// @recomp Also save the extension data.
|
||||
eeprom_writeBlocks(0, SAVE_FILE_EXTENSION_OFFSET_BLOCKS + SAVE_FILE_EXTENSION_DATA_BLOCK_COUNT * filenum, extension_ptr, SAVE_FILE_EXTENSION_DATA_BLOCK_COUNT);
|
||||
|
||||
eeprom_error = savedata_8033CC98(filenum, save_data);
|
||||
if(!eeprom_error){
|
||||
__gameFile_8033CE14(gamenum);
|
||||
}
|
||||
}
|
||||
if(!eeprom_error){
|
||||
for(i = 3; i > 0; i--){//L8033D070
|
||||
eeprom_error = savedata_8033CCD0(next);
|
||||
if(!eeprom_error)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(eeprom_error){
|
||||
gameFile_GameIdToFileIdMap[gamenum] = next;
|
||||
}
|
||||
else{
|
||||
D_80383F04 = next;
|
||||
}
|
||||
return eeprom_error;
|
||||
}
|
||||
|
||||
// @recomp Patched to clear extended file data.
|
||||
RECOMP_PATCH void gameFile_clear(s32 gamenum){
|
||||
s32 filenum = gameFile_GameIdToFileIdMap[gamenum];
|
||||
savedata_clear(&gameFile_saveData[filenum]);
|
||||
|
||||
// @recomp Clear the extended file data for this file number.
|
||||
bzero(&save_file_extension_data[filenum], sizeof(SaveFileExtensionData));
|
||||
}
|
||||
|
||||
// @recomp Patched to load extended file data.
|
||||
RECOMP_PATCH void gameFile_load(s32 gamenum){
|
||||
s32 filenum = gameFile_GameIdToFileIdMap[gamenum];
|
||||
saveData_load(&gameFile_saveData[filenum]);
|
||||
|
||||
// @recomp Load the extended file data for this file number.
|
||||
memcpy(&loaded_file_extension_data, &save_file_extension_data[filenum], sizeof(SaveFileExtensionData));
|
||||
}
|
||||
|
||||
// @recomp Patched to save extended file data.
|
||||
RECOMP_PATCH void gameFile_save(s32 gamenum){
|
||||
s32 filenum = gameFile_GameIdToFileIdMap[gamenum];
|
||||
saveData_create(&gameFile_saveData[filenum]);
|
||||
|
||||
// @recomp Save the extended file data for this file number.
|
||||
memcpy(&save_file_extension_data[filenum], &loaded_file_extension_data, sizeof(SaveFileExtensionData));
|
||||
}
|
||||
|
||||
// @recomp Patched to clear the current loaded extended file data.
|
||||
RECOMP_PATCH void clearScoreStates(void) {
|
||||
bsStoredState_clear();
|
||||
func_8031FFAC();
|
||||
item_setItemsStartCounts();
|
||||
jiggyscore_clearAll();
|
||||
honeycombscore_clear();
|
||||
mumboscore_clear();
|
||||
volatileFlag_clear();
|
||||
func_802D6344();
|
||||
|
||||
// @recomp Clear the current loaded extended file data.
|
||||
bzero(&loaded_file_extension_data, sizeof(loaded_file_extension_data));
|
||||
}
|
||||
+3
-1
@@ -44,4 +44,6 @@ recomp_get_camera_inputs = 0x8F00009C;
|
||||
recomp_get_analog_cam_enabled = 0x8F0000A0;
|
||||
recomp_get_analog_inverted_axes = 0x8F0000A4;
|
||||
recomp_set_right_analog_suppressed = 0x8F0000A8;
|
||||
osContGetReadData_recomp = 0x8F0000AC;
|
||||
osContGetReadData_recomp = 0x8F0000AC;
|
||||
bcopy_recomp = 0x8F0000B0;
|
||||
recomp_get_note_saving_enabled = 0x8F0000B4;
|
||||
|
||||
@@ -239,6 +239,7 @@ bool save_general_config(const std::filesystem::path& path) {
|
||||
config_json["camera_invert_mode"] = banjo::get_camera_invert_mode();
|
||||
config_json["analog_cam_mode"] = banjo::get_analog_cam_mode();
|
||||
config_json["analog_camera_invert_mode"] = banjo::get_analog_camera_invert_mode();
|
||||
config_json["note_saving_mode"] = banjo::get_note_saving_mode();
|
||||
config_json["debug_mode"] = banjo::get_debug_mode_enabled();
|
||||
|
||||
return save_json_with_backups(path, config_json);
|
||||
@@ -253,6 +254,7 @@ void set_general_settings_from_json(const nlohmann::json& config_json) {
|
||||
banjo::set_camera_invert_mode(from_or_default(config_json, "camera_invert_mode", banjo::CameraInvertMode::InvertY));
|
||||
banjo::set_analog_cam_mode(from_or_default(config_json, "analog_cam_mode", banjo::AnalogCamMode::Off));
|
||||
banjo::set_analog_camera_invert_mode(from_or_default(config_json, "analog_camera_invert_mode", banjo::CameraInvertMode::InvertNone));
|
||||
banjo::set_note_saving_mode(from_or_default(config_json, "note_saving_mode", banjo::NoteSavingMode::On));
|
||||
banjo::set_debug_mode_enabled(from_or_default(config_json, "debug_mode", false));
|
||||
}
|
||||
|
||||
|
||||
@@ -149,6 +149,10 @@ extern "C" void recomp_get_analog_cam_enabled(uint8_t* rdram, recomp_context* ct
|
||||
_return<s32>(ctx, banjo::get_analog_cam_mode() == banjo::AnalogCamMode::On);
|
||||
}
|
||||
|
||||
extern "C" void recomp_get_note_saving_enabled(uint8_t* rdram, recomp_context* ctx) {
|
||||
_return<s32>(ctx, banjo::get_note_saving_mode() == banjo::NoteSavingMode::On);
|
||||
}
|
||||
|
||||
extern "C" void recomp_get_camera_inputs(uint8_t* rdram, recomp_context* ctx) {
|
||||
float* x_out = _arg<0, float*>(rdram, ctx);
|
||||
float* y_out = _arg<1, float*>(rdram, ctx);
|
||||
|
||||
+2
-1
@@ -349,7 +349,8 @@ std::vector<recomp::GameEntry> supported_games = {
|
||||
.internal_name = "Banjo-Kazooie",
|
||||
.game_id = u8"bk.n64.us.1.0",
|
||||
.mod_game_id = "bk",
|
||||
.save_type = recomp::SaveType::Eep4k,
|
||||
// Eep16k instead of Eep4k to have room for extra save file data.
|
||||
.save_type = recomp::SaveType::Eep16k,
|
||||
.is_enabled = false,
|
||||
.decompression_routine = banjo::decompress_bk,
|
||||
.has_compressed_code = true,
|
||||
|
||||
@@ -238,6 +238,7 @@ struct ControlOptionsContext {
|
||||
banjo::CameraInvertMode camera_invert_mode;
|
||||
banjo::AnalogCamMode analog_cam_mode;
|
||||
banjo::CameraInvertMode analog_camera_invert_mode;
|
||||
banjo::NoteSavingMode note_saving_mode;
|
||||
};
|
||||
|
||||
ControlOptionsContext control_options_context;
|
||||
@@ -325,6 +326,17 @@ void banjo::set_analog_cam_mode(banjo::AnalogCamMode mode) {
|
||||
}
|
||||
}
|
||||
|
||||
banjo::NoteSavingMode banjo::get_note_saving_mode() {
|
||||
return control_options_context.note_saving_mode;
|
||||
}
|
||||
|
||||
void banjo::set_note_saving_mode(banjo::NoteSavingMode mode) {
|
||||
control_options_context.note_saving_mode = mode;
|
||||
if (general_model_handle) {
|
||||
general_model_handle.DirtyVariable("note_saving_mode");
|
||||
}
|
||||
}
|
||||
|
||||
banjo::CameraInvertMode banjo::get_analog_camera_invert_mode() {
|
||||
return control_options_context.analog_camera_invert_mode;
|
||||
}
|
||||
@@ -830,6 +842,7 @@ public:
|
||||
bind_option(constructor, "camera_invert_mode", &control_options_context.camera_invert_mode);
|
||||
bind_option(constructor, "analog_cam_mode", &control_options_context.analog_cam_mode);
|
||||
bind_option(constructor, "analog_camera_invert_mode", &control_options_context.analog_camera_invert_mode);
|
||||
bind_option(constructor, "note_saving_mode", &control_options_context.note_saving_mode);
|
||||
|
||||
general_model_handle = constructor.GetModelHandle();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user