diff --git a/patches/ips/map_progress_maintain.ips b/patches/ips/map_progress_maintain.ips index 945b0cb72..a2837aff3 100644 Binary files a/patches/ips/map_progress_maintain.ips and b/patches/ips/map_progress_maintain.ips differ diff --git a/patches/rom_map/Bank 89.txt b/patches/rom_map/Bank 89.txt index b0854aa85..63b913ae6 100644 --- a/patches/rom_map/Bank 89.txt +++ b/patches/rom_map/Bank 89.txt @@ -1,3 +1,4 @@ AF00 - AF31: ; hud_expansion_opaque.asm AF60 - B0BA: ; Area FX.asm B100 - B200: nothing item graphics (zeros, used only for Bomb Torizo Room; nothing_item.asm) +B200 - B800 : mapstation partial reveal bitmask. \ No newline at end of file diff --git a/patches/rom_map/Bank 90.txt b/patches/rom_map/Bank 90.txt index 1c6e95c77..47a776daa 100644 --- a/patches/rom_map/Bank 90.txt +++ b/patches/rom_map/Bank 90.txt @@ -7,4 +7,4 @@ F980 - F99E: ; respin.asm FC00 - FC0F: ; Fake Lava.asm FC10 - FC1C: ; crash_handle_yapping.asm FC20 - FC3A: ; remove_spikesuit.asm -FC40 - FCBA: ; split_speed.asm +FC40 - FCBA: ; split_speed.asm \ No newline at end of file diff --git a/patches/src/map_progress_maintain.asm b/patches/src/map_progress_maintain.asm index a5849f7a1..7068b672a 100644 --- a/patches/src/map_progress_maintain.asm +++ b/patches/src/map_progress_maintain.asm @@ -1,9 +1,8 @@ arch snes.cpu lorom -!map_station_reveal_type = $90F700 ; 0 = Full reveal, 1 = Partial reveal -!map_reveal_tile_table = $90FA00 ; must match reference in patch.rs -!bank_90_freespace_start = $90F702 +!map_reveal_tile_table = $90FA00 ; must match reference in patch.rs (fn write_map_reveal_tiles) +!bank_90_freespace_start = $90F700 !bank_90_freespace_end = $90F800 @@ -30,9 +29,6 @@ org $90AB6D org $90A98B jmp mark_progress -org !map_station_reveal_type - dw $0000 ; default: full reveal - org !bank_90_freespace_start mark_progress: lda $12 ; Samus X map coordinate @@ -108,26 +104,18 @@ activate_map_station_hook: tax ; X <- map area * $100 ldy #$0080 ; Y <- loop counter (number of words to fill with #$FFFF) - lda !map_station_reveal_type - bne .partial_only_loop .loop: lda $829727, x - sta $702000, x - sta $702700, x + ora $702000, x + sta $702000, x ; maptiles bitmask + lda $89b200, x + ora $702700, x ; don't clear any bits already marked explored as it can break scrolling with sub area reveal set to off. + sta $702700, x ; partially revealed bitmask inx inx dey bne .loop - bra .leave - -.partial_only_loop: - sta $702700, x - inx - inx - dey - bne .partial_only_loop -.leave jsr cross_area_reveal rtl diff --git a/rust/data/map_tiles.json b/rust/data/map_tiles.json index 0ff775b4e..3204f75b4 100644 --- a/rust/data/map_tiles.json +++ b/rust/data/map_tiles.json @@ -256,7 +256,7 @@ "roomId": 301, "roomName": "Kraid Recharge Station", "mapTiles": [ - {"coords": [0, 0], "left": "door", "right": "door", "top": "door", "bottom": "door", "interior": "doubleRefill"} + {"coords": [0, 0], "left": "door", "right": "wall", "top": "wall", "bottom": "wall", "interior": "doubleRefill"} ] }, { diff --git a/rust/data/presets/full-settings/Community Race Season 5.json b/rust/data/presets/full-settings/Community Race Season 5.json index f38d1e186..c4dadc4e6 100644 --- a/rust/data/presets/full-settings/Community Race Season 5.json +++ b/rust/data/presets/full-settings/Community Race Season 5.json @@ -4685,6 +4685,20 @@ "other": "No", "all_areas": false }, + "map_station_activation_settings": { + "preset": "Full", + "save_stations": "Full", + "refill_stations": "Full", + "ship": "Full", + "objectives": "Full", + "area_transitions": "Full", + "items1": "Full", + "items2": "Full", + "items3": "Full", + "items4": "Full", + "other": "Full", + "sub_area": "Same" + }, "item_markers": "4-Tiered", "room_outline_revealed": true, "opposite_area_revealed": true, @@ -4842,7 +4856,6 @@ }, "door_locks_size": "Large", "maps_revealed": null, - "map_station_reveal": "Full", "energy_free_shinesparks": false, "all_enemies_respawn": false, "race_mode": true, diff --git a/rust/data/presets/full-settings/Default.json b/rust/data/presets/full-settings/Default.json index 61233b989..1383c8092 100644 --- a/rust/data/presets/full-settings/Default.json +++ b/rust/data/presets/full-settings/Default.json @@ -4597,6 +4597,20 @@ "other": "No", "all_areas": false }, + "map_station_activation_settings": { + "preset": "Full", + "save_stations": "Full", + "refill_stations": "Full", + "ship": "Full", + "objectives": "Full", + "area_transitions": "Full", + "items1": "Full", + "items2": "Full", + "items3": "Full", + "items4": "Full", + "other": "Full", + "sub_area": "Same" + }, "item_markers": "3-Tiered", "room_outline_revealed": true, "opposite_area_revealed": false, @@ -4753,7 +4767,6 @@ "item_dot_change": "Fade", "transition_letters": true, "door_locks_size": "Large", - "map_station_reveal": "Full", "energy_free_shinesparks": false, "all_enemies_respawn": false, "disable_spikesuit": false, diff --git a/rust/data/presets/full-settings/Mentor Tournament.json b/rust/data/presets/full-settings/Mentor Tournament.json index ec2323008..2bf5fbd83 100644 --- a/rust/data/presets/full-settings/Mentor Tournament.json +++ b/rust/data/presets/full-settings/Mentor Tournament.json @@ -4685,6 +4685,20 @@ "other": "No", "all_areas": false }, + "map_station_activation_settings": { + "preset": "Full", + "save_stations": "Full", + "refill_stations": "Full", + "ship": "Full", + "objectives": "Full", + "area_transitions": "Full", + "items1": "Full", + "items2": "Full", + "items3": "Full", + "items4": "Full", + "other": "Full", + "sub_area": "Same" + }, "item_markers": "4-Tiered", "room_outline_revealed": true, "opposite_area_revealed": true, @@ -4842,7 +4856,6 @@ }, "door_locks_size": "Large", "maps_revealed": null, - "map_station_reveal": "Full", "energy_free_shinesparks": false, "all_enemies_respawn": false, "race_mode": true, diff --git a/rust/data/presets/full-settings/Summer Series Expert Challenge.json b/rust/data/presets/full-settings/Summer Series Expert Challenge.json index 83aec261e..bcf33dadf 100644 --- a/rust/data/presets/full-settings/Summer Series Expert Challenge.json +++ b/rust/data/presets/full-settings/Summer Series Expert Challenge.json @@ -4597,6 +4597,20 @@ "other": "No", "all_areas": false }, + "map_station_activation_settings": { + "preset": "Full", + "save_stations": "Full", + "refill_stations": "Full", + "ship": "Full", + "objectives": "Full", + "area_transitions": "Full", + "items1": "Full", + "items2": "Full", + "items3": "Full", + "items4": "Full", + "other": "Full", + "sub_area": "Same" + }, "item_markers": "4-Tiered", "room_outline_revealed": true, "opposite_area_revealed": true, @@ -4754,7 +4768,6 @@ }, "door_locks_size": "Large", "maps_revealed": null, - "map_station_reveal": "Full", "energy_free_shinesparks": false, "all_enemies_respawn": false, "race_mode": true, diff --git a/rust/data/presets/quality-of-life/Default.json b/rust/data/presets/quality-of-life/Default.json index 46f9ae345..f67c790f2 100644 --- a/rust/data/presets/quality-of-life/Default.json +++ b/rust/data/presets/quality-of-life/Default.json @@ -30,6 +30,20 @@ "other": "No", "all_areas": false }, + "map_station_activation_settings": { + "preset": "Full", + "save_stations": "Full", + "refill_stations": "Full", + "ship": "Full", + "objectives": "Full", + "area_transitions": "Full", + "items1": "Full", + "items2": "Full", + "items3": "Full", + "items4": "Full", + "other": "Full", + "sub_area": "Same" + }, "item_markers": "3-Tiered", "room_outline_revealed": true, "opposite_area_revealed": false, diff --git a/rust/data/presets/quality-of-life/High.json b/rust/data/presets/quality-of-life/High.json index cf4f33c04..cb08afbde 100644 --- a/rust/data/presets/quality-of-life/High.json +++ b/rust/data/presets/quality-of-life/High.json @@ -30,6 +30,20 @@ "other": "No", "all_areas": false }, + "map_station_activation_settings": { + "preset": "Full", + "save_stations": "Full", + "refill_stations": "Full", + "ship": "Full", + "objectives": "Full", + "area_transitions": "Full", + "items1": "Full", + "items2": "Full", + "items3": "Full", + "items4": "Full", + "other": "Full", + "sub_area": "Same" + }, "item_markers": "4-Tiered", "room_outline_revealed": true, "opposite_area_revealed": true, diff --git a/rust/data/presets/quality-of-life/Low.json b/rust/data/presets/quality-of-life/Low.json index a312773b9..6cd0fca4d 100644 --- a/rust/data/presets/quality-of-life/Low.json +++ b/rust/data/presets/quality-of-life/Low.json @@ -30,6 +30,20 @@ "other": "No", "all_areas": false }, + "map_station_activation_settings": { + "preset": "Full", + "save_stations": "Full", + "refill_stations": "Full", + "ship": "Full", + "objectives": "Full", + "area_transitions": "Full", + "items1": "Full", + "items2": "Full", + "items3": "Full", + "items4": "Full", + "other": "Full", + "sub_area": "Same" + }, "item_markers": "Uniques", "room_outline_revealed": false, "opposite_area_revealed": false, diff --git a/rust/data/presets/quality-of-life/Max.json b/rust/data/presets/quality-of-life/Max.json index 9ce6efe10..c2f1c8802 100644 --- a/rust/data/presets/quality-of-life/Max.json +++ b/rust/data/presets/quality-of-life/Max.json @@ -30,6 +30,20 @@ "other": "No", "all_areas": false }, + "map_station_activation_settings": { + "preset": "Full", + "save_stations": "Full", + "refill_stations": "Full", + "ship": "Full", + "objectives": "Full", + "area_transitions": "Full", + "items1": "Full", + "items2": "Full", + "items3": "Full", + "items4": "Full", + "other": "Full", + "sub_area": "Same" + }, "item_markers": "4-Tiered", "room_outline_revealed": true, "opposite_area_revealed": true, diff --git a/rust/data/presets/quality-of-life/Off.json b/rust/data/presets/quality-of-life/Off.json index 3f1702581..b3e42e4dd 100644 --- a/rust/data/presets/quality-of-life/Off.json +++ b/rust/data/presets/quality-of-life/Off.json @@ -30,6 +30,20 @@ "other": "No", "all_areas": false }, + "map_station_activation_settings": { + "preset": "Full", + "save_stations": "Full", + "refill_stations": "Full", + "ship": "Full", + "objectives": "Full", + "area_transitions": "Full", + "items1": "Full", + "items2": "Full", + "items3": "Full", + "items4": "Full", + "other": "Full", + "sub_area": "Same" + }, "item_markers": "Simple", "room_outline_revealed": false, "opposite_area_revealed": false, diff --git a/rust/maprando-web/src/main.rs b/rust/maprando-web/src/main.rs index a49514cbe..cb7e5584f 100644 --- a/rust/maprando-web/src/main.rs +++ b/rust/maprando-web/src/main.rs @@ -437,6 +437,7 @@ struct SeedData { item_progression_preset: Option, difficulty: DifficultyConfig, quality_of_life_preset: Option, + map_station_activation_preset: Option, supers_double: bool, escape_autosave: bool, mother_brain_fight: String, @@ -719,6 +720,11 @@ async fn randomize( race_mode: settings.other_settings.race_mode, preset: settings.skill_assumption_settings.preset.clone(), item_progression_preset: settings.item_progression_settings.preset.clone(), + map_station_activation_preset: settings + .quality_of_life_settings + .map_station_activation_settings + .preset + .map(|p| format!("{:?}", p)), difficulty: output.difficulty_tiers[0].clone(), quality_of_life_preset: settings.quality_of_life_settings.preset.clone(), supers_double: settings.quality_of_life_settings.supers_double, diff --git a/rust/maprando-web/src/randomize_helpers.rs b/rust/maprando-web/src/randomize_helpers.rs index 56dba4f54..2f35ea7dc 100644 --- a/rust/maprando-web/src/randomize_helpers.rs +++ b/rust/maprando-web/src/randomize_helpers.rs @@ -11,8 +11,8 @@ use maprando::{ seed_repository::{Seed, SeedFile}, settings::{ AreaAssignmentBaseOrder, AreaAssignmentPreset, CrashFixesPreset, DisableETankSetting, - DoorLocksSize, ETankRefill, FillerItemPriority, ItemCount, RandomizerSettings, - SpeedBooster, WallJump, get_objective_groups, + DoorLocksSize, ETankRefill, FillerItemPriority, ItemCount, MapStationActivationPreset, + RandomizerSettings, SpeedBooster, WallJump, get_objective_groups, }, spoiler_log::SpoilerLog, spoiler_map, @@ -157,6 +157,18 @@ impl SeedHeaderTemplate<'_> { } } + fn map_station_activation_preset(&self) -> &'static str { + match self + .settings + .quality_of_life_settings + .map_station_activation_settings + .preset + { + Some(MapStationActivationPreset::Partial) => "Partial", + Some(MapStationActivationPreset::Full) => "Full", + None => "Custom", + } + } fn game_variations(&self) -> Vec<&str> { let mut game_variations = vec![]; let other_settings = &self.settings.other_settings; @@ -185,10 +197,6 @@ impl SeedHeaderTemplate<'_> { if other_settings.wall_jump == WallJump::Collectible { game_variations.push("Collectible wall jump"); } - if other_settings.map_station_reveal == maprando::settings::MapStationReveal::Partial { - game_variations.push("Map stations give partial reveal"); - } - if other_settings.energy_free_shinesparks { game_variations.push("Energy-free shinesparks"); } diff --git a/rust/maprando-web/templates/generate/game_variations.html b/rust/maprando-web/templates/generate/game_variations.html index 0af75efea..b0da89593 100644 --- a/rust/maprando-web/templates/generate/game_variations.html +++ b/rust/maprando-web/templates/generate/game_variations.html @@ -58,18 +58,6 @@ -
-
- {% include "help/variations/map_station_reveal.html" %} - -
-
- - - - -
-
{% include "help/variations/energy_free_shinesparks.html" %} diff --git a/rust/maprando-web/templates/generate/help/variations/map_station_reveal.html b/rust/maprando-web/templates/generate/help/quality/map_station_activation.html similarity index 62% rename from rust/maprando-web/templates/generate/help/variations/map_station_reveal.html rename to rust/maprando-web/templates/generate/help/quality/map_station_activation.html index 815b77347..a20d718d5 100644 --- a/rust/maprando-web/templates/generate/help/variations/map_station_reveal.html +++ b/rust/maprando-web/templates/generate/help/quality/map_station_activation.html @@ -1,9 +1,4 @@ - - - - \ No newline at end of file diff --git a/rust/maprando-web/templates/generate/map_station_activation.html b/rust/maprando-web/templates/generate/map_station_activation.html new file mode 100644 index 000000000..1e262e8ca --- /dev/null +++ b/rust/maprando-web/templates/generate/map_station_activation.html @@ -0,0 +1,233 @@ + \ No newline at end of file diff --git a/rust/maprando-web/templates/generate/quality_of_life.html b/rust/maprando-web/templates/generate/quality_of_life.html index 1e7f2a8c1..12946a159 100644 --- a/rust/maprando-web/templates/generate/quality_of_life.html +++ b/rust/maprando-web/templates/generate/quality_of_life.html @@ -76,6 +76,28 @@

