diff --git a/Cargo.lock b/Cargo.lock index c449041..e82f6c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,6 +102,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", + "lazy_static", "rand", "serde", "serde_json", diff --git a/enemize/Cargo.toml b/enemize/Cargo.toml index a763a91..6a55151 100644 --- a/enemize/Cargo.toml +++ b/enemize/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] anyhow = "1" clap = { version = "3.1.18", features = ["derive"] } +lazy_static = "1.4" rand = "0.8" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/enemize/src/graph/dungeons.rs b/enemize/src/graph/dungeons.rs new file mode 100644 index 0000000..5d5bfbc --- /dev/null +++ b/enemize/src/graph/dungeons.rs @@ -0,0 +1,144 @@ +use std::collections::HashMap; + +use lazy_static::lazy_static; + +use super::item::ItemId; + +#[derive(Debug, thiserror::Error)] +#[error("{0} is not a dungeon room")] +pub struct NotADungeonRoom(u8); + +#[derive(Clone, Copy, Eq, Hash, PartialEq)] +pub enum Dungeon { + HyruleCastle, + EasternPalace, + DesertPalace, + TowerOfHera, + AgahnimsTower, + PalaceOfDarkness, + SwampPalace, + SkullWoods, + ThievesTown, + IcePalace, + MiseryMire, + TurtleRock, + GanonsTower, +} + +pub fn get_dungeon_from_room(room_id: u8) -> Result { + for (dungeon, rooms) in DUNGEON_ROOMS.iter() { + if let Ok(_) = rooms.binary_search(&room_id) { + return Ok(*dungeon); + } + } + + Err(NotADungeonRoom(room_id)) +} + +const HYRULE_CASTLE_ROOMS: [u8; 21] = [ + 1, 2, 17, 18, 33, 34, 50, 65, 66, 80, 81, 82, 96, 97, 98, 112, 113, 114, 128, 129, 130, +]; + +const EASTERN_PALACE_ROOMS: [u8; 13] = [ + 137, 153, 168, 169, 170, 184, 185, 186, 200, 201, 216, 217, 218, +]; + +const DESERT_PALACE_ROOMS: [u8; 10] = [51, 67, 83, 99, 115, 116, 117, 131, 132, 133]; + +const TOWER_OF_HERA_ROOMS: [u8; 7] = [7, 23, 39, 49, 119, 135, 167]; + +const AGAHNIMS_TOWER_ROOMS: [u8; 7] = [32, 48, 64, 176, 192, 208, 224]; + +const PALACE_OF_DARKNESS_ROOMS: [u8; 14] = [9, 10, 11, 25, 26, 27, 42, 43, 58, 59, 74, 75, 90, 106]; + +const SWAMP_PALACE_ROOMS: [u8; 13] = [6, 22, 38, 40, 52, 53, 54, 55, 56, 70, 84, 102, 118]; + +const SKULL_WOODS_ROOMS: [u8; 9] = [41, 57, 73, 86, 87, 88, 89, 103, 104]; + +const THIEVES_TOWN_ROOMS: [u8; 12] = [68, 69, 100, 101, 171, 172, 187, 188, 203, 204, 219, 220]; + +const ICE_PALACE_ROOMS: [u8; 22] = [ + 14, 30, 31, 46, 62, 63, 78, 79, 94, 95, 110, 126, 127, 142, 158, 159, 174, 175, 190, 191, 206, + 222, +]; + +const MISERY_MIRE_ROOMS: [u8; 18] = [ + 144, 145, 146, 147, 151, 152, 160, 161, 162, 163, 177, 178, 179, 193, 194, 195, 209, 210, +]; + +const TURTLE_ROCK_ROOMS: [u8; 17] = [ + 4, 19, 20, 21, 35, 36, 164, 180, 181, 182, 183, 196, 197, 198, 199, 213, 214, +]; + +const GANONS_TOWER_ROOMS: [u8; 26] = [ + 12, 13, 28, 29, 61, 76, 77, 91, 92, 93, 107, 108, 109, 123, 124, 125, 139, 140, 141, 149, 150, + 155, 156, 157, 165, 166, +]; + +lazy_static! { + static ref DUNGEON_ROOMS: HashMap = { + use Dungeon::*; + + let mut rooms = HashMap::new(); + + rooms.insert(HyruleCastle, &HYRULE_CASTLE_ROOMS[..]); + rooms.insert(EasternPalace, &EASTERN_PALACE_ROOMS[..]); + rooms.insert(DesertPalace, &DESERT_PALACE_ROOMS[..]); + rooms.insert(TowerOfHera, &TOWER_OF_HERA_ROOMS[..]); + rooms.insert(AgahnimsTower, &AGAHNIMS_TOWER_ROOMS[..]); + rooms.insert(PalaceOfDarkness, &PALACE_OF_DARKNESS_ROOMS[..]); + rooms.insert(SwampPalace, &SWAMP_PALACE_ROOMS[..]); + rooms.insert(SkullWoods, &SKULL_WOODS_ROOMS[..]); + rooms.insert(ThievesTown, &THIEVES_TOWN_ROOMS[..]); + rooms.insert(IcePalace, &ICE_PALACE_ROOMS[..]); + rooms.insert(MiseryMire, &MISERY_MIRE_ROOMS[..]); + rooms.insert(TurtleRock, &TURTLE_ROCK_ROOMS[..]); + rooms.insert(GanonsTower, &GANONS_TOWER_ROOMS[..]); + + rooms + }; + pub static ref DUNGEON_KEYS: HashMap = { + use Dungeon::*; + use ItemId::*; + + let mut keys = HashMap::new(); + + keys.insert(HyruleCastle, HyruleKey); + keys.insert(EasternPalace, EasternKey); + keys.insert(DesertPalace, DesertKey); + keys.insert(TowerOfHera, HeraKey); + keys.insert(AgahnimsTower, AgaKey); + keys.insert(PalaceOfDarkness, PodKey); + keys.insert(SwampPalace, SwampKey); + keys.insert(SkullWoods, SkullKey); + keys.insert(ThievesTown, ThievesKey); + keys.insert(IcePalace, IceKey); + keys.insert(MiseryMire, MireKey); + keys.insert(TurtleRock, TurtleKey); + keys.insert(GanonsTower, GTKey); + + keys + }; + pub static ref DUNGEON_BIG_KEYS: HashMap = { + use Dungeon::*; + use ItemId::*; + + let mut keys = HashMap::new(); + + keys.insert(HyruleCastle, HyruleBigKey); + keys.insert(EasternPalace, EasternBigKey); + keys.insert(DesertPalace, DesertBigKey); + keys.insert(TowerOfHera, HeraBigKey); + keys.insert(AgahnimsTower, AgaBigKey); + keys.insert(PalaceOfDarkness, PodBigKey); + keys.insert(SwampPalace, SwampBigKey); + keys.insert(SkullWoods, SkullBigKey); + keys.insert(ThievesTown, ThievesBigKey); + keys.insert(IcePalace, IceBigKey); + keys.insert(MiseryMire, MireBigKey); + keys.insert(TurtleRock, TurtleBigKey); + keys.insert(GanonsTower, GTBigKey); + + keys + }; +} diff --git a/enemize/src/graph/item.rs b/enemize/src/graph/item.rs new file mode 100644 index 0000000..13f3f16 --- /dev/null +++ b/enemize/src/graph/item.rs @@ -0,0 +1,554 @@ +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; + +use lazy_static::lazy_static; + +#[derive(Clone, Copy)] +pub enum ItemType { + Normal, + Bottle, + Special, + Progressive { level: u8 }, + Consumable { found: u8, used: u8 }, +} + +impl ItemType { + pub fn level(&self) -> u8 { + match self { + Self::Progressive { level } => *level, + _ => 0, + } + } + + pub fn increase_level(&mut self) { + match self { + Self::Progressive { ref mut level } => *level += 1, + _ => (), + } + } + + pub fn found(&self) -> u8 { + match self { + Self::Consumable { found, .. } => *found, + _ => 0, + } + } + + pub fn increase_count(&mut self) { + match self { + Self::Consumable { ref mut found, .. } => *found += 1, + _ => (), + } + } + + pub fn used(&self) -> u8 { + match self { + Self::Consumable { used, .. } => *used, + _ => 0, + } + } + + pub fn consume(&mut self) { + match self { + Self::Consumable { ref mut used, .. } => *used += 1, + _ => (), + } + } + + pub fn usable(&self) -> bool { + match self { + Self::Consumable { found, used } => found > used, + _ => false, + } + } +} + +#[derive(Clone, Copy)] +pub struct Item { + pub id: ItemId, + pub name: &'static str, + pub item_type: ItemType, +} + +impl Item { + pub fn normal(id: ItemId, name: &'static str) -> Self { + Self { + id, + name, + item_type: ItemType::Normal, + } + } + + pub fn bottle(id: ItemId, name: &'static str) -> Self { + Self { + id, + name, + item_type: ItemType::Bottle, + } + } + + pub fn special(id: ItemId, name: &'static str) -> Self { + Self { + id, + name, + item_type: ItemType::Special, + } + } + + pub fn progressive(id: ItemId, name: &'static str) -> Self { + Self { + id, + name, + item_type: ItemType::Progressive { level: 0 }, + } + } + + pub fn consumable(id: ItemId, name: &'static str) -> Self { + Self { + id, + name, + item_type: ItemType::Consumable { found: 0, used: 0 }, + } + } +} + +impl PartialEq for Item { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Eq for Item {} + +impl Hash for Item { + fn hash(&self, state: &mut H) { + self.id.hash(state); + } +} + +#[derive(Clone, Copy, Eq, Hash, PartialEq)] +#[repr(u32)] +pub enum ItemId { + FightersSwordAndShield = 0x00, + MasterSword1 = 0x01, + TemperedSword = 0x02, + GoldenSword = 0x03, + FightersShield = 0x04, + FireShield = 0x05, + MirrorShield = 0x06, + FireRod = 0x07, + IceRod = 0x08, + Hammer = 0x09, + Hookshot = 0x0a, + Bow = 0x0b, + Boomerang = 0x0c, + Powder = 0x0d, + Bee = 0x0e, + Bombos = 0x0f, + Ether = 0x10, + Quake = 0x11, + Lamp = 0x12, + Shovel = 0x13, + Flute = 0x14, + Somaria = 0x15, + Bottle = 0x16, + PieceOfHeart = 0x17, + Byrna = 0x18, + Cape = 0x19, + Mirror = 0x1a, + L1Gloves = 0x1b, + L2Gloves = 0x1c, + Book = 0x1d, + Flippers = 0x1e, + MoonPearl = 0x1f, + Crystal = 0x20, + BugCatchingNet = 0x21, + BlueMail = 0x22, + RedMail = 0x23, + Key = 0x24, + Compass = 0x25, + HeartContainerNoAnimation = 0x26, + Bomb = 0x27, + ThreeBombs = 0x28, + Mushroom = 0x29, + MagicalBoomerang = 0x2a, + BottleRedPotion = 0x2b, + BottleGreenPotion = 0x2c, + BottleBluePotion = 0x2d, + RedPotion = 0x2e, + GreenPotion = 0x2f, + BluePotion = 0x30, + TenBombs = 0x31, + BigKey = 0x32, + DungeonMap = 0x33, + GreenRupee = 0x34, + BlueRupee = 0x35, + RedRupee = 0x36, + PendantGreen = 0x37, + PendantRed = 0x38, + PendantBlue = 0x39, + BowAndArrows = 0x3a, + BowAndSilverArrows = 0x3b, + BottleBee = 0x3c, + BottleFairy = 0x3d, + HeartContainer = 0x3e, + HeartContainerRefill = 0x3f, + OneHundredRupees = 0x40, + FiftyRupees = 0x41, + Heart = 0x42, + SingleArrow = 0x43, + TenArrows = 0x44, + SmallMagic = 0x45, + ThreeHundredRupees = 0x46, + RedRupee2 = 0x47, + BottleGoldenBee = 0x48, + FightersSword = 0x49, + FluteActive = 0x4a, + Boots = 0x4b, + MaxBombs = 0x4c, + MaxArrows = 0x4d, + HalfMagic = 0x4e, + QuarterMagic = 0x4f, + MasterSword2 = 0x50, + BombUpgrade5 = 0x51, + BombUpgrade10 = 0x52, + ArrowUpgrade5 = 0x53, + ArrowUpgrade10 = 0x54, + Programmable1 = 0x55, + Programmable2 = 0x56, + Programmable3 = 0x57, + SilverArrows = 0x58, + Rupoor = 0x59, + Nothing = 0x5a, + RedClock = 0x5b, + BlueClock = 0x5c, + GreenClock = 0x5d, + ProgressiveSword = 0x5e, + ProgressiveShield = 0x5f, + ProgressiveArmor = 0x60, + ProgressiveGloves = 0x61, + UniqueRngItem = 0x62, + NonUniqueRngItem = 0x63, + ProgressiveBow = 0x64, + ProgressiveBow2 = 0x65, + Triforce = 0x6a, + PowerStar = 0x6b, + TriforcePiece = 0x6c, + LightWorldMap = 0x70, + DarkWorldMap = 0x71, + GTMap = 0x72, + TurtleMap = 0x73, + ThievesMap = 0x74, + HeraMap = 0x75, + IceMap = 0x76, + SkullMap = 0x77, + MireMap = 0x78, + PodMap = 0x79, + SwampMap = 0x7a, + AgaMap = 0x7b, + DesertMap = 0x7c, + EasternMap = 0x7d, + HyruleMap = 0x7e, + SewersMap = 0x7f, + GTCompass = 0x82, + TurtleCompass = 0x83, + ThievesCompass = 0x84, + HeraCompass = 0x85, + IceCompass = 0x86, + SkullCompass = 0x87, + MireCompass = 0x88, + PodCompass = 0x89, + SwampCompass = 0x8a, + AgaCompass = 0x8b, + DesertCompass = 0x8c, + EasternCompass = 0x8d, + HyruleCompass = 0x8e, + SewersCompass = 0x8f, + SkeletonKey = 0x90, + Reserved0x91 = 0x91, + GTBigKey = 0x92, + TurtleBigKey = 0x93, + ThievesBigKey = 0x94, + HeraBigKey = 0x95, + IceBigKey = 0x96, + SkullBigKey = 0x97, + MireBigKey = 0x98, + PodBigKey = 0x99, + SwampBigKey = 0x9a, + AgaBigKey = 0x9b, + DesertBigKey = 0x9c, + EasternBigKey = 0x9d, + HyruleBigKey = 0x9e, + SewersBigKey = 0x9f, + SewersKey = 0xa0, + HyruleKey = 0xa1, + EasternKey = 0xa2, + DesertKey = 0xa3, + AgaKey = 0xa4, + SwampKey = 0xa5, + PodKey = 0xa6, + MireKey = 0xa7, + SkullKey = 0xa8, + IceKey = 0xa9, + HeraKey = 0xaa, + ThievesKey = 0xab, + TurtleKey = 0xac, + GTKey = 0xad, + Reserved0xAE = 0xae, + Reserved0xAF = 0xaf, + Agahnim1 = 0x01ff, + Agahnim1Boss = 0x02ff, + Agahnim2 = 0x03ff, + Agahnim2Boss = 0x04ff, + BigBomb = 0x05ff, + Crystal1 = 0x06ff, + Crystal2 = 0x07ff, + Crystal3 = 0x08ff, + Crystal4 = 0x09ff, + Crystal5 = 0x0aff, + Crystal6 = 0x0bff, + Crystal7 = 0x0cff, + DesertBoss = 0x0dff, + EasternBoss = 0x0eff, + Smith = 0x0fff, + Ganon = 0x10ff, + GTArmosBoss = 0x11ff, + GTLanmolasBoss = 0x12ff, + GTMoldormBoss = 0x13ff, + HeraBoss = 0x14ff, + IceBoss = 0x15ff, + IceBlock = 0x16ff, + L1Shield = 0x17ff, + L1Sword = 0x18ff, + L2Shield = 0x19ff, + L2Sword = 0x1aff, + L3Shield = 0x1bff, + L3Sword = 0x1cff, + L4Sword = 0x1dff, + MireBoss = 0x1eff, + MiseryMireToken = 0x1fff, + MireSwitch = 0x20ff, + PodBoss = 0x21ff, + PurpleChest = 0x22ff, + SkullBoss = 0x23ff, + SwampBoss = 0x24ff, + ThievesBoss = 0x25ff, + TurtleBoss = 0x26ff, + TurtleRockToken = 0x27ff, + ProgressiveMagic = 0x28ff, +} + +lazy_static! { + pub static ref ITEMS: HashMap = { + use ItemId::*; + + let mut items: HashMap = HashMap::new(); + + items.insert(FightersSwordAndShield, Item::normal(FightersSwordAndShield, "Fighters Sword and Shield")); + items.insert(MasterSword1, Item::normal(MasterSword1, "Master Sword")); + items.insert(TemperedSword, Item::normal(TemperedSword, "Tempered Sword")); + items.insert(GoldenSword, Item::normal(GoldenSword, "Golden Sword")); + items.insert(FightersShield, Item::normal(FightersShield, "Fighters Shield")); + items.insert(FireShield, Item::normal(FireShield, "Fire Shield")); + items.insert(FireRod, Item::normal(FireRod, "Fire Rod")); + items.insert(IceRod, Item::normal(IceRod, "Ice Rod")); + items.insert(Hammer, Item::normal(Hammer, "Hammer")); + items.insert(Hookshot, Item::normal(Hookshot, "Hookshot")); + items.insert(Bow, Item::normal(Bow, "Bow")); + items.insert(Boomerang, Item::normal(Boomerang, "Boomerang")); + items.insert(Powder, Item::normal(Powder, "Magic Powder")); + items.insert(Bee, Item::normal(Bee, "Bee")); + items.insert(Bombos, Item::normal(Bombos, "Bombos")); + items.insert(Ether, Item::normal(Ether, "Ether")); + items.insert(Quake, Item::normal(Quake, "Quake")); + items.insert(Lamp, Item::normal(Lamp, "Lamp")); + items.insert(Shovel, Item::normal(Shovel, "Shovel")); + items.insert(Flute, Item::normal(Flute, "Flute")); + items.insert(Somaria, Item::normal(Somaria, "Cane of Somaria")); + items.insert(Bottle, Item::bottle(Bottle, "Bottle")); + items.insert(PieceOfHeart, Item::normal(PieceOfHeart, "Piece of Heart")); + items.insert(Byrna, Item::normal(Byrna, "Cane of Byrna")); + items.insert(Cape, Item::normal(Cape, "Magic Cape")); + items.insert(Mirror, Item::normal(Mirror, "Magic Mirror")); + items.insert(L1Gloves, Item::progressive(L1Gloves, "Power Gloves")); + items.insert(L2Gloves, Item::progressive(L2Gloves, "Titan Mitts")); + items.insert(Book, Item::normal(Book, "Book of Mudora")); + items.insert(Flippers, Item::normal(Flippers, "Zora's Flippers")); + items.insert(MoonPearl, Item::normal(MoonPearl, "Moon Pearl")); + items.insert(Crystal, Item::special(Crystal, "Crystal")); + items.insert(BugCatchingNet, Item::normal(BugCatchingNet, "Bug Catching Net")); + items.insert(BlueMail, Item::normal(BlueMail, "Blue Mail")); + items.insert(RedMail, Item::normal(RedMail, "Red Mail")); + items.insert(Key, Item::consumable(Key, "Key")); + items.insert(Compass, Item::special(Compass, "Compass")); + items.insert(HeartContainerNoAnimation, Item::normal(HeartContainerNoAnimation, "Heart Container (no animation)")); + items.insert(Bomb, Item::normal(Bomb, "Bomb")); + items.insert(ThreeBombs, Item::normal(ThreeBombs, "3 Bombs")); + items.insert(Mushroom, Item::consumable(Mushroom, "Mushroom")); + items.insert(MagicalBoomerang, Item::normal(MagicalBoomerang, "Magical Boomerang")); + items.insert(BottleRedPotion, Item::bottle(BottleRedPotion, "Bottle (Red Potion)")); + items.insert(BottleGreenPotion, Item::bottle(BottleGreenPotion, "Bottle (Green Potion)")); + items.insert(BottleBluePotion, Item::bottle(BottleBluePotion, "Bottle (Blue Potion)")); + items.insert(RedPotion, Item::special(RedPotion, "Red Potion")); + items.insert(GreenPotion, Item::special(GreenPotion, "Green Potion")); + items.insert(BluePotion, Item::special(BluePotion, "Blue Potion")); + items.insert(TenBombs, Item::normal(TenBombs, "10 Bombs")); + items.insert(BigKey, Item::special(BigKey, "Big Key")); + items.insert(DungeonMap, Item::special(DungeonMap, "Dungeon Map")); + items.insert(GreenRupee, Item::normal(GreenRupee, "Green Rupee")); + items.insert(BlueRupee, Item::normal(BlueRupee, "Blue Rupee")); + items.insert(RedRupee, Item::normal(RedRupee, "Red Rupee")); + items.insert(PendantGreen, Item::special(PendantGreen, "Pendant of Courage")); + items.insert(PendantRed, Item::special(PendantRed, "Pendant of Wisdom")); + items.insert(PendantBlue, Item::special(PendantBlue, "Pendant of Power")); + items.insert(BowAndArrows, Item::special(BowAndArrows, "Bow and Arrows")); + items.insert(BowAndSilverArrows, Item::special(BowAndSilverArrows, "Bow and Silver Arrows")); + items.insert(BottleBee, Item::bottle(BottleBee, "Bottle (Bee)")); + items.insert(BottleFairy, Item::bottle(BottleFairy, "Bottle, (Fairy)")); + items.insert(HeartContainer, Item::normal(HeartContainer, "Heart Container")); + items.insert(HeartContainerRefill, Item::normal(HeartContainerRefill, "Heart Container (refill)")); + items.insert(OneHundredRupees, Item::normal(OneHundredRupees, "100 Rupees")); + items.insert(FiftyRupees, Item::normal(FiftyRupees, "50 Rupees")); + items.insert(Heart, Item::normal(Heart, "Heart")); + items.insert(SingleArrow, Item::normal(SingleArrow, "Single Arrow")); + items.insert(TenArrows, Item::normal(TenArrows, "10 Arrows")); + items.insert(SmallMagic, Item::normal(SmallMagic, "Small Magic Refill")); + items.insert(ThreeHundredRupees, Item::normal(ThreeHundredRupees, "300 Rupees")); + items.insert(RedRupee2, Item::normal(RedRupee2, "Red Rupee")); + items.insert(BottleGoldenBee, Item::bottle(BottleGoldenBee, "Bottle (Golden Bee)")); + items.insert(FightersSword, Item::normal(FightersSword, "Fighter's Sword")); + items.insert(FluteActive, Item::normal(FluteActive, "Flute (activated)")); + items.insert(Boots, Item::normal(Boots, "Pegasus Boots")); + items.insert(MaxBombs, Item::normal(MaxBombs, "Max Bomb Upgrade (50)")); + items.insert(MaxArrows, Item::normal(MaxArrows, "Max Arrow Upgrade (70)")); + items.insert(HalfMagic, Item::normal(HalfMagic, "Half Magic")); + items.insert(QuarterMagic, Item::normal(QuarterMagic, "Quarter Magic")); + items.insert(MasterSword2, Item::normal(MasterSword2, "Master Sword")); + items.insert(BombUpgrade5, Item::normal(BombUpgrade5, "Bomb Upgrade (5)")); + items.insert(BombUpgrade10, Item::normal(BombUpgrade10, "Bomb Upgrade (10)")); + items.insert(ArrowUpgrade5, Item::normal(ArrowUpgrade5, "Arrow Upgrade (5)")); + items.insert(ArrowUpgrade10, Item::normal(ArrowUpgrade10, "Arrow Upgrade (10)")); + items.insert(Programmable1, Item::normal(Programmable1, "Programmable 1")); + items.insert(Programmable2, Item::normal(Programmable2, "Programmable 2")); + items.insert(Programmable3, Item::normal(Programmable3, "Programmable 3")); + items.insert(SilverArrows, Item::normal(SilverArrows, "Silver Arrows")); + items.insert(Rupoor, Item::normal(Rupoor, "Rupoor")); + items.insert(Nothing, Item::normal(Nothing, "Nothing (Null Item)")); + items.insert(RedClock, Item::consumable(RedClock, "Red Clock")); + items.insert(BlueClock, Item::consumable(BlueClock, "Blue Clock")); + items.insert(GreenClock, Item::consumable(GreenClock, "Green Clock")); + items.insert(ProgressiveSword, Item::progressive(ProgressiveSword, "Progressive Sword")); + items.insert(ProgressiveShield, Item::progressive(ProgressiveShield, "Progressive Shield")); + items.insert(ProgressiveArmor, Item::progressive(ProgressiveArmor, "Progressive Armor")); + items.insert(ProgressiveGloves, Item::progressive(ProgressiveGloves, "Progressive Gloves")); + items.insert(UniqueRngItem, Item::special(UniqueRngItem, "Unique RNG Item (RNG Pool Single)")); + items.insert(NonUniqueRngItem, Item::special(NonUniqueRngItem, "Non-unique RNG Item (RNG Pool Multi)")); + items.insert(ProgressiveBow, Item::normal(ProgressiveBow, "Progressive Bow")); // *NOT* ProgressiveItem??? + items.insert(ProgressiveBow2, Item::normal(ProgressiveBow2, "Progressive Bow 2")); + items.insert(Triforce, Item::special(Triforce, "Triforce (Single Goal Item)")); + items.insert(PowerStar, Item::special(PowerStar, "Power Star (Multi Goal Item)")); + items.insert(TriforcePiece, Item::special(TriforcePiece, "Triforce Piece")); + items.insert(LightWorldMap, Item::special(LightWorldMap, "Light World Map")); + items.insert(DarkWorldMap, Item::special(DarkWorldMap, "Dark World Map")); + items.insert(GTMap, Item::special(GTMap, "Ganon's Tower Map")); + items.insert(TurtleMap, Item::special(TurtleMap, "Turtle Rock Map")); + items.insert(ThievesMap, Item::special(ThievesMap, "Thieves' Town Map")); + items.insert(HeraMap, Item::special(HeraMap, "Tower of Hera Map")); + items.insert(IceMap, Item::special(IceMap, "Ice Palace Map")); + items.insert(SkullMap, Item::special(SkullMap, "Skull Woods Map")); + items.insert(MireMap, Item::special(MireMap, "Misery Mire Map")); + items.insert(PodMap, Item::special(PodMap, "Palace of Darkness Map")); + items.insert(SwampMap, Item::special(SwampMap, "Swamp Palace Map")); + items.insert(AgaMap, Item::special(AgaMap, "Agahnim's Tower Map")); + items.insert(DesertMap, Item::special(DesertMap, "Desert Palace Map")); + items.insert(EasternMap, Item::special(EasternMap, "Eastern Palace Map")); + items.insert(HyruleMap, Item::special(HyruleMap, "Hyrule Castle Map")); + items.insert(SewersMap, Item::special(SewersMap, "Sewers Map")); // not the same as HyruleMap? + items.insert(GTCompass, Item::special(GTCompass, "Ganon's Tower Compass")); + items.insert(TurtleCompass, Item::special(TurtleCompass, "Turtle Rock Compass")); + items.insert(ThievesCompass, Item::special(ThievesCompass, "Thieves' Town Compass")); + items.insert(HeraCompass, Item::special(HeraCompass, "Tower of Hera Compass")); + items.insert(IceCompass, Item::special(IceCompass, "Ice Palace Compass")); + items.insert(SkullCompass, Item::special(SkullCompass, "Skull Woods Compass")); + items.insert(MireCompass, Item::special(MireCompass, "Misery Mire Compass")); + items.insert(PodCompass, Item::special(PodCompass, "Palace of Darkness Compass")); + items.insert(SwampCompass, Item::special(SwampCompass, "Swamp Palace Compass")); + items.insert(AgaCompass, Item::special(AgaCompass, "Agahnim's Tower Compass")); + items.insert(DesertCompass, Item::special(DesertCompass, "Desert Palace Compass")); + items.insert(EasternCompass, Item::special(EasternCompass, "Eastern Palace Compass")); + items.insert(HyruleCompass, Item::special(HyruleCompass, "Hyrule Castle Compass")); + items.insert(SewersCompass, Item::special(SewersCompass, "Sewers Compass")); // not the same as HyruleCompass? + items.insert(SkeletonKey, Item::special(SkeletonKey, "Skeleton Key")); + items.insert(Reserved0x91, Item::special(Reserved0x91, "")); + items.insert(GTBigKey, Item::special(GTBigKey, "Ganon's Tower Big Key")); + items.insert(TurtleBigKey, Item::special(TurtleBigKey, "Turtle Rock Big Key")); + items.insert(ThievesBigKey, Item::special(ThievesBigKey, "Thieves' Town Big Key")); + items.insert(HeraBigKey, Item::special(HeraBigKey, "Tower of Hera Big Key")); + items.insert(IceBigKey, Item::special(IceBigKey, "Ice Palace Big Key")); + items.insert(SkullBigKey, Item::special(SkullBigKey, "Skull Woods Big Key")); + items.insert(MireBigKey, Item::special(MireBigKey, "Misery Mire Big Key")); + items.insert(PodBigKey, Item::special(PodBigKey, "Palace of Darkness Big Key")); + items.insert(SwampBigKey, Item::special(SwampBigKey, "Swamp Palace Big Key")); + items.insert(AgaBigKey, Item::special(AgaBigKey, "Agahnim's Tower Big Key")); + items.insert(DesertBigKey, Item::special(DesertBigKey, "Desert Palace Big Key")); + items.insert(EasternBigKey, Item::special(EasternBigKey, "Eastern Palace Big Key")); + items.insert(HyruleBigKey, Item::special(HyruleBigKey, "Hyrule Castle Big Key")); + items.insert(SewersBigKey, Item::special(SewersBigKey, "Sewers Big Key")); // not the same as HyruleBigKey? + items.insert(SewersKey, Item::consumable(SewersKey, "Sewers Key")); + items.insert(HyruleKey, Item::consumable(HyruleKey, "Hyrule Castle Key")); // not the same as SewersKey? + items.insert(EasternKey, Item::consumable(EasternKey, "Eastern Palace Key")); + items.insert(DesertKey, Item::consumable(DesertKey, "Desert Palace Key")); + items.insert(AgaKey, Item::consumable(AgaKey, "Agahnim's Tower Key")); + items.insert(SwampKey, Item::consumable(SwampKey, "Swamp Palace Key")); + items.insert(PodKey, Item::consumable(PodKey, "Palace of Darkness Key")); + items.insert(MireKey, Item::consumable(MireKey, "Misery Mire Key")); + items.insert(SkullKey, Item::consumable(SkullKey, "Skull Woods Key")); + items.insert(IceKey, Item::consumable(IceKey, "Ice Palace Key")); + items.insert(HeraKey, Item::consumable(HeraKey, "Tower of Hera Key")); + items.insert(ThievesKey, Item::consumable(ThievesKey, "Thieves' Town Key")); + items.insert(TurtleKey, Item::consumable(TurtleKey, "Turtle Rock Key")); + items.insert(GTKey, Item::consumable(GTKey, "Ganon's Tower Key")); + items.insert(Reserved0xAE, Item::special(Reserved0xAE, "")); + items.insert(Reserved0xAF, Item::special(Reserved0xAF, "")); + items.insert(Agahnim1, Item::special(Agahnim1, "Agahnim 1 Defeated")); + items.insert(Agahnim1Boss, Item::special(Agahnim1Boss, "Agahnim Boss")); + items.insert(Agahnim2, Item::special(Agahnim2, "Agahnim 2 Defeated")); + items.insert(Agahnim2Boss, Item::special(Agahnim2Boss, "Agahnim 2 Boss")); + items.insert(BigBomb, Item::special(BigBomb, "Big Bomb")); + items.insert(Crystal1, Item::special(Crystal1, "Crystal 1")); + items.insert(Crystal2, Item::special(Crystal2, "Crystal 2")); + items.insert(Crystal3, Item::special(Crystal3, "Crystal 3")); + items.insert(Crystal4, Item::special(Crystal4, "Crystal 4")); + items.insert(Crystal5, Item::special(Crystal5, "Crystal 5")); + items.insert(Crystal6, Item::special(Crystal6, "Crystal 6")); + items.insert(Crystal7, Item::special(Crystal7, "Crystal 7")); + items.insert(DesertBoss, Item::special(DesertBoss, "Desert Boss")); + items.insert(EasternBoss, Item::special(EasternBoss, "Eastern Boss")); + items.insert(Smith, Item::special(Smith, "Frog Smith")); + items.insert(Ganon, Item::special(Ganon, "Ganon")); + items.insert(GTArmosBoss, Item::special(GTArmosBoss, "GT Armos Boss")); + items.insert(GTLanmolasBoss, Item::special(GTLanmolasBoss, "GT Lanmolas Boss")); + items.insert(GTMoldormBoss, Item::special(GTMoldormBoss, "GT Moldorm Boss")); + items.insert(HeraBoss, Item::special(HeraBoss, "Hera Boss")); + items.insert(IceBoss, Item::special(IceBoss, "Ice Boss")); + items.insert(IceBlock, Item::special(IceBlock, "Ice Palace Block")); + items.insert(L1Shield, Item::progressive(L1Shield, "L1 Shield")); + items.insert(L1Sword, Item::progressive(L1Sword, "L1 Sword")); + items.insert(L2Shield, Item::progressive(L2Shield, "L2 Shield")); + items.insert(L2Sword, Item::progressive(L2Sword, "L2 Sword")); + items.insert(L3Shield, Item::progressive(L3Shield, "L3 Shield")); + items.insert(L3Sword, Item::progressive(L3Sword, "L3 Sword")); + items.insert(L4Sword, Item::progressive(L4Sword, "L4 Sword")); + items.insert(MireBoss, Item::special(MireBoss, "Mire Boss")); + items.insert(MiseryMireToken, Item::special(MiseryMireToken, "Misery Mire Entrance Token")); + items.insert(MireSwitch, Item::special(MireSwitch, "Misery Mire Switch")); + items.insert(PodBoss, Item::special(PodBoss, "PoD Boss")); + items.insert(PurpleChest, Item::special(PurpleChest, "Purple Chest")); + items.insert(SkullBoss, Item::special(SkullBoss, "Skull Boss")); + items.insert(ThievesBoss, Item::special(ThievesBoss, "Thieves Boss")); + items.insert(TurtleBoss, Item::special(TurtleBoss, "Turtle Boss")); + items.insert(ProgressiveMagic, Item::special(ProgressiveMagic, "Progressive Magic")); + + items + }; +} diff --git a/enemize/src/graph/mod.rs b/enemize/src/graph/mod.rs new file mode 100644 index 0000000..eb2d62b --- /dev/null +++ b/enemize/src/graph/mod.rs @@ -0,0 +1,3 @@ +pub mod dungeons; +pub mod item; +pub mod requirement; diff --git a/enemize/src/graph/requirement.rs b/enemize/src/graph/requirement.rs new file mode 100644 index 0000000..2bbece0 --- /dev/null +++ b/enemize/src/graph/requirement.rs @@ -0,0 +1,16 @@ +use std::collections::HashSet; + +use super::item::Item; + +pub struct Requirement(HashSet); + +impl Requirement { + pub fn new(items: &[Item]) -> Requirement { + Requirement(HashSet::from_iter(items.into_iter().map(|i| *i))) + } + + pub fn is_met(&self, items: &[Item]) -> bool { + let items = HashSet::from_iter(items.into_iter().map(|i| *i)); + self.0.is_subset(&items) + } +} diff --git a/enemize/src/lib.rs b/enemize/src/lib.rs index a499767..d130be2 100644 --- a/enemize/src/lib.rs +++ b/enemize/src/lib.rs @@ -10,6 +10,7 @@ use crate::rom::RomData; pub mod asar; pub mod bosses; pub mod constants; +pub mod graph; pub mod option_flags; pub mod randomize; pub mod rom;