diff --git a/patches/rom_map/Bank DF.txt b/patches/rom_map/Bank DF.txt index 8c2fb2474..9dd4efbc0 100644 --- a/patches/rom_map/Bank DF.txt +++ b/patches/rom_map/Bank DF.txt @@ -6,4 +6,7 @@ FEF0 - FEFF: seed name (null-terminated ASCII string) FF04 - FF04: [FREE] FF05 - FF06: randomizer settings: - bitmask $0001: walljump-boots item enabled + - bitmask $0002: split speedbooster item enabled FF07 - FF08: number of frames of artificial lag to add to the unpause black screen +FF09 - FF0D: randomizer settings: skill/progression/qol/objectives/maplayout (autotracker use) + - stored as single byte ordinal numbers, if a setting has a custom option then byte is set to 0 diff --git a/rust/maprando/src/patch.rs b/rust/maprando/src/patch.rs index e42f257ee..7313defed 100644 --- a/rust/maprando/src/patch.rs +++ b/rust/maprando/src/patch.rs @@ -19,8 +19,9 @@ use crate::{ randomize::{LockedDoor, Randomization, get_starting_items}, settings::{ AreaAssignmentPreset, CrashFixes, CrashFixesPreset, DisableETankSetting, ETankRefill, - EnemyDrops, Fanfares, FixMode, ItemCount, MotherBrainFight, Objective, ObjectiveScreen, - RandomizerSettings, SaveAnimals, SpeedBooster, StartLocationMode, WallJump, + EnemyDrops, Fanfares, FixMode, ItemCount, MapPreset, MotherBrainFight, ObjPreset, + Objective, ObjectiveScreen, ProgressionPreset, QolPreset, RandomizerSettings, SaveAnimals, + SkillPreset, SpeedBooster, StartLocationMode, WallJump, }, }; use anyhow::{Context, Result, bail, ensure}; @@ -747,6 +748,7 @@ impl Patcher<'_> { // For now this is just to indicate if walljump-boots exists as an item, // and if Speed Booster is split into Blue Booster and Spark Booster. let mut settings_flag = 0x0000; + if self.settings.other_settings.wall_jump == WallJump::Collectible { settings_flag |= 0x0001; } @@ -1987,6 +1989,75 @@ impl Patcher<'_> { Ok(()) } + fn write_autotracker_skill_setting(&mut self) -> Result<()> { + let settings = &self.settings.skill_assumption_settings; + + let value: u8 = settings + .preset + .as_deref() + .and_then(SkillPreset::from_preset) + .map(SkillPreset::to_byte) + .unwrap_or(0); + + self.rom.write_u8(snes2pc(0xdfff09), value as isize)?; + + Ok(()) + } + + fn write_autotracker_item_progression(&mut self) -> Result<()> { + let settings = &self.settings.item_progression_settings; + + let value: u8 = settings + .preset + .as_deref() + .and_then(ProgressionPreset::from_preset) + .map(ProgressionPreset::to_byte) + .unwrap_or(0); + + self.rom.write_u8(snes2pc(0xdfff0a), value as isize)?; + + Ok(()) + } + + fn write_autotracker_qol_setting(&mut self) -> Result<()> { + let settings = &self.settings.quality_of_life_settings; + + let value: u8 = settings + .preset + .as_deref() + .and_then(QolPreset::from_preset) + .map(QolPreset::to_byte) + .unwrap_or(0); + + self.rom.write_u8(snes2pc(0xdfff0b), value as isize)?; + + Ok(()) + } + + fn write_autotracker_objective_setting(&mut self) -> Result<()> { + let settings = &self.settings.objective_settings; + + let value: u8 = settings + .preset + .as_deref() + .and_then(ObjPreset::from_preset) + .map(ObjPreset::to_byte) + .unwrap_or(0); + + self.rom.write_u8(snes2pc(0xdfff0c), value as isize)?; + + Ok(()) + } + + fn write_autotracker_map_layout(&mut self) -> Result<()> { + let value: u8 = MapPreset::from_preset(&self.settings.map_layout) + .map(MapPreset::to_byte) + .unwrap_or(0); + self.rom.write_u8(snes2pc(0xdfff0d), value as isize)?; + + Ok(()) + } + fn apply_seed_identifiers(&mut self) -> Result<()> { let cartridge_name = "SUPERMETROID MAPRANDO"; self.rom.write_n(0x7FC0, cartridge_name.as_bytes())?; @@ -3642,6 +3713,11 @@ pub fn make_rom( patcher.apply_mother_brain_fight_patches()?; patcher.write_custom_item_graphics()?; patcher.write_objective_data()?; + patcher.write_autotracker_skill_setting()?; + patcher.write_autotracker_item_progression()?; + patcher.write_autotracker_qol_setting()?; + patcher.write_autotracker_objective_setting()?; + patcher.write_autotracker_map_layout()?; patcher.apply_seed_identifiers()?; patcher.apply_credits()?; if randomizer_settings.quality_of_life_settings.hazard_markers { diff --git a/rust/maprando/src/settings.rs b/rust/maprando/src/settings.rs index e3e2cb292..4ad550248 100644 --- a/rust/maprando/src/settings.rs +++ b/rust/maprando/src/settings.rs @@ -103,6 +103,153 @@ pub struct ItemProgressionSettings { pub filler_items: Vec, } +#[repr(u8)] +#[derive(Serialize, Deserialize, Clone, PartialEq)] +pub enum SkillPreset { + Basic = 1, + Medium = 2, + Hard = 3, + VeryHard = 4, + Expert = 5, + ExpertPlus = 6, + Extreme = 7, + ExtremePlus = 8, + Insane = 9, + InsanePlus = 10, +} + +impl SkillPreset { + pub fn from_preset(s: &str) -> Option { + match s.trim().to_lowercase().as_str() { + "basic" => Some(Self::Basic), + "medium" => Some(Self::Medium), + "hard" => Some(Self::Hard), + "very hard" => Some(Self::VeryHard), + "expert" => Some(Self::Expert), + "expert+" => Some(Self::ExpertPlus), + "extreme" => Some(Self::Extreme), + "extreme+" => Some(Self::ExtremePlus), + "insane" => Some(Self::Insane), + "insane+" => Some(Self::InsanePlus), + _ => None, + } + } + + pub fn to_byte(self) -> u8 { + self as u8 + } +} + +#[repr(u8)] +#[derive(Serialize, Deserialize, Clone, PartialEq)] +pub enum QolPreset { + Off = 1, + Low = 2, + Default = 3, + High = 4, + Max = 5, +} + +impl QolPreset { + pub fn from_preset(s: &str) -> Option { + match s.trim().to_lowercase().as_str() { + "off" => Some(Self::Off), + "low" => Some(Self::Low), + "default" => Some(Self::Default), + "high" => Some(Self::High), + "max" => Some(Self::Max), + _ => None, + } + } + + pub fn to_byte(self) -> u8 { + self as u8 + } +} + +#[repr(u8)] +#[derive(Serialize, Deserialize, Clone, PartialEq)] +pub enum ObjPreset { + None = 1, + Bosses = 2, + MiniBosses = 3, + Chozos = 4, + Pirates = 5, + Metroids = 6, + Random = 7, +} + +impl ObjPreset { + pub fn from_preset(s: &str) -> Option { + match s.trim().to_lowercase().as_str() { + "none" => Some(Self::None), + "bosses" => Some(Self::Bosses), + "minibosses" => Some(Self::MiniBosses), + "chozos" => Some(Self::Chozos), + "pirates" => Some(Self::Pirates), + "metroids" => Some(Self::Metroids), + "random" => Some(Self::Random), + _ => None, + } + } + + pub fn to_byte(self) -> u8 { + self as u8 + } +} + +#[repr(u8)] +#[derive(Serialize, Deserialize, Clone, PartialEq)] +pub enum ProgressionPreset { + Normal = 1, + Tricky = 2, + Technical = 3, + Challenge = 4, + Desolate = 5, +} + +impl ProgressionPreset { + pub fn from_preset(s: &str) -> Option { + match s.trim().to_lowercase().as_str() { + "normal" => Some(Self::Normal), + "tricky" => Some(Self::Tricky), + "technical" => Some(Self::Technical), + "challenge" => Some(Self::Challenge), + "desolate" => Some(Self::Desolate), + _ => None, + } + } + + pub fn to_byte(self) -> u8 { + self as u8 + } +} + +#[repr(u8)] +#[derive(Serialize, Deserialize, Clone, PartialEq)] +pub enum MapPreset { + Vanilla = 1, + Small = 2, + Standard = 3, + Wild = 4, +} + +impl MapPreset { + pub fn from_preset(s: &str) -> Option { + match s.trim().to_lowercase().as_str() { + "vanilla" => Some(Self::Vanilla), + "small" => Some(Self::Small), + "standard" => Some(Self::Standard), + "wild" => Some(Self::Wild), + _ => None, + } + } + + pub fn to_byte(self) -> u8 { + self as u8 + } +} + #[derive(Serialize, Deserialize, Clone, PartialEq)] pub struct ItemCount { pub item: Item,