Quality-of-life options

+
+
+ + +
+
+ + + + + +
+
+
+
+
Map station activation:
+
{{+ self.map_station_activation_preset() }}
+
Item markers:
{{+ item_markers }}
diff --git a/rust/maprando-web/templates/seed/seed_header.html b/rust/maprando-web/templates/seed/seed_header.html index 36cdbd392..61f29c07a 100644 --- a/rust/maprando-web/templates/seed/seed_header.html +++ b/rust/maprando-web/templates/seed/seed_header.html @@ -155,6 +155,15 @@

+
+
+ Map station activation details +
+
+ {% include "map_station_activation_details.html" %} +
+
+
Crash fix details diff --git a/rust/maprando/src/patch.rs b/rust/maprando/src/patch.rs index 7313defed..0804059bd 100644 --- a/rust/maprando/src/patch.rs +++ b/rust/maprando/src/patch.rs @@ -1231,36 +1231,6 @@ impl Patcher<'_> { Ok(()) } - fn write_area_bitmask(&mut self) -> Result<()> { - let addr = 0x829727; - - for (room_idx, room) in self.game_data.room_geometry.iter().enumerate() { - let room_x = self.rom.read_u8(room.rom_address + 2)?; - let room_y = self.rom.read_u8(room.rom_address + 3)?; - let area = self.map.area[room_idx]; - - for y in 0..room.map.len() { - for x in 0..room.map[y].len() { - if (room.map[y][x] == 0 && room_idx != self.game_data.toilet_room_idx) - || !self.map.room_mask[room_idx] - { - continue; - } - - let (offset, bitmask) = - xy_to_explored_bit_ptr(room_x + x as isize, room_y + y as isize); - - let bit_addr = addr + area * 0x100 + offset as usize; - let mut curr = self.rom.read_u8(snes2pc(bit_addr))?; - curr |= bitmask as isize; - self.rom.write_u8(snes2pc(bit_addr), curr)?; - } - } - } - - Ok(()) - } - fn fix_save_stations(&mut self) -> Result<()> { let save_station_ptrs = vec![ 0x44C5, 0x44D3, 0x44E1, 0x45CF, 0x45DD, 0x45EB, 0x45F9, 0x4607, 0x46D9, 0x46E7, 0x46F5, @@ -3733,7 +3703,6 @@ pub fn make_rom( patcher.apply_mother_brain_setup_asm()?; patcher.apply_extra_setup_asm()?; patcher.write_extra_room_data()?; - patcher.write_area_bitmask()?; info!("CustomizeSettings: {customize_settings:?}"); customize_rom( diff --git a/rust/maprando/src/patch/map_tiles.rs b/rust/maprando/src/patch/map_tiles.rs index dd481d2ba..6a28ce137 100644 --- a/rust/maprando/src/patch/map_tiles.rs +++ b/rust/maprando/src/patch/map_tiles.rs @@ -5,8 +5,8 @@ use crate::{ randomize::{LockedDoor, Randomization}, settings::{ DisableETankSetting, DoorLocksSize, EnhancedMapLevel, EnhancedMapOther, EnhancedMapWalls, - InitialMapRevealSettings, ItemMarkers, MapRevealLevel, MapStationReveal, Objective, - RandomizerSettings, + InitialMapRevealSettings, ItemMarkers, MapRevealLevel, MapStationActivationLevel, + MapStationActivationSubArea, Objective, RandomizerSettings, }, }; use maprando_game::{ @@ -2604,16 +2604,6 @@ impl<'a> MapPatcher<'a> { Ok(()) } - fn set_map_activation_behavior(&mut self) -> Result<()> { - match self.settings.other_settings.map_station_reveal { - MapStationReveal::Partial => { - self.rom.write_u16(snes2pc(0x90F700), 0xFFFF)?; - } - MapStationReveal::Full => {} - } - Ok(()) - } - fn sort_dynamic_tile_data(&mut self) -> Result<()> { let interior_priority = [ MapTileInterior::Empty, @@ -3165,6 +3155,129 @@ impl<'a> MapPatcher<'a> { Ok(()) } + fn write_map_station_bitmasks(&mut self) -> Result<()> { + const FULL_MASK_ADDR: usize = 0x829727; + const PARTIAL_MASK_ADDR: usize = 0x89B200; + let mut map_station_subareas = [None; NUM_AREAS]; + let settings = &self + .settings + .quality_of_life_settings + .map_station_activation_settings; + for (room_idx, room) in self.game_data.room_geometry.iter().enumerate() { + let area = self.map.area[room_idx]; + let subarea = self.map.subarea[room_idx]; + for y in 0..room.map.len() { + for x in 0..room.map[y].len() { + let Some((tile_area, map_x, map_y)) = + self.get_room_coords(room.room_id, x as isize, y as isize) + else { + continue; + }; + let Some(tile) = self.map_tile_map.get(&(tile_area, map_x, map_y)) else { + continue; + }; + if tile.interior == MapTileInterior::MapStation { + map_station_subareas[area] = Some(subarea); + } + } + } + } + for area in 0..NUM_AREAS { + for i in 0..0x100 { + self.rom + .write_u8(snes2pc(FULL_MASK_ADDR + area * 0x100 + i), 0)?; + self.rom + .write_u8(snes2pc(PARTIAL_MASK_ADDR + area * 0x100 + i), 0)?; + } + } + for (room_idx, room) in self.game_data.room_geometry.iter().enumerate() { + let room_x = self.rom.read_u8(room.rom_address + 2)?; + let room_y = self.rom.read_u8(room.rom_address + 3)?; + for y in 0..room.map.len() { + for x in 0..room.map[y].len() { + if (room.map[y][x] == 0 && room_idx != self.game_data.toilet_room_idx) + || !self.map.room_mask[room_idx] + { + continue; + } + let Some((area, map_x, map_y)) = + self.get_room_coords(room.room_id, x as isize, y as isize) + else { + continue; + }; + let Some(tile) = self.map_tile_map.get(&(area, map_x, map_y)) else { + continue; + }; + let mut reveal_level = self.get_map_station_reveal(tile); + let tile_subarea = self.map.subarea[room_idx]; + if let Some(map_station_subarea) = map_station_subareas[area] + && tile_subarea != map_station_subarea + && settings.sub_area != MapStationActivationSubArea::Same + { + reveal_level = match settings.sub_area { + MapStationActivationSubArea::Partial => { + MapStationActivationLevel::Partial + } + MapStationActivationSubArea::No => MapStationActivationLevel::No, + MapStationActivationSubArea::Same => unreachable!(), + }; + } + let local_x = room_x + x as isize; + let local_y = room_y + y as isize; + let (offset, bitmask) = xy_to_explored_bit_ptr(local_x, local_y); + let full_addr = FULL_MASK_ADDR + area * 0x100 + offset as usize; + let partial_addr = PARTIAL_MASK_ADDR + area * 0x100 + offset as usize; + match reveal_level { + MapStationActivationLevel::No => {} + MapStationActivationLevel::Partial => { + let mut partial = self.rom.read_u8(snes2pc(partial_addr))?; + partial |= bitmask as isize; + self.rom.write_u8(snes2pc(partial_addr), partial)?; + } + MapStationActivationLevel::Full => { + let mut full = self.rom.read_u8(snes2pc(full_addr))?; + full |= bitmask as isize; + self.rom.write_u8(snes2pc(full_addr), full)?; + let mut partial = self.rom.read_u8(snes2pc(partial_addr))?; + partial |= bitmask as isize; + self.rom.write_u8(snes2pc(partial_addr), partial)?; + } + } + } + } + } + Ok(()) + } + + fn get_map_station_reveal(&self, tile: &MapTile) -> MapStationActivationLevel { + let settings = &self + .settings + .quality_of_life_settings + .map_station_activation_settings; + if let Some(MapTileSpecialType::AreaTransition(_, _)) = tile.special_type { + return if settings.area_transitions == MapStationActivationLevel::No { + MapStationActivationLevel::No + } else { + MapStationActivationLevel::Full + }; + } + match tile.interior { + MapTileInterior::SaveStation => settings.save_stations, + MapTileInterior::EnergyRefill + | MapTileInterior::AmmoRefill + | MapTileInterior::DoubleRefill => settings.refill_stations, + MapTileInterior::Ship => settings.ship, + MapTileInterior::Objective => settings.objectives, + MapTileInterior::Item | MapTileInterior::DoubleItem | MapTileInterior::HiddenItem => { + settings.items1 + } + MapTileInterior::AmmoItem => settings.items2, + MapTileInterior::MediumItem => settings.items3, + MapTileInterior::MajorItem => settings.items4, + _ => settings.other, + } + } + pub fn compute_area_bounds(&mut self) -> Result<()> { for &(area_idx, x, y) in self.map_tile_map.keys() { if x < self.area_min_x[area_idx] { @@ -3232,7 +3345,6 @@ impl<'a> MapPatcher<'a> { } self.indicate_locked_doors()?; self.add_cross_area_arrows()?; - self.set_map_activation_behavior()?; self.indicate_items()?; self.compute_area_bounds()?; self.write_map_tiles()?; @@ -3248,6 +3360,7 @@ impl<'a> MapPatcher<'a> { } self.fix_kraid()?; self.fix_item_colors()?; + self.write_map_station_bitmasks()?; Ok(()) } diff --git a/rust/maprando/src/settings.rs b/rust/maprando/src/settings.rs index 4ad550248..e65069b65 100644 --- a/rust/maprando/src/settings.rs +++ b/rust/maprando/src/settings.rs @@ -306,6 +306,7 @@ pub struct QualityOfLifeSettings { // Map: pub enhanced_map_settings: EnhancedMapSettings, pub initial_map_reveal_settings: InitialMapRevealSettings, + pub map_station_activation_settings: MapStationActivationSettings, pub item_markers: ItemMarkers, pub room_outline_revealed: bool, pub opposite_area_revealed: bool, @@ -416,6 +417,60 @@ pub struct InitialMapRevealSettings { pub all_areas: bool, } +#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)] +pub enum MapStationActivationLevel { + No, + Partial, + Full, +} + +impl std::fmt::Display for MapStationActivationLevel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)] +pub enum MapStationActivationSubArea { + No, + Partial, + Same, +} + +impl std::fmt::Display for MapStationActivationSubArea { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[derive(Serialize, Deserialize, Clone, PartialEq)] +pub struct MapStationActivationSettings { + pub preset: Option, + pub save_stations: MapStationActivationLevel, + pub refill_stations: MapStationActivationLevel, + pub ship: MapStationActivationLevel, + pub objectives: MapStationActivationLevel, + pub area_transitions: MapStationActivationLevel, + pub items1: MapStationActivationLevel, + pub items2: MapStationActivationLevel, + pub items3: MapStationActivationLevel, + pub items4: MapStationActivationLevel, + pub other: MapStationActivationLevel, + pub sub_area: MapStationActivationSubArea, +} + +#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)] +pub enum MapStationActivationPreset { + Partial, + Full, +} + +impl std::fmt::Display for MapStationActivationPreset { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + #[derive(Serialize, Deserialize, Clone, PartialEq)] pub struct EnhancedMapSettings { pub preset: Option, @@ -592,7 +647,6 @@ pub struct OtherSettings { pub wall_jump: WallJump, pub area_assignment: AreaAssignment, pub door_locks_size: DoorLocksSize, - pub map_station_reveal: MapStationReveal, pub energy_free_shinesparks: bool, pub all_enemies_respawn: bool, pub disable_spikesuit: bool, @@ -835,12 +889,6 @@ pub enum ETankRefill { Full, } -#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)] -pub enum MapStationReveal { - Partial, - Full, -} - #[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)] pub enum SaveAnimals { No, @@ -1169,6 +1217,44 @@ fn upgrade_enhanced_map_settings(settings: &mut serde_json::Value) -> Result<()> Ok(()) } +fn upgrade_map_station_activation_settings(settings: &mut serde_json::Value) -> Result<()> { + // Skip if already present + let qol_settings = settings["quality_of_life_settings"] + .as_object_mut() + .context("missing 'quality_of_life_settings'")?; + + if !qol_settings.contains_key("map_station_activation_settings") { + let msa_settings = MapStationActivationSettings { + preset: Some(MapStationActivationPreset::Full), + save_stations: MapStationActivationLevel::Full, + refill_stations: MapStationActivationLevel::Full, + ship: MapStationActivationLevel::Full, + objectives: MapStationActivationLevel::Full, + area_transitions: MapStationActivationLevel::Full, + items1: MapStationActivationLevel::Full, + items2: MapStationActivationLevel::Full, + items3: MapStationActivationLevel::Full, + items4: MapStationActivationLevel::Full, + other: MapStationActivationLevel::Full, + sub_area: MapStationActivationSubArea::Same, + }; + + qol_settings.insert( + "map_station_activation_settings".to_string(), + serde_json::to_value(msa_settings)?, + ); + } + + let msa_settings = qol_settings["map_station_activation_settings"] + .as_object_mut() + .context("map_station_activation_settings is not object")?; + if !msa_settings.contains_key("sub_area") { + msa_settings.insert("sub_area".to_string(), "Same".into()); + } + + Ok(()) +} + fn upgrade_qol_settings(settings: &mut serde_json::Value) -> Result<()> { let etank_refill = settings["other_settings"]["etank_refill"] .as_str() @@ -1267,6 +1353,7 @@ fn upgrade_qol_settings(settings: &mut serde_json::Value) -> Result<()> { } upgrade_initial_map_reveal_settings(settings)?; + upgrade_map_station_activation_settings(settings)?; upgrade_enhanced_map_settings(settings)?; Ok(()) } diff --git a/rust/maprando/src/traverse.rs b/rust/maprando/src/traverse.rs index 50ed11516..91e8b09c1 100644 --- a/rust/maprando/src/traverse.rs +++ b/rust/maprando/src/traverse.rs @@ -573,31 +573,23 @@ pub struct LockedDoorData { type LocalStateArray = ArrayVec; fn apply_link(link: &Link, mut local: LocalStateArray, cx: &TraversalContext) -> LocalStateArray { - if cx.reverse { - if !link.end_with_shinecharge { - local.retain(|x| x.shinecharge_frames_remaining == 0); - } - } else { - if !link.start_with_shinecharge { - for loc in &mut local { - loc.shinecharge_frames_remaining = 0; - } + if cx.reverse && !link.end_with_shinecharge { + local.retain(|x| x.shinecharge_frames_remaining == 0); + } else if !cx.reverse && !link.start_with_shinecharge { + for loc in &mut local { + loc.shinecharge_frames_remaining = 0; } } local = apply_requirement_complex(&link.requirement, local, cx); - if cx.reverse { - if !link.start_with_shinecharge { - local.retain(|x| x.shinecharge_frames_remaining == 0); - } - } else { - if !link.end_with_shinecharge { - for loc in &mut local { - loc.shinecharge_frames_remaining = 0; - } + if cx.reverse && !link.start_with_shinecharge { + local.retain(|x| x.shinecharge_frames_remaining == 0); + } else if !cx.reverse && !link.end_with_shinecharge { + for loc in &mut local { + loc.shinecharge_frames_remaining = 0; } } - for x in &mut local { - x.length += link.length; + for loc in &mut local { + loc.length += link.length; } local } diff --git a/rust/maprando/tests/logic_scenarios.rs b/rust/maprando/tests/logic_scenarios.rs index 9a5d5c9d9..a17b0ab23 100644 --- a/rust/maprando/tests/logic_scenarios.rs +++ b/rust/maprando/tests/logic_scenarios.rs @@ -6,9 +6,9 @@ use maprando::{ randomize::{DifficultyConfig, LockedDoor, Preprocessor, make_locked_door_data}, settings::{ DisableETankSetting, DoorsSettings, EnemyDrops, EnhancedMapSettings, - InitialMapRevealSettings, ItemProgressionSettings, Objective, ObjectiveSettings, - OtherSettings, QualityOfLifeSettings, RandomizerSettings, SkillAssumptionSettings, - StartLocationSettings, + InitialMapRevealSettings, ItemProgressionSettings, MapStationActivationSettings, Objective, + ObjectiveSettings, OtherSettings, QualityOfLifeSettings, RandomizerSettings, + SkillAssumptionSettings, StartLocationSettings, }, traverse::{LockedDoorData, Traverser}, }; @@ -243,6 +243,20 @@ fn get_settings(scenario: &Scenario) -> Result { other: maprando::settings::MapRevealLevel::No, all_areas: false, }, + map_station_activation_settings: MapStationActivationSettings { + preset: Some(maprando::settings::MapStationActivationPreset::Full), + save_stations: maprando::settings::MapStationActivationLevel::Full, + refill_stations: maprando::settings::MapStationActivationLevel::Full, + ship: maprando::settings::MapStationActivationLevel::Full, + objectives: maprando::settings::MapStationActivationLevel::Full, + area_transitions: maprando::settings::MapStationActivationLevel::Full, + items1: maprando::settings::MapStationActivationLevel::Full, + items2: maprando::settings::MapStationActivationLevel::Full, + items3: maprando::settings::MapStationActivationLevel::Full, + items4: maprando::settings::MapStationActivationLevel::Full, + other: maprando::settings::MapStationActivationLevel::Full, + sub_area: maprando::settings::MapStationActivationSubArea::Same, + }, item_markers: maprando::settings::ItemMarkers::Simple, room_outline_revealed: false, opposite_area_revealed: false, @@ -328,7 +342,6 @@ fn get_settings(scenario: &Scenario) -> Result { maprando::settings::AreaAssignmentPreset::Standard, ), door_locks_size: maprando::settings::DoorLocksSize::Large, - map_station_reveal: maprando::settings::MapStationReveal::Full, energy_free_shinesparks: settings.energy_free_shinesparks.unwrap_or(false), all_enemies_respawn: false, speed_booster: maprando::settings::SpeedBooster::Vanilla,