Skip to content

Commit 059dbf5

Browse files
committed
unlocksDoors tests, and bypassesDoorShell 'free' fix
1 parent 4aba52b commit 059dbf5

36 files changed

Lines changed: 1502 additions & 53 deletions

File tree

rust/maprando-game/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4127,7 +4127,8 @@ impl GameData {
41274127
} else {
41284128
(None, None)
41294129
};
4130-
let bypasses_door_shell = strat_json["bypassesDoorShell"].as_str().unwrap_or("no") == "yes";
4130+
let bypasses_door_shell =
4131+
["yes", "free"].contains(&strat_json["bypassesDoorShell"].as_str().unwrap_or("no"));
41314132
let (exit_condition, exit_req) = if strat_json.has_key("exitCondition") {
41324133
ensure!(strat_json["exitCondition"].is_object());
41334134
let (e, r) = self.parse_exit_condition(
@@ -4188,7 +4189,7 @@ impl GameData {
41884189
let strat_name = strat_json["name"].as_str().unwrap().to_string();
41894190
let strat_notes = self.parse_note(&strat_json["note"]);
41904191

4191-
if bypasses_door_shell {
4192+
if strat_json["bypassesDoorShell"].as_str().unwrap_or("no") == "yes" {
41924193
requires_vec.push(Requirement::Tech(
41934194
self.tech_isv.index_by_key[&TECH_ID_CAN_SKIP_DOOR_LOCK],
41944195
));

rust/maprando/src/randomize.rs

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2521,10 +2521,7 @@ impl<'a> Preprocessor<'a> {
25212521
}
25222522
}
25232523

2524-
fn get_come_in_with_r_mode_reqs(
2525-
&self,
2526-
exit_condition: &ExitCondition,
2527-
) -> Option<Requirement> {
2524+
fn get_come_in_with_r_mode_reqs(&self, exit_condition: &ExitCondition) -> Option<Requirement> {
25282525
match exit_condition {
25292526
ExitCondition::LeaveWithGModeSetup { .. } => {
25302527
let reqs: Vec<Requirement> = vec![
@@ -3149,6 +3146,49 @@ fn get_walls(map: &Map, game_data: &GameData) -> Vec<DoorPtrPair> {
31493146
out
31503147
}
31513148

3149+
pub fn make_locked_door_data(
3150+
locked_doors: Vec<LockedDoor>,
3151+
game_data: &GameData,
3152+
) -> LockedDoorData {
3153+
let mut locked_door_node_map: HashMap<(RoomId, NodeId), usize> = HashMap::new();
3154+
for (i, door) in locked_doors.iter().enumerate() {
3155+
let (src_room_id, src_node_id) = game_data.door_ptr_pair_map[&door.src_ptr_pair];
3156+
locked_door_node_map.insert((src_room_id, src_node_id), i);
3157+
if door.bidirectional {
3158+
let (dst_room_id, dst_node_id) = game_data.door_ptr_pair_map[&door.dst_ptr_pair];
3159+
locked_door_node_map.insert((dst_room_id, dst_node_id), i);
3160+
}
3161+
}
3162+
3163+
// Homing Geemer Room left door -> West Ocean Bridge left door
3164+
if let Some(&idx) = locked_door_node_map.get(&(313, 1)) {
3165+
locked_door_node_map.insert((32, 7), idx);
3166+
}
3167+
3168+
// Homing Geemer Room right door -> West Ocean Bridge right door
3169+
if let Some(&idx) = locked_door_node_map.get(&(313, 2)) {
3170+
locked_door_node_map.insert((32, 8), idx);
3171+
}
3172+
3173+
// Pants Room right door -> East Pants Room right door
3174+
if let Some(&idx) = locked_door_node_map.get(&(322, 2)) {
3175+
locked_door_node_map.insert((220, 2), idx);
3176+
}
3177+
3178+
let mut locked_door_vertex_ids = vec![vec![]; locked_doors.len()];
3179+
for (&(room_id, node_id), vertex_ids) in &game_data.node_door_unlock {
3180+
if let Some(&locked_door_idx) = locked_door_node_map.get(&(room_id, node_id)) {
3181+
locked_door_vertex_ids[locked_door_idx].extend(vertex_ids);
3182+
}
3183+
}
3184+
3185+
LockedDoorData {
3186+
locked_doors,
3187+
locked_door_node_map,
3188+
locked_door_vertex_ids,
3189+
}
3190+
}
3191+
31523192
pub fn randomize_doors(
31533193
game_data: &GameData,
31543194
map: &Map,
@@ -3239,43 +3279,7 @@ pub fn randomize_doors(
32393279
});
32403280
}
32413281

3242-
let mut locked_door_node_map: HashMap<(RoomId, NodeId), usize> = HashMap::new();
3243-
for (i, door) in locked_doors.iter().enumerate() {
3244-
let (src_room_id, src_node_id) = game_data.door_ptr_pair_map[&door.src_ptr_pair];
3245-
locked_door_node_map.insert((src_room_id, src_node_id), i);
3246-
if door.bidirectional {
3247-
let (dst_room_id, dst_node_id) = game_data.door_ptr_pair_map[&door.dst_ptr_pair];
3248-
locked_door_node_map.insert((dst_room_id, dst_node_id), i);
3249-
}
3250-
}
3251-
3252-
// Homing Geemer Room left door -> West Ocean Bridge left door
3253-
if let Some(&idx) = locked_door_node_map.get(&(313, 1)) {
3254-
locked_door_node_map.insert((32, 7), idx);
3255-
}
3256-
3257-
// Homing Geemer Room right door -> West Ocean Bridge right door
3258-
if let Some(&idx) = locked_door_node_map.get(&(313, 2)) {
3259-
locked_door_node_map.insert((32, 8), idx);
3260-
}
3261-
3262-
// Pants Room right door -> East Pants Room right door
3263-
if let Some(&idx) = locked_door_node_map.get(&(322, 2)) {
3264-
locked_door_node_map.insert((220, 2), idx);
3265-
}
3266-
3267-
let mut locked_door_vertex_ids = vec![vec![]; locked_doors.len()];
3268-
for (&(room_id, node_id), vertex_ids) in &game_data.node_door_unlock {
3269-
if let Some(&locked_door_idx) = locked_door_node_map.get(&(room_id, node_id)) {
3270-
locked_door_vertex_ids[locked_door_idx].extend(vertex_ids);
3271-
}
3272-
}
3273-
3274-
LockedDoorData {
3275-
locked_doors,
3276-
locked_door_node_map,
3277-
locked_door_vertex_ids,
3278-
}
3282+
make_locked_door_data(locked_doors, game_data)
32793283
}
32803284

32813285
// An inexpensive way to evaluate whether a requirement may be possible to satisfy,

rust/maprando/tests/logic_scenarios.rs

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::{env, path::Path};
33
use anyhow::{Context, Result, bail};
44
use hashbrown::HashMap;
55
use maprando::{
6-
randomize::{DifficultyConfig, Preprocessor},
6+
randomize::{DifficultyConfig, LockedDoor, Preprocessor, make_locked_door_data},
77
settings::{
88
DisableETankSetting, InitialMapRevealSettings, ItemProgressionSettings, Objective,
99
ObjectiveSettings, OtherSettings, QualityOfLifeSettings, RandomizerSettings,
@@ -12,8 +12,8 @@ use maprando::{
1212
traverse::{LockedDoorData, Traverser},
1313
};
1414
use maprando_game::{
15-
Capacity, GameData, LinksDataGroup, NodeId, NotableId, ObstacleMask, RoomId, VertexId,
16-
VertexKey,
15+
BeamType, Capacity, DoorPtrPair, DoorType, GameData, LinksDataGroup, NodeId, NotableId,
16+
ObstacleMask, RoomId, VertexId, VertexKey,
1717
};
1818
use maprando_logic::{GlobalState, Inventory, LocalState, ResourceLevel};
1919
use serde::Deserialize;
@@ -45,6 +45,8 @@ struct Scenario {
4545
#[serde(default)]
4646
settings: ScenarioSettings,
4747
global_state: Option<ScenarioGlobalState>,
48+
#[serde(default)]
49+
locked_doors: Vec<ScenarioLockedDoor>,
4850
start_room_id: usize,
4951
start_node_id: usize,
5052
#[serde(default)]
@@ -111,6 +113,28 @@ struct ScenarioGlobalState {
111113
pool_max_power_bombs: Option<Capacity>,
112114
}
113115

116+
#[derive(Debug, Deserialize)]
117+
#[serde(rename_all = "camelCase")]
118+
struct ScenarioLockedDoor {
119+
room_id: RoomId,
120+
node_id: NodeId,
121+
type_: ScenarioLockedDoorType,
122+
}
123+
124+
#[derive(Debug, Deserialize)]
125+
#[serde(rename_all = "camelCase")]
126+
enum ScenarioLockedDoorType {
127+
Red,
128+
Green,
129+
Yellow,
130+
Charge,
131+
Ice,
132+
Spazer,
133+
Wave,
134+
Plasma,
135+
Wall,
136+
}
137+
114138
#[derive(Debug, Deserialize)]
115139
#[serde(rename_all = "camelCase")]
116140
struct ScenarioNotable {
@@ -355,6 +379,7 @@ fn get_global_state(
355379
game_data: &GameData,
356380
difficulty: &DifficultyConfig,
357381
scenario: &Scenario,
382+
locked_door_data: &LockedDoorData,
358383
) -> Result<GlobalState> {
359384
let mut flags = vec![false; game_data.flag_isv.keys.len()];
360385

@@ -449,7 +474,7 @@ fn get_global_state(
449474
inventory,
450475
pool_inventory,
451476
flags,
452-
doors_unlocked: vec![],
477+
doors_unlocked: vec![false; locked_door_data.locked_doors.len()],
453478
weapon_mask,
454479
})
455480
}
@@ -542,7 +567,31 @@ fn test_scenario(
542567
game_data.vertex_isv.keys.len(),
543568
game_data.base_links_data.links.len(),
544569
);
545-
let global_state = get_global_state(game_data, &difficulty, scenario)?;
570+
571+
let mut locked_doors: Vec<LockedDoor> = vec![];
572+
for door in scenario.locked_doors.iter() {
573+
let door_ptr_pair = get_door_ptr_pair(door.room_id, door.node_id);
574+
let door_type = match door.type_ {
575+
ScenarioLockedDoorType::Red => DoorType::Red,
576+
ScenarioLockedDoorType::Green => DoorType::Green,
577+
ScenarioLockedDoorType::Yellow => DoorType::Yellow,
578+
ScenarioLockedDoorType::Charge => DoorType::Beam(BeamType::Charge),
579+
ScenarioLockedDoorType::Ice => DoorType::Beam(BeamType::Ice),
580+
ScenarioLockedDoorType::Spazer => DoorType::Beam(BeamType::Spazer),
581+
ScenarioLockedDoorType::Wave => DoorType::Beam(BeamType::Wave),
582+
ScenarioLockedDoorType::Plasma => DoorType::Beam(BeamType::Plasma),
583+
ScenarioLockedDoorType::Wall => DoorType::Wall,
584+
};
585+
locked_doors.push(LockedDoor {
586+
src_ptr_pair: door_ptr_pair,
587+
dst_ptr_pair: (None, None),
588+
door_type,
589+
bidirectional: false,
590+
});
591+
}
592+
let locked_door_data = make_locked_door_data(locked_doors, game_data);
593+
594+
let global_state = get_global_state(game_data, &difficulty, scenario, &locked_door_data)?;
546595
let start_local_state = get_local_state(&scenario.start_state);
547596
let end_local_state = get_local_state(&scenario.end_state);
548597
let objectives = vec![
@@ -585,11 +634,6 @@ fn test_scenario(
585634
.context("End vertex not found")?;
586635

587636
let num_vertices = game_data.vertex_isv.keys.len();
588-
let locked_door_data = LockedDoorData {
589-
locked_doors: vec![],
590-
locked_door_node_map: HashMap::new(),
591-
locked_door_vertex_ids: vec![],
592-
};
593637
let inventory = &global_state.inventory;
594638

595639
for (state, name) in [(start_local_state, "start"), (end_local_state, "end")] {
@@ -834,6 +878,11 @@ fn test_scenario(
834878
Ok(())
835879
}
836880

881+
fn get_door_ptr_pair(room_id: RoomId, node_id: NodeId) -> DoorPtrPair {
882+
let fake_ptr = (room_id << 16) | node_id;
883+
(Some(fake_ptr), Some(fake_ptr))
884+
}
885+
837886
#[test]
838887
fn test_logic_scenarios() -> Result<()> {
839888
let base_game_data = GameData::load_minimal(Path::new(".."))?;
@@ -849,6 +898,16 @@ fn test_logic_scenarios() -> Result<()> {
849898
game_data.load_rooms(&room_pattern)?;
850899
game_data.make_links_data(&|_, _| (0, 1));
851900

901+
for &(room_id, node_id) in game_data.node_json_map.keys().clone() {
902+
let node_json = &game_data.node_json_map[&(room_id, node_id)];
903+
if node_json["nodeType"] == "door" {
904+
let ptr_pair = get_door_ptr_pair(room_id, node_id);
905+
game_data
906+
.door_ptr_pair_map
907+
.insert(ptr_pair, (room_id, node_id));
908+
}
909+
}
910+
852911
let connections_path = entry.path().join("connections.json");
853912
let connections_list: ConnectionsList = if connections_path.exists() || num_rooms > 1 {
854913
let connections_str = std::fs::read_to_string(connections_path.clone())
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"$schema": "../../../../../sm-json-data/schema/m3-room.schema.json",
3+
"roomEnvironments": [{"heated": false}],
4+
"id": 0,
5+
"roomAddress": "0x0000",
6+
"name": "Test Room",
7+
"area": "Test Area",
8+
"subarea": "Test Subarea",
9+
"mapTileMask": [
10+
[1]
11+
],
12+
"nodes": [
13+
{
14+
"id": 1,
15+
"name": "Starting Node",
16+
"nodeType": "junction",
17+
"nodeSubType": "junction",
18+
"mapTileMask": [
19+
[1]
20+
]
21+
},
22+
{
23+
"id": 2,
24+
"name": "Ending Node",
25+
"nodeType": "junction",
26+
"nodeSubType": "junction",
27+
"mapTileMask": [
28+
[1]
29+
]
30+
}
31+
],
32+
"strats": [
33+
{
34+
"link": [1, 2],
35+
"name": "Base",
36+
"requires": [
37+
{"electricityHits": 2}
38+
],
39+
"flashSuitChecked": true
40+
}
41+
]
42+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"$schema": "../../schema/scenarios.schema.json",
3+
"scenarios": [
4+
{
5+
"name": "Enough Energy",
6+
"startRoomId": 0,
7+
"startNodeId": 1,
8+
"startState": {
9+
"energy": {"remaining": 61}
10+
},
11+
"endRoomId": 0,
12+
"endNodeId": 2
13+
},
14+
{
15+
"name": "Not Enough Energy",
16+
"globalState": {
17+
"maxEnergy": 199
18+
},
19+
"startRoomId": 0,
20+
"startNodeId": 1,
21+
"startState": {
22+
"energy": {"remaining": 60}
23+
},
24+
"endRoomId": 0,
25+
"endNodeId": 2,
26+
"fail": true
27+
},
28+
{
29+
"name": "Reserves",
30+
"globalState": {
31+
"maxReserves": 100
32+
},
33+
"startRoomId": 0,
34+
"startNodeId": 1,
35+
"startState": {
36+
"reserves": {"remaining": 60}
37+
},
38+
"endRoomId": 0,
39+
"endNodeId": 2,
40+
"reverse": "weak"
41+
}
42+
]
43+
}

0 commit comments

Comments
 (0)