diff --git a/hollow-knight-save-parser/src/lib.rs b/hollow-knight-save-parser/src/lib.rs index e609b5f..4e15cce 100644 --- a/hollow-knight-save-parser/src/lib.rs +++ b/hollow-knight-save-parser/src/lib.rs @@ -94,6 +94,9 @@ impl Parser { let data: GameDeser = serde_json::from_slice(&v).map_err(|e| error(&format!("JSON parse error: {e}")))?; + let to_map = + |entries: &[(&str, bool)]| entries.iter().map(|&(k, v)| (k.to_owned(), v)).collect(); + if let GameDeser::HollowKnight(data) = data { let pd = &data.player_data; @@ -110,10 +113,6 @@ impl Parser { let grub_freed = |name| scene_activated(name, "Grub Bottle"); let whispering_root = |name| scene_activated(name, "Dream Plant"); - let to_map = |entries: &[(&str, bool)]| { - entries.iter().map(|&(k, v)| (k.to_owned(), v)).collect() - }; - let bosses = to_map(&[ ("[Broken Vessel]", pd.killed_infected_knight), ("[Brooding Mawlek]", pd.killed_mawlek), @@ -510,17 +509,20 @@ impl Parser { ]); let items = to_map(&[ - ("[SIMPLE_KEY] [Simple Key] from [Sly]", pd.sly_simple_key), ( - "[SIMPLE_KEY] [Simple Key] near [City Storerooms]", + "[SIMPLE_KEY_(HOLLOW_KNIGHT)] [Simple Key] from [Sly]", + pd.sly_simple_key, + ), + ( + "[SIMPLE_KEY_(HOLLOW_KNIGHT)] [Simple Key] near [City Storerooms]", scene_activated("Ruins1_17", "Shiny Item"), ), ( - "[SIMPLE_KEY] [Simple Key] in the [Ancient Basin]", + "[SIMPLE_KEY_(HOLLOW_KNIGHT)] [Simple Key] in the [Ancient Basin]", scene_activated("Abyss_20", "Shiny Item Stand"), ), ( - "[SIMPLE_KEY] [Simple Key] behind [Pale Lurker]", + "[SIMPLE_KEY_(HOLLOW_KNIGHT)] [Simple Key] behind [Pale Lurker]", pd.got_lurker_key, ), ("[ELEGANT_KEY] [Elegant Key]", pd.has_white_key), @@ -817,7 +819,417 @@ impl Parser { whispering_roots, }); } else if let GameDeser::Silksong(data) = data { - unimplemented!("{data:#?}"); + let pd = &data.player_data; + + let scene_activated = |name, id| { + data.scene_data + .persistent_bools + .serialized_list + .iter() + .find(|x| x.scene_name == name && x.id == id) + .is_some_and(|x| x.value) + }; + + let has_tool = |name| { + pd.tools + .saved_data + .iter() + .find(|x| x.name == name) + .is_some_and(|x| x.data.is_unlocked) + }; + + let quest_completed = |name| { + pd.quest_completion_data + .saved_data + .iter() + .find(|x| x.name == name) + .is_some_and(|x| x.data.is_completed) + }; + + let mask_shard_collected = |name| scene_activated(name, "Heart Piece"); + let silk_spool_collected = |name| scene_activated(name, "Silk Spool"); + let silk_heart_collected = |name| scene_activated(name, "glow_rim_Remasker"); + + let bosses = to_map(&[ + ("[Moss Mother]", false), + ("[Bell Beast]", false), + ("[Fourth Chorus]", false), + ("[Savage Beastfly]", false), + ("[Widow]", false), + ("[Last Judge] / [Phantom]", false), + ("[Savage Beastfly 2](Savage Beastfly#Far_Fields)", false), + ("[Cogwork Dancers]", false), + ("[Trobbio]", false), + ("[The Unravelled]", false), + ("[Lace 2](Lace#The_Cradle)", false), + ]); + + let silk_hearts = to_map(&[ + ( + "[Bell Beast]", + silk_heart_collected("Memory_Silk_Heart_BellBeast"), + ), + ( + "[Lace 2](Lace#The_Cradle)", + silk_heart_collected("Memory_Silk_Heart_LaceTower"), + ), + ( + "[The Unravelled]", + silk_heart_collected("Memory_Silk_Heart_WardBoss"), + ), + ]); + + let silk_skills = to_map(&[ + ("[Silkspear]", pd.has_needle_throw), + ("[Thread Storm]", pd.has_thread_sphere), + ("[Cross Stitch]", pd.has_parry), + ("[Sharpdart]", pd.has_silk_charge), + ("[Rune Rage]", pd.has_silk_bomb), + ("[Pale Nails]", pd.has_silk_boss_needle), + ]); + + let tools = to_map(&[ + ("[Straight Pin]", has_tool("Straight Pin")), + ("[Threefold Pin]", has_tool("Tri Pin")), + ("[Sting Shard]", has_tool("Sting Shard")), + ("[Tacks]", has_tool("Tack")), + ("[Longpin]", has_tool("Harpoon")), + ( + "[Curveclaw] / [Curvesickle]", + has_tool("Curve Claws") | has_tool("Curve Claws Upgraded"), + ), + ("[Throwing Ring]", has_tool("Shakra Ring")), + ("[Pimpillo]", has_tool("Pimpilo")), + ("[Conchcutter]", has_tool("Conch Drill")), + ( + "[Silkshot]", + has_tool("WebShot Forge") + | has_tool("WebShot Weaver") + | has_tool("WebShot Architect"), + ), + ("[Delver's Drill]", has_tool("Screw Attack")), + ("[Cogwork Wheel]", has_tool("Cogwork Saw")), + ("[Cogfly]", has_tool("Cogwork Flier")), + ("[Rosary Cannon]", has_tool("Rosary Cannon")), + ("[Voltvessels]", has_tool("Lightning Rod")), + ("[Flintslate]", has_tool("Flintstone")), + ("[Flea Brew]", has_tool("Flea Brew")), + ("[Plasmium Phial]", has_tool("Lifeblood Syringe")), + ( + "[Druid's Eye] / [Druid's Eyes]", + has_tool("Mosscreep Tool 1") | has_tool("Mosscreep Tool 2"), + ), + ("[Magma Bell]", has_tool("Lava Charm")), + ("[Warding Bell]", has_tool("Bell Bind")), + ("[Pollip Pouch]", has_tool("Poison Pouch")), + ("[Fractured Mask]", has_tool("Fractured Mask")), + ("[Multibinder]", has_tool("Multibind")), + ("[Weavelight]", has_tool("White Ring")), + ("[Sawtooth Circlet]", has_tool("Brolly Spike")), + ("[Injector Band]", has_tool("Quickbind")), + ("[Spool Extender]", has_tool("Spool Extender")), + ("[Reserve Bind]", has_tool("Reserve Bind")), + ( + "[Claw Mirror] / [Claw Mirrors]", + has_tool("Dazzle Bind") | has_tool("Dazzle Bind Upgraded"), + ), + ("[Memory Crystal]", has_tool("Revenge Crystal")), + ("[Snitch Pick]", has_tool("Thief Claw")), + ("[Volt Filament]", has_tool("Zap Imbuement")), + ("[Quick Sling]", has_tool("Quick Sling")), + ("[Wreath of Purity]", has_tool("Maggot Charm")), + ("[Longclaw]", has_tool("Longneedle")), + ("[Wispfire Lantern]", has_tool("Wisp Lantern")), + ("[Egg of Flealia]", has_tool("Flea Charm")), + ("[Pin Badge]", has_tool("Pinstress Tool")), + ("[Compass]", has_tool("Compass")), + ("[Shard Pendant]", has_tool("Bone Necklace")), + ("[Magnetite Brooch]", has_tool("Rosary Magnet")), + ("[Weighted Belt]", has_tool("Weighted Anklet")), + ("[Barbed Bracelet]", has_tool("Barbed Wire")), + ( + "[Dead Bug's Purse] / [Shell Satchel]", + has_tool("Dead Mans Purse") | has_tool("Shell Satchel"), + ), + ("[Magnetite Dice]", has_tool("Magnetite Dice")), + ("[Scuttlebrace]", has_tool("Scuttlebrace")), + ("[Ascendant's Grip]", has_tool("Wallcling")), + ("[Spider Strings]", has_tool("Musician Charm")), + ("[Silkspeed Anklets]", has_tool("Sprintmaster")), + ("[Thief's Mark]", has_tool("Thief Charm")), + ]); + + let ancestral_arts = to_map(&[ + ("[Swift Step]", pd.has_dash), + ("[Cling Grip]", pd.has_walljump), + ("[Needolin]", pd.has_needolin), + ("[Clawline]", pd.has_harpoon_dash), + ("[Silk Soar]", pd.has_super_jump), + ("[Needle Strike]", pd.has_charge_slash), + ("[Sylphsong]", pd.has_bound_crest_upgrader), + ]); + + let crests = to_map(&[ + ("[Reaper Crest]", pd.completed_memory_reaper), + ("[Wanderer Crest]", pd.completed_memory_wanderer), + ("[Beast Crest]", pd.completed_memory_beast), + // completedMemory_witch does NOT give you %, only the crest, + // which is Cursed, and you get Witch crest and % from the quest + ("[Witch Crest]", quest_completed("Doctor Curse Cure")), + ("[Architect Crest]", pd.completed_memory_toolmaster), + ("[Shaman Crest]", pd.completed_memory_shaman), + ]); + + let eva = to_map(&[ + ("Evolved [Hunter Crest]", false), + ("[Vesticrest] yellow slot", false), + ("[Vesticrest] blue slot", false), + ("Further evolved [Hunter Crest]", false), + ]); + + let mask_shards = to_map(&[ + ( + "[Pebb] from [Bone Bottom] for [ROSARY] 300", + pd.purchased_bonebottom_heart_piece, + ), + ("[Wormways]", mask_shard_collected("Crawl_02")), + ("[Deep Docks] entrance", mask_shard_collected("Dock_08")), + ( + "[Far Fields] [Seamstress]", + mask_shard_collected("Bone_East_20"), + ), + ("[Shellwood]", mask_shard_collected("Shellwood_14")), + ("[Weavenest Alta]", mask_shard_collected("Weave_05b")), + ( + "[Jubilana] from [Songclave] for [ROSARY] 750", + pd.merchant_enclave_shell_fragment, + ), + ("West [Cogwork Core]", mask_shard_collected("Song_09")), + ("[Whispering Vaults]", mask_shard_collected("Library_05")), + ("[Savage Beastfly] [Wish]", quest_completed("Beastfly Hunt")), + ( + "[Far Fields] rising lava escape sequence", + scene_activated("Bone_East_LavaChallenge", "Heart Piece (1)"), + ), + ("West [Mount Fay]", mask_shard_collected("Peak_04c")), + ("[Slab]", mask_shard_collected("Slab_17")), + ("[Bilewater]", mask_shard_collected("Shadow_13")), + ("[Wisp Thicket]", mask_shard_collected("Wisp_07")), + ("[Blasted Steps]", mask_shard_collected("Coral_19b")), + ("[Mount Fay] [Brightvein]", mask_shard_collected("Peak_06")), + ( + "[Fastest in Pharloom] [Wish]", + quest_completed("Sprintmaster Race"), + ), + ( + "[Dark Hearts] [Wish]", + quest_completed("Destroy Thread Cores"), + ), + ("[The Hidden Hunter] [Wish]", quest_completed("Ant Trapper")), + ]); + + let needle = to_map(&[ + ( + "[Sharpened Needle](Needle#Upgrades)", + pd.nail_upgrades > 0.0, + ), + ("[Shining Needle](Needle#Upgrades)", pd.nail_upgrades > 1.0), + ( + "[Hivesteel Needle](Needle#Upgrades)", + pd.nail_upgrades > 2.0, + ), + ( + "[Palesteel Needle](Needle#Upgrades)", + pd.nail_upgrades > 3.0, + ), + ]); + + let spool_fragments = to_map(&[ + ("[Bone Bottom]", silk_spool_collected("Bone_11b")), + ( + "[Deep Docks] hot floor", + silk_spool_collected("Bone_East_13"), + ), + ("[Weavenest Alta]", silk_spool_collected("Weave_11")), + ("[Greymoor]", silk_spool_collected("Greymoor_02")), + ("[Slab]", silk_spool_collected("Peak_01")), + ( + "[Frey] from [Bellhart] for [ROSARY] 270", + pd.purchased_belltown_spool_segment, + ), + ("[Grand Gate]", silk_spool_collected("Song_19_entrance")), + ("[Underworks]", silk_spool_collected("Under_10")), + ( + "From [Mooshka] at [Grand Gate]", + pd.caravan_troupe_location > 1.0, + ), + ("[Whiteward]", silk_spool_collected("Ward_01")), + ("[Cogwork Core]", silk_spool_collected("Cog_07")), + ( + "[Underworks] near [The Cauldron]", + silk_spool_collected("Library_11b"), + ), + ( + "[Balm for the Wounded] [Wish]", + quest_completed("Save Sherma"), + ), + ( + "[Jubilana] from [Songclave] for [ROSARY] 500", + pd.merchant_enclave_spool_piece, + ), + ( + "[Deep Docks] behind [Simple Key]", + silk_spool_collected("Dock_03c"), + ), + ("[High Halls]", silk_spool_collected("Hang_03_top")), + ("[Memorium]", silk_spool_collected("Arborium_09")), + ( + "[Grindle] for [ROSARY] 680", + pd.purchased_grindle_spool_piece, + ), + ]); + + let tool_pouch = to_map(&[ + ( + "[Mort] from [Pilgrim's Rest] for [ROSARY] 220", + pd.purchased_pilgrims_rest_tool_pouch, + ), + ("[Loddie]'s pin challenge", pd.pin_galleries_completed > 0.0), + ("[Nuu]'s wish", quest_completed("Journal")), + ( + "From [Mooshka] in [Fleatopia]", + pd.caravan_troupe_location > 2.0, + ), + ( + "[Forge Daughter] for [ROSARY] 180", + pd.purchased_forge_tool_kit, + ), + ( + "[Crawbug Clearing] [Wish]", + quest_completed("Crow Feathers"), + ), + ( + "[Twelfth Architect] for [ROSARY] 450", + pd.purchased_architect_tool_kit, + ), + ("[Grindle] for [ROSARY] 700", pd.purchased_grindle_tool_kit), + ]); + + let items = to_map(&[ + ("[Drifter's Cloak]", pd.has_brolly), + ("[Faydown Cloak]", pd.has_double_jump), + ("[White Key]", false), + ("[Key of Apostate]", false), + ("[Sacred Cylinder]", false), + ( + "[MEMORY_LOCKET] [Memory Locket] for [Volatile Flintbeetles] [Wish]", + false, + ), + ("[MEMORY_LOCKET] [Memory Locket] in [The Marrow]", false), + ("[MEMORY_LOCKET] [Memory Locket] in [Hunter's March]", false), + ( + "[MEMORY_LOCKET] [Memory Locket] in [Deep Docks] behind [Simple Key]", + false, + ), + ( + "[MEMORY_LOCKET] [Memory Locket] from [Mort] in [Far Fields] for [ROSARY] 150", + false, + ), + ( + "[MEMORY_LOCKET] [Memory Locket] in [Far Fields] near [Skarrsinger Karmelita]", + false, + ), + ( + "[MEMORY_LOCKET] [Memory Locket] in [Greymoor] near [Bellway]", + false, + ), + ( + "[MEMORY_LOCKET] [Memory Locket] in [Greymoor] inside [Halfway Home]", + false, + ), + ( + "[MEMORY_LOCKET] [Memory Locket] from [Frey] in [Bellhart] for [ROSARY] 330", + false, + ), + ( + "[MEMORY_LOCKET] [Memory Locket] in [Bellhart]'s ceiling", + false, + ), + ("[MEMORY_LOCKET] [Memory Locket] in [Blasted Steps]", false), + ( + "[MEMORY_LOCKET] [Memory Locket] in the [Sands of Karak]", + false, + ), + ("[MEMORY_LOCKET] [Memory Locket] in [Wormways]", false), + ("[MEMORY_LOCKET] [Memory Locket] in the [Underworks]", false), + ("[MEMORY_LOCKET] [Memory Locket] at [Grand Bellway]", false), + ("[MEMORY_LOCKET] [Memory Locket] in [Memorium]", false), + ("[MEMORY_LOCKET] [Memory Locket] in [The Slab]", false), + ( + "[MEMORY_LOCKET] [Memory Locket] in [Whispering Vaults]", + false, + ), + ( + "[MEMORY_LOCKET] [Memory Locket] in [Bilewater] secret room", + false, + ), + ( + "[MEMORY_LOCKET] [Memory Locket] in [Bilewater] near the bench shortcut", + false, + ), + ]); + + #[expect(clippy::float_cmp)] + let everbloom = to_map(&[( + "[Everbloom]", + pd.completed_red_memory + && pd + .collectables + .saved_data + .iter() + .find(|x| x.name == "White Flower") + .is_some_and(|x| x.data.amount == 1.0), + )]); + + let wishes = to_map(&[ + ("[My Missing Courier]", false), + ("[Volatile Flintbeetles]", false), + ("[The Wandering Merchant]", false), + ("[Savage Beastfly](Wishes#Grand_Hunt_Wishes)", false), + ("[Fine Pins]", false), + ("[Balm for the Wounded]", false), + ("[Building Up Songclave]", false), + ("[Cloaks of the Choir]", false), + ("[Strengthening Songclave]", false), + ("[The Lost Merchant]", false), + ("[Infestation Operation]", false), + ("[Fastest in Pharloom]", false), + ("[Dark Hearts]", false), + ("[The Hidden Hunter]", false), + ]); + + let relics = to_map(&[]); + + let fleas = to_map(&[]); + + self.map = GameSer::Silksong(SilksongChecks { + bosses, + silk_hearts, + tools, + silk_skills, + ancestral_arts, + crests, + eva, + mask_shards, + needle, + spool_fragments, + tool_pouch, + items, + everbloom, + wishes, + relics, + fleas, + }); } Ok(()) @@ -841,30 +1253,18 @@ impl Parser { type Number = f64; -#[allow(clippy::large_enum_variant)] -#[derive(Deserialize, Debug)] -#[serde(untagged)] -enum GameDeser { - HollowKnight(SaveFile), - Silksong(SaveFile), -} +//////////////////////////////////////////////////////////////////////////////// +// Serialization//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// -#[allow(clippy::large_enum_variant)] -#[derive(Serialize, Debug, Clone)] +#[derive(Debug, Clone, Serialize)] #[serde(rename_all = "kebab-case")] pub enum GameSer { HollowKnight(HollowKnightChecks), Silksong(SilksongChecks), } -#[derive(Serialize, Debug, Default, Clone)] -#[serde(rename_all = "camelCase")] -pub struct SilksongChecks { - bosses: HashMap, - things: HashMap, -} - -#[derive(Serialize, Debug, Default, Clone)] +#[derive(Debug, Default, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct HollowKnightChecks { bosses: HashMap, @@ -888,17 +1288,50 @@ pub struct HollowKnightChecks { whispering_roots: HashMap, } -#[derive(Deserialize, Debug)] +#[derive(Debug, Default, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SilksongChecks { + bosses: HashMap, + silk_hearts: HashMap, + tools: HashMap, + silk_skills: HashMap, + ancestral_arts: HashMap, + crests: HashMap, + eva: HashMap, + mask_shards: HashMap, + needle: HashMap, + spool_fragments: HashMap, + tool_pouch: HashMap, + items: HashMap, + everbloom: HashMap, + wishes: HashMap, + relics: HashMap, + fleas: HashMap, +} + +//////////////////////////////////////////////////////////////////////////////// +// Deserialization ///////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#[allow(clippy::large_enum_variant)] +#[derive(Debug, Deserialize)] +#[serde(untagged)] +enum GameDeser { + HollowKnight(SaveFile), + Silksong(SaveFile), +} + +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct SaveFile { - player_data: Box, +pub struct SaveFile { + player_data: Box, scene_data: SceneData, } -#[allow(clippy::struct_excessive_bools)] -#[derive(Deserialize, Debug)] +#[expect(clippy::struct_excessive_bools)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct PlayedData { +pub struct HollowKnightPlayerData { fireball_level: Number, quake_level: Number, scream_level: Number, @@ -1091,16 +1524,128 @@ pub struct BossDoorStateTier { completed: bool, } -#[derive(Deserialize, Debug)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct SceneData { - persistent_bool_items: Vec, +pub struct HollowKnightSceneData { + persistent_bool_items: Vec, } -#[derive(Deserialize, Debug)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct SceneObjectBool { +pub struct SilksongSceneData { + persistent_bools: PersistentBools, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PersistentBools { + serialized_list: Vec, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +struct SilksongSceneObjectBool { + #[serde(rename = "ID")] + id: String, + scene_name: String, + value: bool, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct HollowKnightSceneObjectBool { id: String, scene_name: String, activated: bool, } + +#[expect(clippy::struct_excessive_bools)] +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SilksongPlayerData { + has_brolly: bool, + has_double_jump: bool, + has_needle_throw: bool, + has_thread_sphere: bool, + has_dash: bool, + has_walljump: bool, + has_needolin: bool, + has_charge_slash: bool, + has_silk_boss_needle: bool, + has_parry: bool, + has_harpoon_dash: bool, + has_silk_charge: bool, + has_silk_bomb: bool, + has_super_jump: bool, + #[serde(rename = "HasBoundCrestUpgrader")] + has_bound_crest_upgrader: bool, + nail_upgrades: Number, + #[serde(rename = "PurchasedBonebottomHeartPiece")] + purchased_bonebottom_heart_piece: bool, + #[serde(rename = "MerchantEnclaveShellFragment")] + merchant_enclave_shell_fragment: bool, + #[serde(rename = "MerchantEnclaveSpoolPiece")] + merchant_enclave_spool_piece: bool, + #[serde(rename = "PurchasedBelltownSpoolSegment")] + purchased_belltown_spool_segment: bool, + purchased_grindle_spool_piece: bool, + pin_galleries_completed: Number, + #[serde(rename = "PurchasedForgeToolKit")] + purchased_forge_tool_kit: bool, + #[serde(rename = "PurchasedPilgrimsRestToolPouch")] + purchased_pilgrims_rest_tool_pouch: bool, + purchased_grindle_tool_kit: bool, + #[serde(rename = "PurchasedArchitectToolKit")] + purchased_architect_tool_kit: bool, + #[serde(rename = "Tools")] + tools: SavedData, + #[serde(rename = "QuestCompletionData")] + quest_completion_data: SavedData, + #[serde(rename = "completedMemory_reaper")] + completed_memory_reaper: bool, + #[serde(rename = "completedMemory_beast")] + completed_memory_beast: bool, + #[serde(rename = "completedMemory_wanderer")] + completed_memory_wanderer: bool, + #[serde(rename = "completedMemory_toolmaster")] + completed_memory_toolmaster: bool, + #[serde(rename = "completedMemory_shaman")] + completed_memory_shaman: bool, + #[serde(rename = "CaravanTroupeLocation")] + caravan_troupe_location: Number, + #[serde(rename = "CompletedRedMemory")] + completed_red_memory: bool, + #[serde(rename = "Collectables")] + collectables: SavedData, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SavedData { + saved_data: Vec>, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct SavedDataData { + data: Data, + name: String, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct ToolsData { + is_unlocked: bool, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct QuestsData { + is_completed: bool, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct CollectablesData { + amount: Number, +} diff --git a/hollow-knight-save-parser/src/main.rs b/hollow-knight-save-parser/src/main.rs index d1d73c1..e8d5e38 100644 --- a/hollow-knight-save-parser/src/main.rs +++ b/hollow-knight-save-parser/src/main.rs @@ -13,8 +13,8 @@ fn main() { }; println!("Parsing save file `{path}`"); let mut parser = Parser::new(); - if parser.parse_save_file(&data).is_err() { - println!("Failed to parse save file"); + if let Err(e) = parser.parse_save_file(&data) { + println!("Failed to parse save file: {e}"); return; } let map = parser.get_map(); diff --git a/package.json b/package.json index be0e99f..b570de6 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ }, "dependencies": { "hollow-knight-save-parser": "./pkg", - "immer": "^10.1.1", - "object-deep-merge": "^1.0.4", + "immer": "^10.1.3", + "object-deep-merge": "^1.0.5", "react": "^19.1.1", "react-dom": "^19.1.1", "styled-components": "^6.1.19", @@ -24,18 +24,18 @@ "zustand": "^4.5.7" }, "devDependencies": { - "@eslint/js": "^9.34.0", - "@types/react": "^19.1.12", + "@eslint/js": "^9.35.0", + "@types/react": "^19.1.13", "@types/react-dom": "^19.1.9", "@vitejs/plugin-react": "^5.0.2", - "eslint": "^9.34.0", + "eslint": "^9.35.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", - "globals": "^16.3.0", - "type-fest": "^4.41.0", + "globals": "^16.4.0", + "type-fest": "^5.0.0", "typescript": "^5.9.2", - "typescript-eslint": "^8.41.0", - "vite": "^7.1.4", + "typescript-eslint": "^8.43.0", + "vite": "^7.1.5", "vite-plugin-checker": "^0.10.3", "wasm-pack": "^0.13.1" }, diff --git a/src/App.tsx b/src/App.tsx index 6c006f6..53bdcd8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -108,11 +108,30 @@ const Info = ({ game, sidebar }: { game: GameKey; sidebar?: boolean }) => { ['[GEO]', geo, geoReq], ['[ESSENCE]', essence, Math.max(...essenceReq)], ['[PALE_ORE]', paleOre, paleOreReq], - ['[SIMPLE_KEY]', simpleKeys, simpleKeysReq], + ['[SIMPLE_KEY_(HOLLOW_KNIGHT)]', simpleKeys, simpleKeysReq], ] as const; } else { - const { rosaries, rosariesReq } = useChecklistStore(game)(); - info = [['[ROSARIES]', rosaries, rosariesReq]] as const; + const { + rosaries, + rosariesReq, + paleOil, + paleOilReq, + simpleKeys, + simpleKeysReq, + memoryLockets, + memoryLocketsReq, + craftmetal, + craftmetalReq, + acts, + } = useChecklistStore(game)(); + info = [ + ['[ROSARY]', rosaries, rosariesReq], + ['[PALE_OIL]', paleOil, paleOilReq], + ['[SIMPLE_KEY_(SILKSONG)]', simpleKeys, simpleKeysReq], + ['[MEMORY_LOCKET]', memoryLockets, Math.max(...memoryLocketsReq)], + ['[CRAFTMETAL]', craftmetal, craftmetalReq], + ['[Act](Acts)', acts, acts], + ] as const; } return ( @@ -124,7 +143,7 @@ const Info = ({ game, sidebar }: { game: GameKey; sidebar?: boolean }) => { if (sidebar) { return ( - {it} {val} / {req} + {it} {val} {it !== '[Act](Acts)' ? `/ ${req}` : ''} ); } @@ -138,7 +157,10 @@ const Info = ({ game, sidebar }: { game: GameKey; sidebar?: boolean }) => { return ( - {it} {val} collected / {req} required + {it} {val} + {it !== '[Act](Acts)' + ? `collected / ${req} required` + : ''} {paren} ); @@ -155,7 +177,7 @@ const SectionColumns = ({ game }: { game: Game }) => { useEffect(() => { if (game === 'silksong') { setTooltipText(`NOTE: THIS SECTION IS WIP - EXPECT MORE ITEMS TO BE ADDED, ALONGSIDE WITH SAVEFILE SUPPORT`); + Some requirements are not yet implemented.`); openTooltip(); } }, [game]); diff --git a/src/assets/craftmetal.png b/src/assets/craftmetal.png new file mode 100644 index 0000000..e5e4e6a Binary files /dev/null and b/src/assets/craftmetal.png differ diff --git a/src/assets/index.ts b/src/assets/index.ts index ed668c8..ae051ba 100644 --- a/src/assets/index.ts +++ b/src/assets/index.ts @@ -2,6 +2,7 @@ export { default as ARCANE_EGG } from './arcaneegg.png'; export { default as POINTER } from './button-pointer.png'; export { default as CHARM_NOTCH } from './charmnotch.png'; export { CheckIcon } from './checkmark'; +export { default as CRAFTMETAL } from './craftmetal.png'; export { default as BOTTOM } from './dialog-fleur-bottom.png'; export { default as TOP } from './dialog-fleur-top.png'; export { default as ELEGANT_KEY } from './elegantkey.png'; @@ -15,13 +16,16 @@ export { default as HR2 } from './hr2.png'; export { default as KINGS_IDOL } from './kingsidol.png'; export { default as LOGO } from './logo.png'; export { default as LOVE_KEY } from './lovekey.png'; +export { default as MEMORY_LOCKET } from './memory-locket.png'; +export { default as PALE_OIL } from './paleoil.png'; export { default as PALE_ORE } from './paleore.png'; export { QuestionMark } from './questionmark'; -export { default as ROSARIES } from './rosaries.png'; +export { default as ROSARY } from './rosary.png'; export { default as SLY_KEY } from './shopkeeperskey.png'; export { default as SILKSONG } from './silksong.png'; -export { default as SILKSONG_BACKGROUND } from './silksongplaceholderbg.png'; -export { default as SIMPLE_KEY } from './simplekey.png'; +export { default as SILKSONG_BACKGROUND } from './silksongbg.png'; +export { default as SIMPLE_KEY_HK } from './simplekey-hk.png'; +export { default as SIMPLE_KEY_SS } from './simplekey-ss.png'; export { default as TRAM_PASS } from './trampass.png'; export { default as HOLLOW_KNIGHT_BACKGROUD } from './voidheart.png'; export { default as JOURNAL } from './wanderersjournal.png'; diff --git a/src/assets/memory-locket.png b/src/assets/memory-locket.png new file mode 100644 index 0000000..7f32669 Binary files /dev/null and b/src/assets/memory-locket.png differ diff --git a/src/assets/paleoil.png b/src/assets/paleoil.png new file mode 100644 index 0000000..c5f6780 Binary files /dev/null and b/src/assets/paleoil.png differ diff --git a/src/assets/rosaries.png b/src/assets/rosary.png similarity index 100% rename from src/assets/rosaries.png rename to src/assets/rosary.png diff --git a/src/assets/silksongbg.png b/src/assets/silksongbg.png new file mode 100644 index 0000000..200a685 Binary files /dev/null and b/src/assets/silksongbg.png differ diff --git a/src/assets/silksongplaceholderbg.png b/src/assets/silksongplaceholderbg.png deleted file mode 100644 index 5d7ece4..0000000 Binary files a/src/assets/silksongplaceholderbg.png and /dev/null differ diff --git a/src/assets/simplekey.png b/src/assets/simplekey-hk.png similarity index 100% rename from src/assets/simplekey.png rename to src/assets/simplekey-hk.png diff --git a/src/assets/simplekey-ss.png b/src/assets/simplekey-ss.png new file mode 100644 index 0000000..56518e0 Binary files /dev/null and b/src/assets/simplekey-ss.png differ diff --git a/src/components/Checkbox/SectionCheckBox.tsx b/src/components/Checkbox/SectionCheckBox.tsx index c5c5b36..32245f0 100644 --- a/src/components/Checkbox/SectionCheckBox.tsx +++ b/src/components/Checkbox/SectionCheckBox.tsx @@ -89,11 +89,7 @@ export const SectionCheckBox = ({ const { description } = check; const error = formatCheckListError( checkName, - errors - ? errors![sectionName] - ? errors![sectionName]![checkName] // wtf typescript - : undefined - : undefined + errors?.[sectionName]?.[checkName] ); let label = diff --git a/src/constants.ts b/src/constants.ts index 8c03a11..1fe4aa7 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -26,13 +26,13 @@ const HOLLOW_KNIGHT_SECTION_TITLES: Record< > = { bosses: '[Bosses](Bosses (Hollow Knight))', optionalBosses: '[Bosses](Bosses (Hollow Knight)) (no percents)', - equipment: '[Equipment](Abilities)', + equipment: '[Equipment](Abilities (Hollow Knight))', spells: '[Spells]', nail: '[Nail]', dreamNail: '[Dream Nail]', nailArts: '[Nail Arts]', charms: '[Charms]', - maskShards: '[Mask Shards]', + maskShards: '[Mask Shards](Mask Shard (Hollow Knight))', vesselFragments: '[Vessel Fragments]', colosseum: '[Colosseum of Fools]', dreamers: '[Dreamers]', @@ -41,14 +41,30 @@ const HOLLOW_KNIGHT_SECTION_TITLES: Record< '[Dream Bosses](Bosses (Hollow Knight)#Boss_Variants) (no percents)', godhome: '[Godhome]', grubs: '[Grubs](Grub#Rewards_and_locations) (no percents)', - items: '[Items](Items (Hollow_Knight)) (no percents)', - relics: '[Relics](Items (Hollow_Knight)#Tradables) (no percents)', + items: '[Items](Items (Hollow Knight)) (no percents)', + relics: '[Relics](Items (Hollow Knight)#Tradables) (no percents)', whisperingRoots: '[Whispering Roots](Whispering Root) (no percents)', // endings: '[Endings](Endings (Hollow Knight))', }; const SILKSONG_SECTION_TITLES: Record, string> = { - bosses: '[Bosses](Bosses (Silksong))', + bosses: '[Bosses](Bosses (Silksong)) (no percents)', + melodies: '[Threefold Melody](The Cradle#How_to_Access) (no percents)', + silkHearts: '[Silk Hearts]', + tools: '[Tools]', + silkSkills: '[Silk Skills](Crests#List_of_Skills)', + ancestralArts: '[Ancestral Arts]', + crests: '[Crests]', + eva: '[Eva]', + maskShards: '[Mask Shards](Mask Shard (Silksong))', + needle: '[Needle]', + spoolFragments: '[Spool Fragments]', + toolPouch: '[Tool Pouch] and [Crafting Kit]', + items: '[Items](Items (Silksong)) (no percents)', + everbloom: '[Everbloom]', + wishes: '[Wishes] (no percents)', + fleas: '[Fleas] (no percents)', + relics: '[Relics](Items (Silksong)#Tradables) (no percents)', }; export const SECTION_TITLES = { @@ -84,8 +100,20 @@ export const HOLLOW_KNIGHT_DISTRIBUTED_SECTIONS = [ ] as const satisfies SectionNames<'hollow-knight'>[][]; export const SILKSONG_DISTRIBUTED_SECTIONS = [ - ['bosses'], - [], + ['silkHearts', 'ancestralArts', 'silkSkills', 'tools', 'items', 'relics'], + [ + 'needle', + 'crests', + 'eva', + 'maskShards', + 'spoolFragments', + 'toolPouch', + 'everbloom', + 'bosses', + 'melodies', + 'fleas', + 'wishes', + ], ] as const satisfies SectionNames<'silksong'>[][]; type MissingSectionNames = UnionToArray< diff --git a/src/stores/INITIAL_CHECKLIST_STATE.ts b/src/stores/INITIAL_CHECKLIST_STATE.ts index d42f4bb..d845942 100644 --- a/src/stores/INITIAL_CHECKLIST_STATE.ts +++ b/src/stores/INITIAL_CHECKLIST_STATE.ts @@ -1,2105 +1,5 @@ -import { - HollowKnightChecklistState, - SilksongChecklistState, -} from '../types/checklist'; - -const checked = { checked: true } as const; - -const INITIAL_HOLLOW_KNIGHT_CHECKLIST_STATE: HollowKnightChecklistState = { - game: 'hollow-knight', - percent: 0, - - geo: 0, - essence: 0, - paleOre: 0, - simpleKeys: 0, - - charms: 0, - grubs: 0, - maskShards: 0, - vesselFragments: 0, - - geoReq: 0, - essenceReq: [0], - paleOreReq: 0, - simpleKeysReq: 0, - - checks: { - bosses: { - '[Broken Vessel]': { - reward: { percent: 1 }, - requires: { - checks: { equipment: { '[Crystal Heart]': checked } }, - }, - }, - '[Brooding Mawlek]': { reward: { percent: 1 } }, - '[The Collector]': { - reward: { percent: 1 }, - requires: { - checks: { - equipment: { '[Mantis Claw]': checked }, - items: { '[LOVE_KEY] [Love Key]': checked }, - }, - }, - }, - '[Dung Defender]': { - reward: { percent: 1, simpleKeysReq: 1 }, - requires: { - simpleKeys: 1, - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[False Knight]': { reward: { percent: 1, geo: 200 } }, - '[Grimm]': { - reward: { percent: 1 }, - requires: { - checks: { - charms: { '[Grimmchild] / [Carefree Melody]': checked }, - }, - }, - }, - '[Gruz Mother]': { reward: { percent: 1, geo: 50 } }, - '[Hive Knight]': { - reward: { percent: 1 }, - requires: { - checks: { items: { '[TRAM_PASS] [Tram Pass]': checked } }, - }, - }, - '[Hornet Protector]': { - reward: { percent: 1 }, - requires: { - checks: { spells: { '[Vengeful Spirit]': checked } }, - }, - }, - '[Hornet Sentinel]': { - reward: { percent: 1 }, - requires: { - checks: { equipment: { '[Monarch Wings]': checked } }, - }, - }, - '[Mantis Lords]': { - reward: { percent: 1, geo: 620 }, - requires: { - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[Nosk]': { - reward: { percent: 1 }, - requires: { - checks: { equipment: { '[Crystal Heart]': checked } }, - }, - }, - '[Soul Master]': { - reward: { percent: 1, geo: 380 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[Traitor Lord]': { - reward: { percent: 1 }, - requires: { - checks: { equipment: { '[Shade Cloak]': checked } }, - }, - }, - '[Uumuu]': { - reward: { percent: 1 }, - requires: { - checks: { equipment: { "[Isma's Tear]": checked } }, - }, - }, - '[Watcher Knight]': { - reward: { percent: 1, geo: 655 }, - requires: { - checks: { equipment: { '[Monarch Wings]': checked } }, - }, - }, - }, - - optionalBosses: { - '[Absolute Radiance]': { - reward: {}, - requires: { - checks: { - godhome: { - '[Pantheon of the Hallownest] (no percent)': - checked, - }, - }, - }, - }, - '[Brothers Oro & Mato]': { - reward: {}, - requires: { - checks: { - godhome: { '[Pantheon of the Master]': checked }, - }, - }, - }, - '[Crystal Guardian]': { - reward: { geo: 385 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[Enraged Guardian]': { - reward: { geo: 550 }, - requires: { - checks: { - optionalBosses: { '[Crystal Guardian]': checked }, - equipment: { '[Monarch Wings]': checked }, - }, - }, - }, - '[Flukemarm]': { - reward: {}, - requires: { - checks: { spells: { '[Desolate Dive]': checked } }, - }, - }, - '[God Tamer]': { - reward: {}, - requires: { - checks: { colosseum: { '[Trial of the Fool]': checked } }, - }, - }, - '[Great Nailsage Sly]': { - reward: {}, - requires: { - checks: { godhome: { '[Pantheon of the Sage]': checked } }, - }, - }, - '[Hollow Knight]': { - reward: {}, - requires: { - checks: { - dreamers: { - '[Herra the Beast]': checked, - '[Lurien the Watcher]': checked, - '[Monomon the Teacher]': checked, - }, - }, - }, - }, - '[Massive Moss Charger]': { - reward: { geo: 300 }, - requires: { - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[Oblobbles]': { - reward: {}, - requires: { - checks: { - colosseum: { '[Trial of the Conqueror]': checked }, - }, - }, - }, - '[Paintmaster Sheo]': { - reward: {}, - requires: { - checks: { - godhome: { '[Pantheon of the Artist]': checked }, - }, - }, - }, - '[Pure Vessel]': { - reward: {}, - requires: { - checks: { - godhome: { '[Pantheon of the Knight]': checked }, - }, - }, - }, - '[Radiance]': { - reward: {}, - requires: { - checks: { - charms: { '[Kingsoul] / [Void Heart]': checked }, - optionalBosses: { '[Hollow Knight]': checked }, - dreamNail: { '[Dream Nail]': checked }, - }, - }, - }, - '[Sisters of Battle]': { - reward: {}, - requires: { - checks: { - godhome: { - '[Pantheon of the Hallownest] (no percent)': - checked, - }, - }, - }, - }, - '[Soul Warrior]': { reward: { geo: 200 } }, - '[Vengefly King]': { - reward: { geo: 65 }, - requires: { - checks: { spells: { '[Vengeful Spirit]': checked } }, - }, - }, - '[Winged Nosk]': { - reward: {}, - requires: { - checks: { - godhome: { - '[Pantheon of the Hallownest] (no percent)': - checked, - }, - }, - }, - }, - '[Zote]': { - reward: {}, - requires: { - checks: { - colosseum: { '[Trial of the Warrior]': checked }, - optionalBosses: { '[Vengefly King]': checked }, - }, - }, - }, - }, - - equipment: { - '[Crystal Heart]': { - reward: { percent: 2 }, - requires: { - checks: { - equipment: { - '[Mantis Claw]': checked, - '[Mothwing Cloak]': checked, - }, - }, - }, - }, - "[Isma's Tear]": { - reward: { percent: 2 }, - requires: { - checks: { - bosses: { '[Dung Defender]': checked }, - equipment: { '[Crystal Heart]': checked }, - }, - }, - }, - '[Mantis Claw]': { - reward: { percent: 2 }, - requires: { - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[Monarch Wings]': { - reward: { percent: 2 }, - requires: { - checks: { - bosses: { '[Broken Vessel]': checked }, - equipment: { - '[Crystal Heart]': checked, - '[Mantis Claw]': checked, - }, - }, - }, - }, - '[Mothwing Cloak]': { - reward: { percent: 2 }, - requires: { - checks: { bosses: { '[Hornet Protector]': checked } }, - }, - }, - '[Shade Cloak]': { - reward: { percent: 2 }, - requires: { - checks: { equipment: { "[King's Brand]": checked } }, - }, - }, - "[King's Brand]": { - reward: { percent: 2 }, - requires: { - checks: { - bosses: { '[Hornet Sentinel]': checked }, - equipment: { '[Monarch Wings]': checked }, - }, - }, - }, - }, - - nail: { - '[Sharpened Nail](Nail#Upgrades)': { - reward: { percent: 1, geoReq: 250 }, - requires: { geo: 250 }, - }, - '[Channelled Nail](Nail#Upgrades)': { - reward: { percent: 1, geoReq: 800, paleOreReq: 1 }, - requires: { - geo: 800, - paleOre: 1, - checks: { - nail: { '[Sharpened Nail](Nail#Upgrades)': checked }, - }, - }, - }, - '[Coiled Nail](Nail#Upgrades)': { - reward: { percent: 1, geoReq: 2000, paleOreReq: 2 }, - requires: { - geo: 2000, - paleOre: 2, - checks: { - nail: { '[Channelled Nail](Nail#Upgrades)': checked }, - }, - }, - }, - '[Pure Nail](Nail#Upgrades)': { - reward: { percent: 1, geoReq: 4000, paleOreReq: 3 }, - requires: { - geo: 4000, - paleOre: 3, - checks: { - nail: { '[Coiled Nail](Nail#Upgrades)': checked }, - }, - }, - }, - }, - - dreamNail: { - '[Dream Nail]': { - reward: { percent: 1 }, - requires: { - checks: { items: { '[Lumafly Lantern]': checked } }, - }, - }, - '[Awoken Dream Nail]': { - reward: { percent: 1, essenceReq: [1800] }, - requires: { - essence: 1800, - checks: { - dreamNail: { '[Dream Nail]': checked }, - maskShards: { '[Seer]': checked }, - }, - }, - }, - '[Ascension](Seer)': { - reward: { percent: 1, essenceReq: [2400] }, - requires: { - essence: 2400, - checks: { dreamNail: { '[Awoken Dream Nail]': checked } }, - }, - }, - }, - - nailArts: { - '[Cyclone Slash]': { - reward: { percent: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[Dash Slash]': { - reward: { percent: 1, geoReq: 800 }, - requires: { geo: 800 }, - }, - '[Great Slash]': { - reward: { percent: 1 }, - requires: { - checks: { equipment: { '[Crystal Heart]': checked } }, - }, - }, - }, - - spells: { - '[Desolate Dive]': { - reward: { percent: 1 }, - requires: { checks: { bosses: { '[Soul Master]': checked } } }, - }, - '[Descending Dark]': { - reward: { percent: 1 }, - requires: { - checks: { - items: { '[Lumafly Lantern]': checked }, - spells: { '[Desolate Dive]': checked }, - }, - }, - }, - '[Howling Wraiths]': { - reward: { percent: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[Abyss Shriek]': { - reward: { percent: 1 }, - requires: { - checks: { - equipment: { "[King's Brand]": checked }, - spells: { '[Howling Wraiths]': checked }, - }, - }, - }, - '[Vengeful Spirit]': { - reward: { percent: 1 }, - requires: { checks: { bosses: { '[False Knight]': checked } } }, - }, - '[Shade Soul]': { - reward: { percent: 1 }, - requires: { - checks: { - items: { '[ELEGANT_KEY] [Elegant Key]': checked }, - }, - }, - }, - }, - - charms: { - '[Wayward Compass]': { - description: - 'Bought from [Iselda] in [Dirtmouth] for [GEO] 220 after the first encounter with [Cornifer].', - reward: { percent: 1, charms: 1, geoReq: 220 }, - requires: { geo: 220 }, - }, - '[Gathering Swarm]': { - description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 300.', - reward: { percent: 1, charms: 1, geoReq: 300 }, - requires: { geo: 300 }, - }, - '[Stalwart Shell]': { - description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 200.', - reward: { percent: 1, charms: 1, geoReq: 200 }, - requires: { geo: 200 }, - }, - '[Soul Catcher]': { - description: - 'Found at the very end of the [Ancestral Mound], after killing the [Elder Baldur].', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { spells: { '[Vengeful Spirit]': checked } }, - }, - }, - '[Shaman Stone]': { - description: - 'Bought from [Salubra] for [GEO] 220 in the [Forgotten Crossroads].', - reward: { percent: 1, charms: 1, geoReq: 220 }, - requires: { - geo: 220, - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[Soul Eater]': { - description: - 'Found to the east side of the [Crypts](Resting Grounds#Crypts).', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { spells: { '[Desolate Dive]': checked } }, - }, - }, - '[Dashmaster]': { - description: - 'Found beneath a statue below the [Mantis Village] in the [Fungal Wastes], near the entrance to the [Royal Waterways].', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[Sprintmaster]': { - description: - "Bought from [Sly] in [Dirtmouth] after acquiring the [SHOPKEEPER'S_KEY] [Shopkeeper's Key] for [GEO] 400.", - reward: { percent: 1, charms: 1, geoReq: 400 }, - requires: { - geo: 400, - checks: { - items: { - "[SHOPKEEPER'S_KEY] [Shopkeeper's Key]": checked, - }, - }, - }, - }, - '[Grubsong]': { - description: - 'Gifted by [Grubfather] after 10 [Grubs] have been freed.', - reward: { percent: 1, charms: 1 }, - requires: { grubs: 10 }, - }, - "[Grubberfly's Elegy]": { - description: - 'Gifted by [Grubfather] after freeing all 46 [Grubs].', - reward: { percent: 1, charms: 1 }, - requires: { grubs: 46 }, - }, - '[Fragile Heart] / [Unbreakable Heart]': { - description: - 'Bought from [Leg Eater] in [Fungal Wastes] for [GEO] 350.', - reward: { percent: 1, charms: 1, geoReq: 350 }, - requires: { geo: 350 }, - }, - '[Fragile Greed] / [Unbreakable Greed]': { - description: - 'Bought from [Leg Eater] in [Fungal Wastes] for [GEO] 250.', - reward: { percent: 1, charms: 1, geoReq: 250 }, - requires: { geo: 250 }, - }, - '[Fragile Strength] / [Unbreakable Strength]': { - description: - 'Bought from [Leg Eater] in [Fungal Wastes] for [GEO] 600.', - reward: { percent: 1, charms: 1, geoReq: 600 }, - requires: { geo: 600 }, - }, - '[Spell Twister]': { - description: - 'A secret room on the top of the [Soul Sanctum], just before fighting [Soul Master].', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[Steady Body]': { - description: - 'Bought from [Salubra] for [GEO] 120 in the [Forgotten Crossroads].', - reward: { percent: 1, charms: 1, geoReq: 120 }, - requires: { - geo: 120, - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[Heavy Blow]': { - description: - "Bought from [Sly] in [Dirtmouth] after acquiring the [SHOPKEEPER'S_KEY] [Shopkeeper's Key] for [GEO] 350.", - reward: { percent: 1, charms: 1, geoReq: 350 }, - requires: { - geo: 350, - checks: { - items: { - "[SHOPKEEPER'S_KEY] [Shopkeeper's Key]": checked, - }, - }, - }, - }, - '[Quick Slash]': { - description: - 'Located in [Kingdom\'s Edge], on a massive anvil in a hidden room in front of a massive corpse known as an "[Ancient Nailsmith]".', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { spells: { '[Desolate Dive]': checked } }, - }, - }, - '[Longnail]': { - description: - 'Bought from [Salubra] in the [Forgotten Crossroads] for [GEO] 300.', - reward: { percent: 1, charms: 1, geoReq: 300 }, - requires: { - geo: 300, - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[Mark of Pride]': { - description: - 'In the [Mantis Village], in a chest in a room northeast of the [Mantis Lords] arena.', - reward: { percent: 1, charms: 1 }, - requires: { checks: { bosses: { '[Mantis Lords]': checked } } }, - }, - '[Fury of the Fallen]': { - description: - "Found in [King's Pass], the starting cavern, behind a spike-filled cavern.", - reward: { percent: 1, charms: 1 }, - }, - '[Thorns of Agony]': { - description: - 'Found in [Greenpath] in a maze of thorns featuring [Charged Lumaflies].', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[Baldur Shell]': { - description: - 'Found in the southwest portion of the [Howling Cliffs], where there is a chest that drops only [GEO] 1.', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[Flukenest]': { - description: - 'Dropped by [Flukemarm] in the [Royal Waterways] when defeated.', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { - spells: { '[Desolate Dive]': checked }, - optionalBosses: { '[Flukemarm]': checked }, - }, - }, - }, - "[Defender's Crest]": { - description: - 'Reward from defeating [Dung Defender] in the [Royal Waterways].', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { bosses: { '[Dung Defender]': checked } }, - }, - }, - '[Glowing Womb]': { - description: - 'Found in the [Aspid Nest](Forgotten Crossroads#Aspid Nest) after completing [Aspid] arena.', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { equipment: { '[Crystal Heart]': checked } }, - }, - }, - '[Quick Focus]': { - description: - 'Bought from [Salubra] in the [Forgotten Crossroads] for [GEO] 800.', - reward: { percent: 1, charms: 1, geoReq: 800 }, - requires: { - geo: 800, - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[Deep Focus]': { - description: - 'Found in [Crystal Peak] in a hidden cave made entirely of crystals.', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { equipment: { '[Crystal Heart]': checked } }, - }, - }, - '[Lifeblood Heart]': { - description: - 'Bought from [Salubra] in the [Forgotten Crossroads] for [GEO] 250.', - reward: { percent: 1, charms: 1, geoReq: 250 }, - requires: { - geo: 250, - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[Lifeblood Core]': { - description: - 'Behind the door in the [Abyss] that opens when you have 15 or more [Lifeblood Masks](Knight#Health).', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { - equipment: { "[King's Brand]": checked }, - charms: { - "[Joni's Blessing]": checked, - '[Fragile Heart] / [Unbreakable Heart]': checked, - }, - }, - maskShards: 8, - }, - }, - "[Joni's Blessing]": { - description: - "Found in [Joni's Repose] in the [Howling Cliffs].", - reward: { percent: 1, charms: 1 }, - requires: { - checks: { - equipment: { '[Mantis Claw]': checked }, - items: { '[Lumafly Lantern]': checked }, - }, - }, - }, - '[Hiveblood]': { - description: - 'Located in [the Hive], below the room where the [Hive Knight] is fought.', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { - bosses: { '[Hive Knight]': checked }, - items: { '[TRAM_PASS] [Tram Pass]': checked }, - }, - }, - }, - '[Spore Shroom]': { - description: - "Found in the [Fungal Wastes] near a pool of acid, close to the entrances to the Queen's Gardens and Deepnest.", - reward: { percent: 1, charms: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[Sharp Shadow]': { - description: - 'Located in [Deepnest], southeast of the Hot Spring behind a [Shade Gate].', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { equipment: { '[Shade Cloak]': checked } }, - }, - }, - '[Shape of Unn]': { - description: 'Acquired from [Unn] beneath the [Lake of Unn].', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { equipment: { "[Isma's Tear]": checked } }, - }, - }, - "[Nailmaster's Glory]": { - description: - 'Given by [Sly] after receiving all 3 [Nail Arts] from the [Nailmasters].', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { - nailArts: { - '[Cyclone Slash]': checked, - '[Dash Slash]': checked, - '[Great Slash]': checked, - }, - }, - }, - }, - '[Weaversong]': { - description: "Found in the upper part of [Weavers' Den].", - reward: { percent: 1, charms: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[Dream Wielder]': { - description: - 'Given by the [Seer] after gathering [ESSENCE] 500.', - reward: { percent: 1, charms: 1, essenceReq: [500] }, - requires: { - essence: 500, - checks: { - items: { - '[PALE_ORE] [Pale Ore] awarded by the [Seer]': - checked, - }, - }, - }, - }, - '[Dreamshield]': { - description: - "Found in a room in the [Resting Grounds], below [Seer]'s room.", - reward: { percent: 1, charms: 1 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Grimmchild] / [Carefree Melody]': { - description: - '[Grimmchild] is given by [Troupe Master Grimm] in Dirtmouth after [the Grimm Troupe] has been summoned. ' + - 'After banishing the Grimm Troupe, the [Carefree Melody] charm can be acquired from [Nymm] by listening to him in [Dirtmouth].', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Kingsoul] / [Void Heart]': { - description: - 'Obtained after getting both [White Fragments]. ' + - 'After obtaining the [Kingsoul], [Void Heart] can be found in [the Birthplace] at the bottom of [the Abyss].', - reward: { percent: 1, charms: 1 }, - requires: { - checks: { - bosses: { '[Traitor Lord]': checked }, - equipment: { '[Monarch Wings]': checked }, - dreamNail: { '[Awoken Dream Nail]': checked }, - }, - }, - }, - }, - - maskShards: { - '[Sly] #1': { - description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 150.', - reward: { maskShards: 1, geoReq: 150 }, - requires: { geo: 150 }, - }, - '[Sly] #2': { - description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 500.', - reward: { maskShards: 1, geoReq: 500 }, - requires: { - geo: 500, - checks: { maskShards: { '[Sly] #1': checked } }, - }, - }, - '[Sly] #3': { - description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 800.', - reward: { maskShards: 1, geoReq: 800 }, - requires: { - geo: 800, - checks: { - maskShards: { '[Sly] #2': checked }, - items: { - "[SHOPKEEPER'S_KEY] [Shopkeeper's Key]": checked, - }, - }, - }, - }, - '[Sly] #4': { - description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 1500.', - reward: { maskShards: 1, geoReq: 1500 }, - requires: { - geo: 1500, - checks: { - maskShards: { '[Sly] #3': checked }, - items: { - "[SHOPKEEPER'S_KEY] [Shopkeeper's Key]": checked, - }, - }, - }, - }, - '[Forgotten Crossroads] [Brooding Mawlek]': { - description: 'Reward for defeating [Brooding Mawlek].', - reward: { maskShards: 1 }, - requires: { - checks: { bosses: { '[Brooding Mawlek]': checked } }, - }, - }, - '[Grubfather]': { - description: 'Requires rescuing 5 [Grubs].', - reward: { maskShards: 1 }, - requires: { grubs: 5 }, - }, - '[Forgotten Crossroads] [Goams]': { - description: - 'Behind a gauntlet of [Goams] in [Forgotten Crossroads].', - reward: { maskShards: 1 }, - requires: { - checks: { equipment: { '[Monarch Wings]': checked } }, - }, - }, - "[Queen's Station]": { - description: "Near east side of the [Queen's Station].", - reward: { maskShards: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - "[Bretta]'s house": { - description: 'Requires rescuing [Bretta] from [Fungal Wastes].', - reward: { maskShards: 1 }, - requires: { - checks: { - equipment: { - '[Mothwing Cloak]': checked, - '[Mantis Claw]': checked, - }, - }, - }, - }, - '[Stone Sanctuary]': { - reward: { maskShards: 1 }, - requires: { - checks: { items: { '[Lumafly Lantern]': checked } }, - }, - }, - '[Royal Waterways]': { - description: - 'Northwest section of the Royal Waterways, swim west under main path.', - reward: { maskShards: 1 }, - requires: { simpleKeys: 1 }, - }, - '[Deepnest] from [Fungal Core]': { - reward: { maskShards: 1 }, - requires: { - checks: { equipment: { '[Monarch Wings]': checked } }, - }, - }, - '[Enraged Guardian]': { - reward: { maskShards: 1 }, - requires: { - checks: { - equipment: { '[Monarch Wings]': checked }, - optionalBosses: { '[Enraged Guardian]': checked }, - }, - }, - }, - '[The Hive]': { - description: - 'Requires baiting a [Hive Guardian] into breaking a wall.', - reward: { maskShards: 1 }, - requires: { - checks: { items: { '[TRAM_PASS] [Tram Pass]': checked } }, - }, - }, - '[Seer]': { - description: 'For collecting [ESSENCE] 1500.', - reward: { maskShards: 1, essenceReq: [1500] }, - requires: { - essence: 1500, - checks: { - relics: { - '[ARCANE_EGG] [Arcane Egg] awarded by the [Seer]': - checked, - }, - }, - }, - }, - '[Grey Mourner]': { - description: 'Requires completing the [Delicate Flower quest].', - reward: { maskShards: 1 }, - requires: { - checks: { - items: { '[Delicate Flower]': checked }, - equipment: { '[Mothwing Cloak]': checked }, - }, - }, - }, - }, - - vesselFragments: { - '[Sly] #1': { - description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 550.', - reward: { vesselFragments: 1, geoReq: 550 }, - requires: { geo: 550 }, - }, - '[Sly] #2': { - description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 900.', - reward: { vesselFragments: 1, geoReq: 900 }, - requires: { - geo: 900, - checks: { - vesselFragments: { '[Sly] #1': checked }, - items: { - "[SHOPKEEPER'S_KEY] [Shopkeeper's Key]": checked, - }, - }, - }, - }, - '[Greenpath]': { - description: - "Near the inaccessible [Queen's Gardens] entrance.", - reward: { vesselFragments: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - 'Left of the lift in [Forgotten Crossroads]': { - description: - 'Accessible after unlocking the lift in the [City of Tears].', - reward: { vesselFragments: 1 }, - }, - "Above [King's Station] near a lift": { - description: 'Accessible after completing the arena.', - reward: { vesselFragments: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[Deepnest]': { - description: - 'At the end of the [Garpede] parkour section above the working [Tram].', - reward: { vesselFragments: 1 }, - }, - '[Stag Nest]': { reward: { vesselFragments: 1 } }, - '[Seer]': { - description: 'For collecting [ESSENCE] 700.', - reward: { vesselFragments: 1, essenceReq: [700] }, - requires: { - essence: 700, - checks: { charms: { '[Dream Wielder]': checked } }, - }, - }, - '[Ancient Basin] fountain': { - description: 'For dropping [GEO] 3000 into the fountain.', - reward: { vesselFragments: 1, geoReq: 3000 }, - requires: { geo: 3000 }, - }, - }, - - dreamers: { - '[Herra the Beast]': { - reward: { percent: 1 }, - requires: { - checks: { - dreamNail: { '[Dream Nail]': checked }, - equipment: { '[Mantis Claw]': checked }, - items: { '[Lumafly Lantern]': checked }, - }, - }, - }, - '[Lurien the Watcher]': { - reward: { percent: 1 }, - requires: { - checks: { - bosses: { '[Watcher Knight]': checked }, - dreamNail: { '[Dream Nail]': checked }, - }, - }, - }, - '[Monomon the Teacher]': { - reward: { percent: 1 }, - requires: { - checks: { - bosses: { '[Uumuu]': checked }, - dreamNail: { '[Dream Nail]': checked }, - }, - }, - }, - }, - - dreamWarriors: { - '[Elder Hu]': { - reward: { percent: 1, essence: 100 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Galien]': { - reward: { percent: 1, essence: 200 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Gorb]': { - reward: { percent: 1, essence: 100 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Markoth]': { - reward: { percent: 1, essence: 250 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Marmu]': { - reward: { percent: 1, essence: 150 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[No Eyes]': { - reward: { percent: 1, essence: 200 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Xero]': { - reward: { percent: 1, essence: 100 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Nightmare King Grimm] / [Banishment](Grimm Troupe (Quest))': { - reward: { percent: 1 }, - requires: { - checks: { - dreamNail: { '[Dream Nail]': checked }, - charms: { '[Grimmchild] / [Carefree Melody]': checked }, - }, - }, - }, - }, - - dreamBosses: { - '[Failed Champion]': { - reward: { essence: 300 }, - requires: { - checks: { - dreamNail: { '[Dream Nail]': checked }, - bosses: { '[False Knight]': checked }, - }, - }, - }, - '[Grey Prince Zote]': { - reward: { essence: 300 }, - requires: { - checks: { - dreamNail: { '[Dream Nail]': checked }, - optionalBosses: { '[Zote]': checked }, - equipment: { '[Monarch Wings]': checked }, - }, - }, - }, - '[Lost Kin]': { - reward: { essence: 400 }, - requires: { - checks: { - dreamNail: { '[Dream Nail]': checked }, - bosses: { '[Broken Vessel]': checked }, - }, - }, - }, - '[White Defender]': { - reward: { essence: 300 }, - requires: { - checks: { - dreamNail: { '[Dream Nail]': checked }, - bosses: { '[Dung Defender]': checked }, - }, - }, - }, - '[Soul Tyrant]': { - reward: { essence: 300 }, - requires: { - checks: { - dreamNail: { '[Dream Nail]': checked }, - bosses: { '[Soul Master]': checked }, - }, - }, - }, - }, - - colosseum: { - '[Trial of the Warrior]': { - reward: { percent: 1, geoReq: 100, geo: 1000 }, - requires: { geo: 100 }, - }, - '[Trial of the Conqueror]': { - reward: { percent: 1, geoReq: 450, geo: 2000 }, - requires: { - geo: 450, - checks: { - colosseum: { '[Trial of the Warrior]': checked }, - optionalBosses: { '[Oblobbles]': checked }, - }, - }, - }, - '[Trial of the Fool]': { - reward: { percent: 1, geoReq: 800, geo: 3000 }, - requires: { - geo: 800, - checks: { - colosseum: { '[Trial of the Conqueror]': checked }, - optionalBosses: { '[God Tamer]': checked }, - }, - }, - }, - }, - - godhome: { - '[Godtuner]': { - reward: { percent: 1, simpleKeysReq: 1 }, - requires: { simpleKeys: 1 }, - }, - '[Pantheon of the Master]': { - reward: { percent: 1 }, - requires: { - checks: { - dreamNail: { '[Dream Nail]': checked }, - bosses: { - '[Gruz Mother]': checked, - '[False Knight]': checked, - '[Hornet Protector]': checked, - '[Dung Defender]': checked, - '[Brooding Mawlek]': checked, - }, - dreamWarriors: { '[Gorb]': checked }, - optionalBosses: { - '[Vengefly King]': checked, - '[Massive Moss Charger]': checked, - '[Soul Warrior]': checked, - '[Brothers Oro & Mato]': checked, - }, - }, - }, - }, - '[Pantheon of the Artist]': { - reward: { percent: 1 }, - requires: { - checks: { - dreamNail: { '[Dream Nail]': checked }, - bosses: { - '[Soul Master]': checked, - '[Mantis Lords]': checked, - '[Nosk]': checked, - '[Broken Vessel]': checked, - }, - colosseum: { '[Trial of the Conqueror]': checked }, - dreamWarriors: { - '[Xero]': checked, - '[Marmu]': checked, - }, - optionalBosses: { - '[Crystal Guardian]': checked, - '[Oblobbles]': checked, - '[Flukemarm]': checked, - '[Paintmaster Sheo]': checked, - }, - }, - }, - }, - '[Pantheon of the Sage]': { - reward: { percent: 1 }, - requires: { - checks: { - dreamNail: { '[Dream Nail]': checked }, - bosses: { - '[Hive Knight]': checked, - '[The Collector]': checked, - '[Grimm]': checked, - '[Uumuu]': checked, - '[Hornet Sentinel]': checked, - }, - dreamWarriors: { - '[Elder Hu]': checked, - '[Galien]': checked, - }, - dreamBosses: { - /* TODO: make optional? // '[Grey Prince Zote]': checked, */ - }, - optionalBosses: { '[Great Nailsage Sly]': checked }, - }, - }, - }, - '[Pantheon of the Knight]': { - reward: { percent: 1 }, - requires: { - checks: { - godhome: { - '[Pantheon of the Master]': checked, - '[Pantheon of the Artist]': checked, - '[Pantheon of the Sage]': checked, - }, - bosses: { - '[Traitor Lord]': checked, - '[Watcher Knight]': checked, - }, - dreamWarriors: { - '[No Eyes]': checked, - '[Markoth]': checked, - }, - optionalBosses: { - '[Enraged Guardian]': checked, - '[Pure Vessel]': checked, - }, - }, - }, - }, - '[Pantheon of the Hallownest] (no percent)': { - reward: {}, - requires: { - checks: { - godhome: { '[Pantheon of the Knight]': checked }, - charms: { '[Kingsoul] / [Void Heart]': checked }, - optionalBosses: { - '[Massive Moss Charger]': checked, - '[Soul Warrior]': checked, - '[Brothers Oro & Mato]': checked, - '[Crystal Guardian]': checked, - '[Oblobbles]': checked, - '[Sisters of Battle]': checked, - '[Flukemarm]': checked, - '[Paintmaster Sheo]': checked, - '[Winged Nosk]': checked, - '[Great Nailsage Sly]': checked, - '[Enraged Guardian]': checked, - '[Pure Vessel]': checked, - }, - bosses: { - '[Gruz Mother]': checked, - '[Hornet Protector]': checked, - '[Dung Defender]': checked, - '[Brooding Mawlek]': checked, - '[Soul Master]': checked, - '[Broken Vessel]': checked, - '[Hive Knight]': checked, - '[The Collector]': checked, - '[Grimm]': checked, - '[Watcher Knight]': checked, - '[Uumuu]': checked, - '[Hornet Sentinel]': checked, - '[Traitor Lord]': checked, - }, - dreamWarriors: { - '[Gorb]': checked, - '[Xero]': checked, - '[Marmu]': checked, - '[Galien]': checked, - '[Elder Hu]': checked, - '[No Eyes]': checked, - '[Markoth]': checked, - }, - dreamBosses: { - /* TODO: make optional? // '[Grey Prince Zote]': checked, */ - }, - }, - }, - }, - }, - - grubs: { - '[Forgotten Crossroads] behind [Husk Guard]': { - reward: { grubs: 1 }, - }, - '[Forgotten Crossroads] [Fog Canyon] entrance': { - reward: { grubs: 1 }, - }, - '[Forgotten Crossroads] breakable wall': { reward: { grubs: 1 } }, - '[Forgotten Crossroads] [Pogo](Nail#Nail-bouncing)': { - reward: { grubs: 1 }, - }, - '[Forgotten Crossroads] on a ledge': { - reward: { grubs: 1 }, - requires: { - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[Greenpath] with a moss block shortcut': { reward: { grubs: 1 } }, - '[Greenpath] near acid': { reward: { grubs: 1 } }, - '[Greenpath] behind [Moss Knight]': { reward: { grubs: 1 } }, - '[Greenpath] in the middle of a [Durandoo] room': { - reward: { grubs: 1 }, - }, - '[Fungal Wastes] behind a line of [Fungling]s': { - reward: { grubs: 1 }, - }, - '[Fungal Wastes] near [Spore Shroom]': { - reward: { grubs: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[City of Tears] on a ledge': { reward: { grubs: 1 } }, - '[City of Tears] behind [Great Husk Sentry]': { - reward: { grubs: 1 }, - }, - '[City of Tears] in the [Desolate Dive] dive': { - reward: { grubs: 1 }, - requires: { - checks: { spells: { '[Desolate Dive]': checked } }, - }, - }, - '[City of Tears] under the entrance to the [Tower of Love]': { - reward: { grubs: 1 }, - }, - '[City of Tears] room leading to [Watcher Knight]': { - reward: { grubs: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[Crystal Peak] from [Dirtmouth]': { - reward: { grubs: 1 }, - requires: { - checks: { - equipment: { - '[Mantis Claw]': checked, - '[Crystal Heart]': checked, - }, - }, - }, - }, - '[Crystal Peak] behind presses': { - reward: { grubs: 1 }, - requires: { - checks: { - equipment: { - '[Mantis Claw]': checked, - '[Mothwing Cloak]': checked, - }, - }, - }, - }, - '[Crystal Peak] near [Crystal Heart]': { - reward: { grubs: 1 }, - requires: { - checks: { - equipment: { - '[Mantis Claw]': checked, - '[Crystal Heart]': checked, - }, - }, - }, - }, - "[Crystal Peak] on the way to [Hallownest's Crown]": { - reward: { grubs: 1 }, - requires: { - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[Crystal Peak] vertical conveyor belts lever': { - reward: { grubs: 1 }, - requires: { - checks: { - equipment: { - '[Mantis Claw]': checked, - '[Mothwing Cloak]': checked, - }, - }, - }, - }, - '[Crystal Peak] from the top room with presses': { - reward: { grubs: 1 }, - requires: { - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[Crystal Peak] in the [Crystallized Mound]': { - reward: { grubs: 1 }, - requires: { - checks: { - equipment: { - '[Mantis Claw]': checked, - '[Mothwing Cloak]': checked, - }, - spells: { '[Desolate Dive]': checked }, - }, - }, - }, - '[Resting Grounds] [Crypts](Resting Grounds#Crypts)': { - reward: { grubs: 1 }, - requires: { - checks: { spells: { '[Desolate Dive]': checked } }, - }, - }, - '[Royal Waterways] behind a wall near water': { - reward: { grubs: 1 }, - }, - "[Royal Waterways] from the [Kingdom's Edge]": { - reward: { grubs: 1 }, - requires: { - checks: { - equipment: { - '[Crystal Heart]': checked, - '[Monarch Wings]': checked, - }, - items: { '[TRAM_PASS] [Tram Pass]': checked }, - }, - }, - }, - "[Royal Waterways] above [Isma's Tear]": { - reward: { grubs: 1 }, - requires: { - checks: { equipment: { "[Isma's Tear]": checked } }, - }, - }, - '[Howling Cliffs]': { - reward: { grubs: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - "[Kingdom's Edge] under [Oro]'s hut": { - reward: { grubs: 1 }, - requires: { - checks: { spells: { '[Desolate Dive]': checked } }, - }, - }, - "[Kingdom's Edge] behind a [Primal Aspid]": { - reward: { grubs: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[Fog Canyon]': { - reward: { grubs: 1 }, - requires: { - checks: { equipment: { '[Crystal Heart]': checked } }, - }, - }, - "[Queen's Gardens] under the [Stag] station": { - reward: { grubs: 1 }, - }, - "[Queen's Gardens] above the spiky roof": { - reward: { grubs: 1 }, - requires: { - checks: { - equipment: { - '[Mantis Claw]': checked, - '[Mothwing Cloak]': checked, - '[Crystal Heart]': checked, - }, - }, - }, - }, - "[Queen's Gardens] near [White Lady]": { - reward: { grubs: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[Deepnest] among [Grub Mimic]s': { - reward: { grubs: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[Deepnest] above the spike pit': { - reward: { grubs: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[Deepnest] on the way to [Nosk]': { - reward: { grubs: 1 }, - requires: { - checks: { equipment: { '[Crystal Heart]': checked } }, - }, - }, - "[Deepnest] near the [Weavers' Den]": { - reward: { grubs: 1 }, - requires: { - checks: { - equipment: { - '[Mantis Claw]': checked, - '[Mothwing Cloak]': checked, - }, - }, - }, - }, - "[Deepnest] in the [Beast's Den]": { - reward: { grubs: 1 }, - requires: { - checks: { - equipment: { - '[Mantis Claw]': checked, - '[Mothwing Cloak]': checked, - }, - }, - }, - }, - '[Ancient Basin] above [Broken Vessel]': { - reward: { grubs: 1 }, - requires: { - checks: { - equipment: { - '[Mothwing Cloak]': checked, - '[Monarch Wings]': checked, - }, - }, - }, - }, - '[Ancient Basin] under [Cloth]': { - reward: { grubs: 1 }, - requires: { - checks: { spells: { '[Desolate Dive]': checked } }, - }, - }, - '[The Hive] isolated room': { - reward: { grubs: 1 }, - requires: { - checks: { - equipment: { "[Isma's Tear]": checked }, - spells: { '[Desolate Dive]': checked }, - items: { '[TRAM_PASS] [Tram Pass]': checked }, - }, - }, - }, - '[The Hive]': { - reward: { grubs: 1 }, - requires: { - checks: { - equipment: { - '[Crystal Heart]': checked, - '[Monarch Wings]': checked, - }, - items: { '[TRAM_PASS] [Tram Pass]': checked }, - }, - }, - }, - '[Tower of Love] #1': { - reward: { grubs: 1 }, - requires: { - checks: { bosses: { '[The Collector]': checked } }, - }, - }, - '[Tower of Love] #2': { - reward: { grubs: 1 }, - requires: { - checks: { bosses: { '[The Collector]': checked } }, - }, - }, - '[Tower of Love] #3': { - reward: { grubs: 1 }, - requires: { - checks: { bosses: { '[The Collector]': checked } }, - }, - }, - }, - - items: { - '[SIMPLE_KEY] [Simple Key] from [Sly]': { - description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 950.', - reward: { simpleKeys: 1, geoReq: 950 }, - requires: { geo: 950 }, - }, - '[SIMPLE_KEY] [Simple Key] near [City Storerooms]': { - reward: { simpleKeys: 1 }, - requires: { - checks: { equipment: { '[Crystal Heart]': checked } }, - }, - }, - '[SIMPLE_KEY] [Simple Key] in the [Ancient Basin]': { - description: - 'In the [Mawlurk] area leading to [Broken Vessel].', - reward: { simpleKeys: 1 }, - requires: { - checks: { equipment: { '[Crystal Heart]': checked } }, - }, - }, - '[SIMPLE_KEY] [Simple Key] behind [Pale Lurker]': { - reward: { simpleKeys: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[ELEGANT_KEY] [Elegant Key]': { - description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 800.', - reward: { geoReq: 800 }, - requires: { - geo: 800, - checks: { - items: { - "[SHOPKEEPER'S_KEY] [Shopkeeper's Key]": checked, - }, - }, - }, - }, - '[LOVE_KEY] [Love Key]': { - reward: {}, - requires: { - checks: { equipment: { "[Isma's Tear]": checked } }, - }, - }, - "[SHOPKEEPER'S_KEY] [Shopkeeper's Key]": { - reward: {}, - requires: { - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[TRAM_PASS] [Tram Pass]': { - reward: {}, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[Lumafly Lantern]': { - description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 1800.', - reward: { geoReq: 1800 }, - requires: { geo: 1800 }, - }, - '[Delicate Flower]': { - reward: {}, - requires: { - checks: { spells: { '[Desolate Dive]': checked } }, - }, - }, - '[PALE_ORE] [Pale Ore] in [Ancient Basin] below [Cloth]': { - reward: { paleOre: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[PALE_ORE] [Pale Ore] awarded by the [Seer]': { - reward: { paleOre: 1, essenceReq: [300] }, - requires: { - essence: 300, - checks: { - relics: { - '[HALLOWNEST_SEAL] [Hallownest Seal] awarded by the [Seer]': - checked, - }, - }, - }, - }, - "[PALE_ORE] [Pale Ore] on the [Hallownest's Crown]": { - reward: { paleOre: 1 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[PALE_ORE] [Pale Ore] behind [Nosk]s lair': { - reward: { paleOre: 1 }, - requires: { checks: { bosses: { '[Nosk]': checked } } }, - }, - '[PALE_ORE] [Pale Ore] awarded by [Grubfather]': { - reward: { paleOre: 1 }, - requires: { grubs: 31 }, - }, - '[PALE_ORE] [Pale Ore] reward in [Trial of the Conqueror]': { - reward: { paleOre: 1 }, - requires: { - checks: { - colosseum: { '[Trial of the Conqueror]': checked }, - }, - }, - }, - '[CHARM_NOTCH] [Charm Notch] from [Salubra] #1': { - reward: {}, - requires: { charms: 5 }, - }, - '[CHARM_NOTCH] [Charm Notch] from [Salubra] #2': { - reward: {}, - requires: { charms: 10 }, - }, - '[CHARM_NOTCH] [Charm Notch] from [Salubra] #3': { - reward: {}, - requires: { charms: 18 }, - }, - '[CHARM_NOTCH] [Charm Notch] from [Salubra] #4': { - reward: {}, - requires: { charms: 25 }, - }, - '[CHARM_NOTCH] [Charm Notch] in [Fog Canyon]': { - reward: {}, - requires: { - checks: { equipment: { "[Isma's Tear]": checked } }, - }, - }, - '[CHARM_NOTCH] [Charm Notch] in [Fungal Wastes]': { - reward: {}, - requires: { - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[CHARM_NOTCH] [Charm Notch] from [Colosseum of Fools]': { - reward: {}, - requires: { - checks: { - colosseum: { '[Trial of the Warrior]': checked }, - }, - }, - }, - '[CHARM_NOTCH] [Charm Notch] from [Grimm]': { - reward: {}, - requires: { checks: { bosses: { '[Grimm]': checked } } }, - }, - }, - - relics: { - "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Greenpath] near a [Stag Station]": - { reward: { geo: 200 } }, - "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Greenpath] near [Fog Canyon] entrance": - { reward: { geo: 200 } }, - "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Fungal Wastes] near [Shrumal Ogre]s": - { reward: { geo: 200 } }, - "[WANDERER'S_JOURNAL] [Wanderer's Journal] north of the [Mantis Village]": - { - reward: { geo: 200 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [City Storerooms]": { - reward: { geo: 200 }, - }, - "[WANDERER'S_JOURNAL] [Wanderer's Journal] north of [King's Station]": - { reward: { geo: 200 } }, - "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Pleasure House]": { - reward: { geo: 200, simpleKeysReq: 1 }, - requires: { simpleKeys: 1 }, - }, - "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Howling Cliffs]": { - reward: { geo: 200 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Crystal Peak]": { - reward: { geo: 200 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Crypts](Resting Grounds#Crypts)": - { reward: { geo: 200 } }, - "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Royal Waterways]": { - description: - 'Near the spikes in a room which connects to the [Ancient Basin].', - reward: { geo: 200 }, - }, - "[WANDERER'S_JOURNAL] [Wanderer's Journal] near [City of Tears] entrance": - { - reward: { geo: 200 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - "[WANDERER'S_JOURNAL] [Wanderer's Journal] next to the [Cast-Off Shell] [Bench]": - { - reward: { geo: 200 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - "[WANDERER'S_JOURNAL] [Wanderer's Journal] near [Markoth]": { - reward: { geo: 200 }, - requires: { - checks: { spells: { '[Desolate Dive]': checked } }, - }, - }, - '[HALLOWNEST_SEAL] [Hallownest Seal] awarded by [Grubfather]': { - reward: { geo: 450 }, - requires: { grubs: 23 }, - }, - '[HALLOWNEST_SEAL] [Hallownest Seal] in the well to [Forgotten Crossroads]': - { - reward: { geo: 450 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[HALLOWNEST_SEAL] [Hallownest Seal] near [Thorns of Agony]': { - reward: { geo: 450 }, - requires: { - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - "[HALLOWNEST_SEAL] [Hallownest Seal] near [Queen's Station]": { - reward: { geo: 450 }, - }, - '[HALLOWNEST_SEAL] [Hallownest Seal] in [Mantis Village]': { - reward: { geo: 450 }, - requires: { checks: { bosses: { '[Mantis Lords]': checked } } }, - }, - '[HALLOWNEST_SEAL] [Hallownest Seal] at the [Willoh]': { - reward: { geo: 450 }, - requires: { - checks: { equipment: { '[Monarch Wings]': checked } }, - }, - }, - '[HALLOWNEST_SEAL] [Hallownest Seal] near [Overgrown Mound]': { - reward: { geo: 450 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[HALLOWNEST_SEAL] [Hallownest Seal] in [Forgotten Crossroads] in [Fog Canyon] entrance': - { - reward: { geo: 450 }, - requires: { - checks: { equipment: { "[Isma's Tear]": checked } }, - }, - }, - '[HALLOWNEST_SEAL] [Hallownest Seal] in [Crypts](Resting Grounds#Crypts)': - { - reward: { geo: 450 }, - requires: { - checks: { spells: { '[Desolate Dive]': checked } }, - }, - }, - '[HALLOWNEST_SEAL] [Hallownest Seal] awarded by the [Seer]': { - reward: { geo: 450, essenceReq: [100] }, - requires: { essence: 100 }, - }, - '[HALLOWNEST_SEAL] [Hallownest Seal] near [Relic Seeker Lemm]': { - reward: { geo: 450 }, - }, - "[HALLOWNEST_SEAL] [Hallownest Seal] above [King's Station] [Stag Station]": - { - reward: { geo: 450 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - '[HALLOWNEST_SEAL] [Hallownest Seal] near [Soul Master]': { - reward: { geo: 450 }, - requires: { - checks: { spells: { '[Desolate Dive]': checked } }, - }, - }, - '[HALLOWNEST_SEAL] [Hallownest Seal] behind [Watcher Knight]': { - reward: { geo: 450 }, - requires: { - checks: { bosses: { '[Watcher Knight]': checked } }, - }, - }, - "[HALLOWNEST_SEAL] [Hallownest Seal] in [Beast's Den]": { - reward: { geo: 450 }, - requires: { - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - '[HALLOWNEST_SEAL] [Hallownest Seal] in [Deepnest] near [Mantis Lords]': - { - reward: { geo: 450 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - "[HALLOWNEST_SEAL] [Hallownest Seal] in [Queen's Gardens]": { - reward: { geo: 450 }, - requires: { - checks: { equipment: { '[Monarch Wings]': checked } }, - }, - }, - "[KING'S_IDOL] [King's Idol] awarded by [Grubfather]": { - reward: { geo: 800 }, - requires: { grubs: 38 }, - }, - "[KING'S_IDOL] [King's Idol] in [Crystal Peak]": { - reward: { geo: 800 }, - requires: { - checks: { equipment: { '[Monarch Wings]': checked } }, - }, - }, - "[KING'S_IDOL] [King's Idol] in [Spirits' Glade]": { - reward: { geo: 800, essenceReq: [200] }, - requires: { - essence: 200, - checks: { equipment: { '[Mothwing Cloak]': checked } }, - }, - }, - "[KING'S_IDOL] [King's Idol] in [Dung Defender]'s secret room": { - reward: { geo: 800 }, - requires: { - checks: { spells: { '[Desolate Dive]': checked } }, - }, - }, - "[KING'S_IDOL] [King's Idol] in [Howling Cliffs]": { - reward: { geo: 800 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - "[KING'S_IDOL] [King's Idol] under [Colosseum of Fools]": { - reward: { geo: 800 }, - requires: { - checks: { equipment: { '[Mantis Claw]': checked } }, - }, - }, - "[KING'S_IDOL] [King's Idol] near [Pale Lurker]": { - reward: { geo: 800 }, - requires: { - checks: { - equipment: { - '[Mantis Claw]': checked, - "[Isma's Tear]": checked, - }, - }, - }, - }, - "[KING'S_IDOL] [King's Idol] in [Deepnest] near [Zote]": { - reward: { geo: 800 }, - }, - '[ARCANE_EGG] [Arcane Egg] below [Lifeblood Core]': { - reward: { geo: 1200 }, - requires: { - checks: { - equipment: { - "[King's Brand]": checked, - '[Crystal Heart]': checked, - }, - charms: { "[Joni's Blessing]": checked }, - }, - }, - }, - '[ARCANE_EGG] [Arcane Egg] near [Shade Cloak]': { - reward: { geo: 1200 }, - requires: { - checks: { equipment: { '[Shade Cloak]': checked } }, - }, - }, - '[ARCANE_EGG] [Arcane Egg] in [Birthplace]': { - reward: { geo: 1200 }, - requires: { - checks: { - charms: { '[Kingsoul] / [Void Heart]': checked }, - }, - }, - }, - '[ARCANE_EGG] [Arcane Egg] awarded by the [Seer]': { - reward: { geo: 1200, essenceReq: [1200] }, - requires: { - essence: 1200, - checks: { vesselFragments: { '[Seer]': checked } }, - }, - }, - }, - - whisperingRoots: { - '[Ancestral Mound]': { - reward: { essence: 42 }, - requires: { - checks: { - dreamNail: { '[Dream Nail]': checked }, - spells: { '[Vengeful Spirit]': checked }, - }, - }, - }, - '[City of Tears]': { - reward: { essence: 28 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Crystal Peak]': { - reward: { essence: 21 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Deepnest]': { - reward: { essence: 45 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Forgotten Crossroads]': { - reward: { essence: 29 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Fungal Wastes] (near [Fog Canyon])': { - reward: { essence: 20 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Fungal Wastes] (above [Mantis Village])': { - reward: { essence: 18 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Greenpath]': { - reward: { essence: 44 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[The Hive]': { - reward: { essence: 20 }, - requires: { - checks: { - dreamNail: { '[Dream Nail]': checked }, - items: { '[TRAM_PASS] [Tram Pass]': checked }, - }, - }, - }, - '[Howling Cliffs]': { - reward: { essence: 46 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - "[Kingdom's Edge]": { - reward: { essence: 51 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - "[Queen's Gardens]": { - reward: { essence: 29 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Resting Grounds]': { - reward: { essence: 20 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - '[Royal Waterways]': { - reward: { essence: 35 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - "[Spirits' Glade]": { - reward: { essence: 34 }, - requires: { - checks: { dreamNail: { '[Dream Nail]': checked } }, - }, - }, - }, - }, -}; - -const INITIAL_SILKSONG_CHECKLIST_STATE: SilksongChecklistState = { - game: 'silksong', - percent: 0, - rosaries: 0, - rosariesReq: 0, - - checks: { - bosses: { - '[Lace]': { - reward: {}, - }, - }, - }, -}; +import INITIAL_HOLLOW_KNIGHT_CHECKLIST_STATE from './INITIAL_HOLLOW_KNIGHT_CHECKLIST_STATE'; +import INITIAL_SILKSONG_CHECKLIST_STATE from './INITIAL_SILKSONG_CHECKLIST_STATE'; const INITIAL_CHECKLIST_STATE = { 'hollow-knight': INITIAL_HOLLOW_KNIGHT_CHECKLIST_STATE, diff --git a/src/stores/INITIAL_HOLLOW_KNIGHT_CHECKLIST_STATE.ts b/src/stores/INITIAL_HOLLOW_KNIGHT_CHECKLIST_STATE.ts new file mode 100644 index 0000000..7d3fe18 --- /dev/null +++ b/src/stores/INITIAL_HOLLOW_KNIGHT_CHECKLIST_STATE.ts @@ -0,0 +1,2114 @@ +import { HollowKnightChecklistState } from '../types/checklist'; + +const nothing = {} as const; +const checked = { checked: true } as const; +const charms = 1 as const; +const grubs = { grubs: 1 } as const; +const maskShards = 1 as const; +const vesselFragments = 1 as const; +const percent = 1 as const; +const simpleKeys = 1 as const; +const simpleKeysReq = 1 as const; + +const INITIAL_HOLLOW_KNIGHT_CHECKLIST_STATE: HollowKnightChecklistState = { + game: 'hollow-knight', + percent: 0, + + geo: 0, + essence: 0, + paleOre: 0, + simpleKeys: 0, + + charms: 0, + grubs: 0, + maskShards: 0, + vesselFragments: 0, + + geoReq: 0, + essenceReq: [0], + paleOreReq: 0, + simpleKeysReq: 0, + + checks: { + bosses: { + '[Broken Vessel]': { + reward: { percent }, + requires: { + checks: { equipment: { '[Crystal Heart]': checked } }, + }, + }, + '[Brooding Mawlek]': { reward: { percent } }, + '[The Collector]': { + reward: { percent }, + requires: { + checks: { + equipment: { '[Mantis Claw]': checked }, + items: { '[LOVE_KEY] [Love Key]': checked }, + }, + }, + }, + '[Dung Defender]': { + reward: { percent, simpleKeysReq }, + requires: { + simpleKeys, + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[False Knight]': { reward: { percent, geo: 200 } }, + '[Grimm]': { + reward: { percent }, + requires: { + checks: { + charms: { + '[Grimmchild] / [Carefree Melody]': checked, + }, + }, + }, + }, + '[Gruz Mother]': { reward: { percent, geo: 50 } }, + '[Hive Knight]': { + reward: { percent }, + requires: { + checks: { + items: { '[TRAM_PASS] [Tram Pass]': checked }, + }, + }, + }, + '[Hornet Protector]': { + reward: { percent }, + requires: { + checks: { spells: { '[Vengeful Spirit]': checked } }, + }, + }, + '[Hornet Sentinel]': { + reward: { percent }, + requires: { + checks: { equipment: { '[Monarch Wings]': checked } }, + }, + }, + '[Mantis Lords]': { + reward: { percent, geo: 620 }, + requires: { + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[Nosk]': { + reward: { percent }, + requires: { + checks: { equipment: { '[Crystal Heart]': checked } }, + }, + }, + '[Soul Master]': { + reward: { percent, geo: 380 }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[Traitor Lord]': { + reward: { percent }, + requires: { + checks: { equipment: { '[Shade Cloak]': checked } }, + }, + }, + '[Uumuu]': { + reward: { percent }, + requires: { + checks: { equipment: { "[Isma's Tear]": checked } }, + }, + }, + '[Watcher Knight]': { + reward: { percent, geo: 655 }, + requires: { + checks: { equipment: { '[Monarch Wings]': checked } }, + }, + }, + }, + + optionalBosses: { + '[Absolute Radiance]': { + reward: nothing, + requires: { + checks: { + godhome: { + '[Pantheon of the Hallownest] (no percent)': + checked, + }, + }, + }, + }, + '[Brothers Oro & Mato]': { + reward: nothing, + requires: { + checks: { + godhome: { '[Pantheon of the Master]': checked }, + }, + }, + }, + '[Crystal Guardian]': { + reward: { geo: 385 }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[Enraged Guardian]': { + reward: { geo: 550 }, + requires: { + checks: { + optionalBosses: { '[Crystal Guardian]': checked }, + equipment: { '[Monarch Wings]': checked }, + }, + }, + }, + '[Flukemarm]': { + reward: nothing, + requires: { + checks: { spells: { '[Desolate Dive]': checked } }, + }, + }, + '[God Tamer]': { + reward: nothing, + requires: { + checks: { + colosseum: { '[Trial of the Fool]': checked }, + }, + }, + }, + '[Great Nailsage Sly]': { + reward: nothing, + requires: { + checks: { + godhome: { '[Pantheon of the Sage]': checked }, + }, + }, + }, + '[Hollow Knight]': { + reward: nothing, + requires: { + checks: { + dreamers: { + '[Herra the Beast]': checked, + '[Lurien the Watcher]': checked, + '[Monomon the Teacher]': checked, + }, + }, + }, + }, + '[Massive Moss Charger]': { + reward: { geo: 300 }, + requires: { + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[Oblobbles]': { + reward: nothing, + requires: { + checks: { + colosseum: { '[Trial of the Conqueror]': checked }, + }, + }, + }, + '[Paintmaster Sheo]': { + reward: nothing, + requires: { + checks: { + godhome: { '[Pantheon of the Artist]': checked }, + }, + }, + }, + '[Pure Vessel]': { + reward: nothing, + requires: { + checks: { + godhome: { '[Pantheon of the Knight]': checked }, + }, + }, + }, + '[Radiance]': { + reward: nothing, + requires: { + checks: { + charms: { '[Kingsoul] / [Void Heart]': checked }, + optionalBosses: { '[Hollow Knight]': checked }, + dreamNail: { '[Dream Nail]': checked }, + }, + }, + }, + '[Sisters of Battle]': { + reward: nothing, + requires: { + checks: { + godhome: { + '[Pantheon of the Hallownest] (no percent)': + checked, + }, + }, + }, + }, + '[Soul Warrior]': { reward: { geo: 200 } }, + '[Vengefly King]': { + reward: { geo: 65 }, + requires: { + checks: { spells: { '[Vengeful Spirit]': checked } }, + }, + }, + '[Winged Nosk]': { + reward: nothing, + requires: { + checks: { + godhome: { + '[Pantheon of the Hallownest] (no percent)': + checked, + }, + }, + }, + }, + '[Zote]': { + reward: nothing, + requires: { + checks: { + colosseum: { '[Trial of the Warrior]': checked }, + optionalBosses: { '[Vengefly King]': checked }, + }, + }, + }, + }, + + equipment: { + '[Crystal Heart]': { + reward: { percent: 2 }, + requires: { + checks: { + equipment: { + '[Mantis Claw]': checked, + '[Mothwing Cloak]': checked, + }, + }, + }, + }, + "[Isma's Tear]": { + reward: { percent: 2 }, + requires: { + checks: { + bosses: { '[Dung Defender]': checked }, + equipment: { '[Crystal Heart]': checked }, + }, + }, + }, + '[Mantis Claw]': { + reward: { percent: 2 }, + requires: { + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[Monarch Wings]': { + reward: { percent: 2 }, + requires: { + checks: { + bosses: { '[Broken Vessel]': checked }, + equipment: { + '[Crystal Heart]': checked, + '[Mantis Claw]': checked, + }, + }, + }, + }, + '[Mothwing Cloak]': { + reward: { percent: 2 }, + requires: { + checks: { bosses: { '[Hornet Protector]': checked } }, + }, + }, + '[Shade Cloak]': { + reward: { percent: 2 }, + requires: { + checks: { equipment: { "[King's Brand]": checked } }, + }, + }, + "[King's Brand]": { + reward: { percent: 2 }, + requires: { + checks: { + bosses: { '[Hornet Sentinel]': checked }, + equipment: { '[Monarch Wings]': checked }, + }, + }, + }, + }, + + nail: { + '[Sharpened Nail](Nail#Upgrades)': { + reward: { percent, geoReq: 250 }, + requires: { geo: 250 }, + }, + '[Channelled Nail](Nail#Upgrades)': { + reward: { percent, geoReq: 800, paleOreReq: 1 }, + requires: { + geo: 800, + paleOre: 1, + checks: { + nail: { + '[Sharpened Nail](Nail#Upgrades)': checked, + }, + }, + }, + }, + '[Coiled Nail](Nail#Upgrades)': { + reward: { percent, geoReq: 2000, paleOreReq: 2 }, + requires: { + geo: 2000, + paleOre: 2, + checks: { + nail: { + '[Channelled Nail](Nail#Upgrades)': checked, + }, + }, + }, + }, + '[Pure Nail](Nail#Upgrades)': { + reward: { percent, geoReq: 4000, paleOreReq: 3 }, + requires: { + geo: 4000, + paleOre: 3, + checks: { + nail: { '[Coiled Nail](Nail#Upgrades)': checked }, + }, + }, + }, + }, + + dreamNail: { + '[Dream Nail]': { + reward: { percent }, + requires: { + checks: { items: { '[Lumafly Lantern]': checked } }, + }, + }, + '[Awoken Dream Nail]': { + reward: { percent, essenceReq: [1800] }, + requires: { + essence: 1800, + checks: { + dreamNail: { '[Dream Nail]': checked }, + maskShards: { '[Seer]': checked }, + }, + }, + }, + '[Ascension](Seer)': { + reward: { percent, essenceReq: [2400] }, + requires: { + essence: 2400, + checks: { + dreamNail: { '[Awoken Dream Nail]': checked }, + }, + }, + }, + }, + + nailArts: { + '[Cyclone Slash]': { + reward: { percent }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[Dash Slash]': { + reward: { percent, geoReq: 800 }, + requires: { geo: 800 }, + }, + '[Great Slash]': { + reward: { percent }, + requires: { + checks: { equipment: { '[Crystal Heart]': checked } }, + }, + }, + }, + + spells: { + '[Desolate Dive]': { + reward: { percent }, + requires: { + checks: { bosses: { '[Soul Master]': checked } }, + }, + }, + '[Descending Dark]': { + reward: { percent }, + requires: { + checks: { + items: { '[Lumafly Lantern]': checked }, + spells: { '[Desolate Dive]': checked }, + }, + }, + }, + '[Howling Wraiths]': { + reward: { percent }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[Abyss Shriek]': { + reward: { percent }, + requires: { + checks: { + equipment: { "[King's Brand]": checked }, + spells: { '[Howling Wraiths]': checked }, + }, + }, + }, + '[Vengeful Spirit]': { + reward: { percent }, + requires: { + checks: { bosses: { '[False Knight]': checked } }, + }, + }, + '[Shade Soul]': { + reward: { percent }, + requires: { + checks: { + items: { '[ELEGANT_KEY] [Elegant Key]': checked }, + }, + }, + }, + }, + + charms: { + '[Wayward Compass]': { + description: + 'Bought from [Iselda] in [Dirtmouth] for [GEO] 220 after the first encounter with [Cornifer].', + reward: { percent, charms, geoReq: 220 }, + requires: { geo: 220 }, + }, + '[Gathering Swarm]': { + description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 300.', + reward: { percent, charms, geoReq: 300 }, + requires: { geo: 300 }, + }, + '[Stalwart Shell]': { + description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 200.', + reward: { percent, charms, geoReq: 200 }, + requires: { geo: 200 }, + }, + '[Soul Catcher]': { + description: + 'Found at the very end of the [Ancestral Mound], after killing the [Elder Baldur].', + reward: { percent, charms }, + requires: { + checks: { spells: { '[Vengeful Spirit]': checked } }, + }, + }, + '[Shaman Stone]': { + description: + 'Bought from [Salubra] for [GEO] 220 in the [Forgotten Crossroads].', + reward: { percent, charms, geoReq: 220 }, + requires: { + geo: 220, + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[Soul Eater]': { + description: + 'Found to the east side of the [Crypts](Resting Grounds#Crypts).', + reward: { percent, charms }, + requires: { + checks: { spells: { '[Desolate Dive]': checked } }, + }, + }, + '[Dashmaster]': { + description: + 'Found beneath a statue below the [Mantis Village] in the [Fungal Wastes], near the entrance to the [Royal Waterways].', + reward: { percent, charms }, + requires: { + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[Sprintmaster]': { + description: + "Bought from [Sly] in [Dirtmouth] after acquiring the [SHOPKEEPER'S_KEY] [Shopkeeper's Key] for [GEO] 400.", + reward: { percent, charms, geoReq: 400 }, + requires: { + geo: 400, + checks: { + items: { + "[SHOPKEEPER'S_KEY] [Shopkeeper's Key]": checked, + }, + }, + }, + }, + '[Grubsong]': { + description: + 'Gifted by [Grubfather] after 10 [Grubs] have been freed.', + reward: { percent, charms }, + requires: { grubs: 10 }, + }, + "[Grubberfly's Elegy]": { + description: + 'Gifted by [Grubfather] after freeing all 46 [Grubs].', + reward: { percent, charms }, + requires: { grubs: 46 }, + }, + '[Fragile Heart] / [Unbreakable Heart]': { + description: + 'Bought from [Leg Eater] in [Fungal Wastes] for [GEO] 350.', + reward: { percent, charms, geoReq: 350 }, + requires: { geo: 350 }, + }, + '[Fragile Greed] / [Unbreakable Greed]': { + description: + 'Bought from [Leg Eater] in [Fungal Wastes] for [GEO] 250.', + reward: { percent, charms, geoReq: 250 }, + requires: { geo: 250 }, + }, + '[Fragile Strength] / [Unbreakable Strength]': { + description: + 'Bought from [Leg Eater] in [Fungal Wastes] for [GEO] 600.', + reward: { percent, charms, geoReq: 600 }, + requires: { geo: 600 }, + }, + '[Spell Twister]': { + description: + 'A secret room on the top of the [Soul Sanctum], just before fighting [Soul Master].', + reward: { percent, charms }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[Steady Body]': { + description: + 'Bought from [Salubra] for [GEO] 120 in the [Forgotten Crossroads].', + reward: { percent, charms, geoReq: 120 }, + requires: { + geo: 120, + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[Heavy Blow]': { + description: + "Bought from [Sly] in [Dirtmouth] after acquiring the [SHOPKEEPER'S_KEY] [Shopkeeper's Key] for [GEO] 350.", + reward: { percent, charms, geoReq: 350 }, + requires: { + geo: 350, + checks: { + items: { + "[SHOPKEEPER'S_KEY] [Shopkeeper's Key]": checked, + }, + }, + }, + }, + '[Quick Slash]': { + description: + 'Located in [Kingdom\'s Edge], on a massive anvil in a hidden room in front of a massive corpse known as an "[Ancient Nailsmith]".', + reward: { percent, charms }, + requires: { + checks: { spells: { '[Desolate Dive]': checked } }, + }, + }, + '[Longnail]': { + description: + 'Bought from [Salubra] in the [Forgotten Crossroads] for [GEO] 300.', + reward: { percent, charms, geoReq: 300 }, + requires: { + geo: 300, + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[Mark of Pride]': { + description: + 'In the [Mantis Village], in a chest in a room northeast of the [Mantis Lords] arena.', + reward: { percent, charms }, + requires: { + checks: { bosses: { '[Mantis Lords]': checked } }, + }, + }, + '[Fury of the Fallen]': { + description: + "Found in [King's Pass], the starting cavern, behind a spike-filled cavern.", + reward: { percent, charms }, + }, + '[Thorns of Agony]': { + description: + 'Found in [Greenpath] in a maze of thorns featuring [Charged Lumaflies].', + reward: { percent, charms }, + requires: { + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[Baldur Shell]': { + description: + 'Found in the southwest portion of the [Howling Cliffs], where there is a chest that drops only [GEO] 1.', + reward: { percent, charms }, + requires: { + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[Flukenest]': { + description: + 'Dropped by [Flukemarm] in the [Royal Waterways] when defeated.', + reward: { percent, charms }, + requires: { + checks: { + spells: { '[Desolate Dive]': checked }, + optionalBosses: { '[Flukemarm]': checked }, + }, + }, + }, + "[Defender's Crest]": { + description: + 'Reward from defeating [Dung Defender] in the [Royal Waterways].', + reward: { percent, charms }, + requires: { + checks: { bosses: { '[Dung Defender]': checked } }, + }, + }, + '[Glowing Womb]': { + description: + 'Found in the [Aspid Nest](Forgotten Crossroads#Aspid_Nest) after completing [Aspid] arena.', + reward: { percent, charms }, + requires: { + checks: { equipment: { '[Crystal Heart]': checked } }, + }, + }, + '[Quick Focus]': { + description: + 'Bought from [Salubra] in the [Forgotten Crossroads] for [GEO] 800.', + reward: { percent, charms, geoReq: 800 }, + requires: { + geo: 800, + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[Deep Focus]': { + description: + 'Found in [Crystal Peak] in a hidden cave made entirely of crystals.', + reward: { percent, charms }, + requires: { + checks: { equipment: { '[Crystal Heart]': checked } }, + }, + }, + '[Lifeblood Heart]': { + description: + 'Bought from [Salubra] in the [Forgotten Crossroads] for [GEO] 250.', + reward: { percent, charms, geoReq: 250 }, + requires: { + geo: 250, + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[Lifeblood Core]': { + description: + 'Behind the door in the [Abyss] that opens when you have 15 or more [Lifeblood Masks](Knight#Health).', + reward: { percent, charms }, + requires: { + checks: { + equipment: { "[King's Brand]": checked }, + charms: { + "[Joni's Blessing]": checked, + '[Fragile Heart] / [Unbreakable Heart]': checked, + }, + }, + maskShards: 8, + }, + }, + "[Joni's Blessing]": { + description: + "Found in [Joni's Repose] in the [Howling Cliffs].", + reward: { percent, charms }, + requires: { + checks: { + equipment: { '[Mantis Claw]': checked }, + items: { '[Lumafly Lantern]': checked }, + }, + }, + }, + '[Hiveblood]': { + description: + 'Located in [the Hive], below the room where the [Hive Knight] is fought.', + reward: { percent, charms }, + requires: { + checks: { + bosses: { '[Hive Knight]': checked }, + items: { '[TRAM_PASS] [Tram Pass]': checked }, + }, + }, + }, + '[Spore Shroom]': { + description: + "Found in the [Fungal Wastes] near a pool of acid, close to the entrances to the Queen's Gardens and Deepnest.", + reward: { percent, charms }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[Sharp Shadow]': { + description: + 'Located in [Deepnest], southeast of the Hot Spring behind a [Shade Gate].', + reward: { percent, charms }, + requires: { + checks: { equipment: { '[Shade Cloak]': checked } }, + }, + }, + '[Shape of Unn]': { + description: 'Acquired from [Unn] beneath the [Lake of Unn].', + reward: { percent, charms }, + requires: { + checks: { equipment: { "[Isma's Tear]": checked } }, + }, + }, + "[Nailmaster's Glory]": { + description: + 'Given by [Sly] after receiving all 3 [Nail Arts] from the [Nailmasters].', + reward: { percent, charms }, + requires: { + checks: { + nailArts: { + '[Cyclone Slash]': checked, + '[Dash Slash]': checked, + '[Great Slash]': checked, + }, + }, + }, + }, + '[Weaversong]': { + description: "Found in the upper part of [Weavers' Den].", + reward: { percent, charms }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[Dream Wielder]': { + description: + 'Given by the [Seer] after gathering [ESSENCE] 500.', + reward: { percent, charms, essenceReq: [500] }, + requires: { + essence: 500, + checks: { + items: { + '[PALE_ORE] [Pale Ore] awarded by the [Seer]': + checked, + }, + }, + }, + }, + '[Dreamshield]': { + description: + "Found in a room in the [Resting Grounds], below [Seer]'s room.", + reward: { percent, charms }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Grimmchild] / [Carefree Melody]': { + description: + '[Grimmchild] is given by [Troupe Master Grimm] in Dirtmouth after [the Grimm Troupe] has been summoned. ' + + 'After banishing the Grimm Troupe, the [Carefree Melody] charm can be acquired from [Nymm] by listening to him in [Dirtmouth].', + reward: { percent, charms }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Kingsoul] / [Void Heart]': { + description: + 'Obtained after getting both [White Fragments]. ' + + 'After obtaining the [Kingsoul], [Void Heart] can be found in [the Birthplace] at the bottom of [the Abyss].', + reward: { percent, charms }, + requires: { + checks: { + bosses: { '[Traitor Lord]': checked }, + equipment: { '[Monarch Wings]': checked }, + dreamNail: { '[Awoken Dream Nail]': checked }, + }, + }, + }, + }, + + maskShards: { + '[Sly] #1': { + description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 150.', + reward: { maskShards, geoReq: 150 }, + requires: { geo: 150 }, + }, + '[Sly] #2': { + description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 500.', + reward: { maskShards, geoReq: 500 }, + requires: { + geo: 500, + checks: { maskShards: { '[Sly] #1': checked } }, + }, + }, + '[Sly] #3': { + description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 800.', + reward: { maskShards, geoReq: 800 }, + requires: { + geo: 800, + checks: { + maskShards: { '[Sly] #2': checked }, + items: { + "[SHOPKEEPER'S_KEY] [Shopkeeper's Key]": checked, + }, + }, + }, + }, + '[Sly] #4': { + description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 1500.', + reward: { maskShards, geoReq: 1500 }, + requires: { + geo: 1500, + checks: { + maskShards: { '[Sly] #3': checked }, + items: { + "[SHOPKEEPER'S_KEY] [Shopkeeper's Key]": checked, + }, + }, + }, + }, + '[Forgotten Crossroads] [Brooding Mawlek]': { + description: 'Reward for defeating [Brooding Mawlek].', + reward: { maskShards }, + requires: { + checks: { bosses: { '[Brooding Mawlek]': checked } }, + }, + }, + '[Grubfather]': { + description: 'Requires rescuing 5 [Grubs].', + reward: { maskShards }, + requires: { grubs: 5 }, + }, + '[Forgotten Crossroads] [Goams]': { + description: + 'Behind a gauntlet of [Goams] in [Forgotten Crossroads].', + reward: { maskShards }, + requires: { + checks: { equipment: { '[Monarch Wings]': checked } }, + }, + }, + "[Queen's Station]": { + description: "Near east side of the [Queen's Station].", + reward: { maskShards }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + "[Bretta]'s house": { + description: 'Requires rescuing [Bretta] from [Fungal Wastes].', + reward: { maskShards }, + requires: { + checks: { + equipment: { + '[Mothwing Cloak]': checked, + '[Mantis Claw]': checked, + }, + }, + }, + }, + '[Stone Sanctuary]': { + reward: { maskShards }, + requires: { + checks: { items: { '[Lumafly Lantern]': checked } }, + }, + }, + '[Royal Waterways]': { + description: + 'Northwest section of the Royal Waterways, swim west under main path.', + reward: { maskShards }, + requires: { simpleKeys }, + }, + '[Deepnest] from [Fungal Core]': { + reward: { maskShards }, + requires: { + checks: { equipment: { '[Monarch Wings]': checked } }, + }, + }, + '[Enraged Guardian]': { + reward: { maskShards }, + requires: { + checks: { + equipment: { '[Monarch Wings]': checked }, + optionalBosses: { '[Enraged Guardian]': checked }, + }, + }, + }, + '[The Hive]': { + description: + 'Requires baiting a [Hive Guardian] into breaking a wall.', + reward: { maskShards }, + requires: { + checks: { + items: { '[TRAM_PASS] [Tram Pass]': checked }, + }, + }, + }, + '[Seer]': { + description: 'For collecting [ESSENCE] 1500.', + reward: { maskShards, essenceReq: [1500] }, + requires: { + essence: 1500, + checks: { + relics: { + '[ARCANE_EGG] [Arcane Egg] awarded by the [Seer]': + checked, + }, + }, + }, + }, + '[Grey Mourner]': { + description: 'Requires completing the [Delicate Flower quest].', + reward: { maskShards }, + requires: { + checks: { + items: { '[Delicate Flower]': checked }, + equipment: { '[Mothwing Cloak]': checked }, + }, + }, + }, + }, + + vesselFragments: { + '[Sly] #1': { + description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 550.', + reward: { vesselFragments, geoReq: 550 }, + requires: { geo: 550 }, + }, + '[Sly] #2': { + description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 900.', + reward: { vesselFragments, geoReq: 900 }, + requires: { + geo: 900, + checks: { + vesselFragments: { '[Sly] #1': checked }, + items: { + "[SHOPKEEPER'S_KEY] [Shopkeeper's Key]": checked, + }, + }, + }, + }, + '[Greenpath]': { + description: + "Near the inaccessible [Queen's Gardens] entrance.", + reward: { vesselFragments }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + 'Left of the lift in [Forgotten Crossroads]': { + description: + 'Accessible after unlocking the lift in the [City of Tears].', + reward: { vesselFragments }, + }, + "Above [King's Station] near a lift": { + description: 'Accessible after completing the arena.', + reward: { vesselFragments }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[Deepnest]': { + description: + 'At the end of the [Garpede] parkour section above the working [Tram].', + reward: { vesselFragments }, + }, + '[Stag Nest]': { reward: { vesselFragments } }, + '[Seer]': { + description: 'For collecting [ESSENCE] 700.', + reward: { vesselFragments, essenceReq: [700] }, + requires: { + essence: 700, + checks: { charms: { '[Dream Wielder]': checked } }, + }, + }, + '[Ancient Basin] fountain': { + description: 'For dropping [GEO] 3000 into the fountain.', + reward: { vesselFragments, geoReq: 3000 }, + requires: { geo: 3000 }, + }, + }, + + dreamers: { + '[Herra the Beast]': { + reward: { percent }, + requires: { + checks: { + dreamNail: { '[Dream Nail]': checked }, + equipment: { '[Mantis Claw]': checked }, + items: { '[Lumafly Lantern]': checked }, + }, + }, + }, + '[Lurien the Watcher]': { + reward: { percent }, + requires: { + checks: { + bosses: { '[Watcher Knight]': checked }, + dreamNail: { '[Dream Nail]': checked }, + }, + }, + }, + '[Monomon the Teacher]': { + reward: { percent }, + requires: { + checks: { + bosses: { '[Uumuu]': checked }, + dreamNail: { '[Dream Nail]': checked }, + }, + }, + }, + }, + + dreamWarriors: { + '[Elder Hu]': { + reward: { percent, essence: 100 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Galien]': { + reward: { percent, essence: 200 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Gorb]': { + reward: { percent, essence: 100 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Markoth]': { + reward: { percent, essence: 250 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Marmu]': { + reward: { percent, essence: 150 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[No Eyes]': { + reward: { percent, essence: 200 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Xero]': { + reward: { percent, essence: 100 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Nightmare King Grimm] / [Banishment](Grimm Troupe (Quest))': { + reward: { percent }, + requires: { + checks: { + dreamNail: { '[Dream Nail]': checked }, + charms: { + '[Grimmchild] / [Carefree Melody]': checked, + }, + }, + }, + }, + }, + + dreamBosses: { + '[Failed Champion]': { + reward: { essence: 300 }, + requires: { + checks: { + dreamNail: { '[Dream Nail]': checked }, + bosses: { '[False Knight]': checked }, + }, + }, + }, + '[Grey Prince Zote]': { + reward: { essence: 300 }, + requires: { + checks: { + dreamNail: { '[Dream Nail]': checked }, + optionalBosses: { '[Zote]': checked }, + equipment: { '[Monarch Wings]': checked }, + }, + }, + }, + '[Lost Kin]': { + reward: { essence: 400 }, + requires: { + checks: { + dreamNail: { '[Dream Nail]': checked }, + bosses: { '[Broken Vessel]': checked }, + }, + }, + }, + '[White Defender]': { + reward: { essence: 300 }, + requires: { + checks: { + dreamNail: { '[Dream Nail]': checked }, + bosses: { '[Dung Defender]': checked }, + }, + }, + }, + '[Soul Tyrant]': { + reward: { essence: 300 }, + requires: { + checks: { + dreamNail: { '[Dream Nail]': checked }, + bosses: { '[Soul Master]': checked }, + }, + }, + }, + }, + + colosseum: { + '[Trial of the Warrior]': { + reward: { percent, geoReq: 100, geo: 1000 }, + requires: { geo: 100 }, + }, + '[Trial of the Conqueror]': { + reward: { percent, geoReq: 450, geo: 2000 }, + requires: { + geo: 450, + checks: { + colosseum: { '[Trial of the Warrior]': checked }, + optionalBosses: { '[Oblobbles]': checked }, + }, + }, + }, + '[Trial of the Fool]': { + reward: { percent, geoReq: 800, geo: 3000 }, + requires: { + geo: 800, + checks: { + colosseum: { '[Trial of the Conqueror]': checked }, + optionalBosses: { '[God Tamer]': checked }, + }, + }, + }, + }, + + godhome: { + '[Godtuner]': { + reward: { percent, simpleKeysReq }, + requires: { simpleKeys }, + }, + '[Pantheon of the Master]': { + reward: { percent }, + requires: { + checks: { + dreamNail: { '[Dream Nail]': checked }, + bosses: { + '[Gruz Mother]': checked, + '[False Knight]': checked, + '[Hornet Protector]': checked, + '[Dung Defender]': checked, + '[Brooding Mawlek]': checked, + }, + dreamWarriors: { '[Gorb]': checked }, + optionalBosses: { + '[Vengefly King]': checked, + '[Massive Moss Charger]': checked, + '[Soul Warrior]': checked, + '[Brothers Oro & Mato]': checked, + }, + }, + }, + }, + '[Pantheon of the Artist]': { + reward: { percent }, + requires: { + checks: { + dreamNail: { '[Dream Nail]': checked }, + bosses: { + '[Soul Master]': checked, + '[Mantis Lords]': checked, + '[Nosk]': checked, + '[Broken Vessel]': checked, + }, + colosseum: { '[Trial of the Conqueror]': checked }, + dreamWarriors: { + '[Xero]': checked, + '[Marmu]': checked, + }, + optionalBosses: { + '[Crystal Guardian]': checked, + '[Oblobbles]': checked, + '[Flukemarm]': checked, + '[Paintmaster Sheo]': checked, + }, + }, + }, + }, + '[Pantheon of the Sage]': { + reward: { percent }, + requires: { + checks: { + dreamNail: { '[Dream Nail]': checked }, + bosses: { + '[Hive Knight]': checked, + '[The Collector]': checked, + '[Grimm]': checked, + '[Uumuu]': checked, + '[Hornet Sentinel]': checked, + }, + dreamWarriors: { + '[Elder Hu]': checked, + '[Galien]': checked, + }, + dreamBosses: { + /* TODO: make optional? // '[Grey Prince Zote]': checked, */ + }, + optionalBosses: { '[Great Nailsage Sly]': checked }, + }, + }, + }, + '[Pantheon of the Knight]': { + reward: { percent }, + requires: { + checks: { + godhome: { + '[Pantheon of the Master]': checked, + '[Pantheon of the Artist]': checked, + '[Pantheon of the Sage]': checked, + }, + bosses: { + '[Traitor Lord]': checked, + '[Watcher Knight]': checked, + }, + dreamWarriors: { + '[No Eyes]': checked, + '[Markoth]': checked, + }, + optionalBosses: { + '[Enraged Guardian]': checked, + '[Pure Vessel]': checked, + }, + }, + }, + }, + '[Pantheon of the Hallownest] (no percent)': { + reward: nothing, + requires: { + checks: { + godhome: { '[Pantheon of the Knight]': checked }, + charms: { '[Kingsoul] / [Void Heart]': checked }, + optionalBosses: { + '[Massive Moss Charger]': checked, + '[Soul Warrior]': checked, + '[Brothers Oro & Mato]': checked, + '[Crystal Guardian]': checked, + '[Oblobbles]': checked, + '[Sisters of Battle]': checked, + '[Flukemarm]': checked, + '[Paintmaster Sheo]': checked, + '[Winged Nosk]': checked, + '[Great Nailsage Sly]': checked, + '[Enraged Guardian]': checked, + '[Pure Vessel]': checked, + }, + bosses: { + '[Gruz Mother]': checked, + '[Hornet Protector]': checked, + '[Dung Defender]': checked, + '[Brooding Mawlek]': checked, + '[Soul Master]': checked, + '[Broken Vessel]': checked, + '[Hive Knight]': checked, + '[The Collector]': checked, + '[Grimm]': checked, + '[Watcher Knight]': checked, + '[Uumuu]': checked, + '[Hornet Sentinel]': checked, + '[Traitor Lord]': checked, + }, + dreamWarriors: { + '[Gorb]': checked, + '[Xero]': checked, + '[Marmu]': checked, + '[Galien]': checked, + '[Elder Hu]': checked, + '[No Eyes]': checked, + '[Markoth]': checked, + }, + dreamBosses: { + /* TODO: make optional? // '[Grey Prince Zote]': checked, */ + }, + }, + }, + }, + }, + + grubs: { + '[Forgotten Crossroads] behind [Husk Guard]': { reward: grubs }, + '[Forgotten Crossroads] [Fog Canyon] entrance': { + reward: grubs, + }, + '[Forgotten Crossroads] breakable wall': { reward: grubs }, + '[Forgotten Crossroads] [Pogo](Nail#Nail-bouncing)': { + reward: grubs, + }, + '[Forgotten Crossroads] on a ledge': { + reward: grubs, + requires: { + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[Greenpath] with a moss block shortcut': { reward: grubs }, + '[Greenpath] near acid': { reward: grubs }, + '[Greenpath] behind [Moss Knight]': { reward: grubs }, + '[Greenpath] in the middle of a [Durandoo] room': { + reward: grubs, + }, + '[Fungal Wastes] behind a line of [Fungling]s': { + reward: grubs, + }, + '[Fungal Wastes] near [Spore Shroom]': { + reward: grubs, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[City of Tears] on a ledge': { reward: grubs }, + '[City of Tears] behind [Great Husk Sentry]': { reward: grubs }, + '[City of Tears] in the [Desolate Dive] dive': { + reward: grubs, + requires: { + checks: { spells: { '[Desolate Dive]': checked } }, + }, + }, + '[City of Tears] under the entrance to the [Tower of Love]': { + reward: grubs, + }, + '[City of Tears] room leading to [Watcher Knight]': { + reward: grubs, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[Crystal Peak] from [Dirtmouth]': { + reward: grubs, + requires: { + checks: { + equipment: { + '[Mantis Claw]': checked, + '[Crystal Heart]': checked, + }, + }, + }, + }, + '[Crystal Peak] behind presses': { + reward: grubs, + requires: { + checks: { + equipment: { + '[Mantis Claw]': checked, + '[Mothwing Cloak]': checked, + }, + }, + }, + }, + '[Crystal Peak] near [Crystal Heart]': { + reward: grubs, + requires: { + checks: { + equipment: { + '[Mantis Claw]': checked, + '[Crystal Heart]': checked, + }, + }, + }, + }, + "[Crystal Peak] on the way to [Hallownest's Crown]": { + reward: grubs, + requires: { + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[Crystal Peak] vertical conveyor belts lever': { + reward: grubs, + requires: { + checks: { + equipment: { + '[Mantis Claw]': checked, + '[Mothwing Cloak]': checked, + }, + }, + }, + }, + '[Crystal Peak] from the top room with presses': { + reward: grubs, + requires: { + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[Crystal Peak] in the [Crystallized Mound]': { + reward: grubs, + requires: { + checks: { + equipment: { + '[Mantis Claw]': checked, + '[Mothwing Cloak]': checked, + }, + spells: { '[Desolate Dive]': checked }, + }, + }, + }, + '[Resting Grounds] [Crypts](Resting Grounds#Crypts)': { + reward: grubs, + requires: { + checks: { spells: { '[Desolate Dive]': checked } }, + }, + }, + '[Royal Waterways] behind a wall near water': { reward: grubs }, + "[Royal Waterways] from the [Kingdom's Edge]": { + reward: grubs, + requires: { + checks: { + equipment: { + '[Crystal Heart]': checked, + '[Monarch Wings]': checked, + }, + items: { '[TRAM_PASS] [Tram Pass]': checked }, + }, + }, + }, + "[Royal Waterways] above [Isma's Tear]": { + reward: grubs, + requires: { + checks: { equipment: { "[Isma's Tear]": checked } }, + }, + }, + '[Howling Cliffs]': { + reward: grubs, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + "[Kingdom's Edge] under [Oro]'s hut": { + reward: grubs, + requires: { + checks: { spells: { '[Desolate Dive]': checked } }, + }, + }, + "[Kingdom's Edge] behind a [Primal Aspid]": { + reward: grubs, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[Fog Canyon]': { + reward: grubs, + requires: { + checks: { equipment: { '[Crystal Heart]': checked } }, + }, + }, + "[Queen's Gardens] under the [Stag] station": { reward: grubs }, + "[Queen's Gardens] above the spiky roof": { + reward: grubs, + requires: { + checks: { + equipment: { + '[Mantis Claw]': checked, + '[Mothwing Cloak]': checked, + '[Crystal Heart]': checked, + }, + }, + }, + }, + "[Queen's Gardens] near [White Lady]": { + reward: grubs, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[Deepnest] among [Grub Mimic]s': { + reward: grubs, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[Deepnest] above the spike pit': { + reward: grubs, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[Deepnest] on the way to [Nosk]': { + reward: grubs, + requires: { + checks: { equipment: { '[Crystal Heart]': checked } }, + }, + }, + "[Deepnest] near the [Weavers' Den]": { + reward: grubs, + requires: { + checks: { + equipment: { + '[Mantis Claw]': checked, + '[Mothwing Cloak]': checked, + }, + }, + }, + }, + "[Deepnest] in the [Beast's Den]": { + reward: grubs, + requires: { + checks: { + equipment: { + '[Mantis Claw]': checked, + '[Mothwing Cloak]': checked, + }, + }, + }, + }, + '[Ancient Basin] above [Broken Vessel]': { + reward: grubs, + requires: { + checks: { + equipment: { + '[Mothwing Cloak]': checked, + '[Monarch Wings]': checked, + }, + }, + }, + }, + '[Ancient Basin] under [Cloth]': { + reward: grubs, + requires: { + checks: { spells: { '[Desolate Dive]': checked } }, + }, + }, + '[The Hive] isolated room': { + reward: grubs, + requires: { + checks: { + equipment: { "[Isma's Tear]": checked }, + spells: { '[Desolate Dive]': checked }, + items: { '[TRAM_PASS] [Tram Pass]': checked }, + }, + }, + }, + '[The Hive]': { + reward: grubs, + requires: { + checks: { + equipment: { + '[Crystal Heart]': checked, + '[Monarch Wings]': checked, + }, + items: { '[TRAM_PASS] [Tram Pass]': checked }, + }, + }, + }, + '[Tower of Love] #1': { + reward: grubs, + requires: { + checks: { bosses: { '[The Collector]': checked } }, + }, + }, + '[Tower of Love] #2': { + reward: grubs, + requires: { + checks: { bosses: { '[The Collector]': checked } }, + }, + }, + '[Tower of Love] #3': { + reward: grubs, + requires: { + checks: { bosses: { '[The Collector]': checked } }, + }, + }, + }, + + items: { + '[SIMPLE_KEY_(HOLLOW_KNIGHT)] [Simple Key] from [Sly]': { + description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 950.', + reward: { simpleKeys, geoReq: 950 }, + requires: { geo: 950 }, + }, + '[SIMPLE_KEY_(HOLLOW_KNIGHT)] [Simple Key] near [City Storerooms]': + { + reward: { simpleKeys }, + requires: { + checks: { equipment: { '[Crystal Heart]': checked } }, + }, + }, + '[SIMPLE_KEY_(HOLLOW_KNIGHT)] [Simple Key] in the [Ancient Basin]': + { + description: + 'In the [Mawlurk] area leading to [Broken Vessel].', + reward: { simpleKeys }, + requires: { + checks: { equipment: { '[Crystal Heart]': checked } }, + }, + }, + '[SIMPLE_KEY_(HOLLOW_KNIGHT)] [Simple Key] behind [Pale Lurker]': { + reward: { simpleKeys }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[ELEGANT_KEY] [Elegant Key]': { + description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 800.', + reward: { geoReq: 800 }, + requires: { + geo: 800, + checks: { + items: { + "[SHOPKEEPER'S_KEY] [Shopkeeper's Key]": checked, + }, + }, + }, + }, + '[LOVE_KEY] [Love Key]': { + reward: nothing, + requires: { + checks: { equipment: { "[Isma's Tear]": checked } }, + }, + }, + "[SHOPKEEPER'S_KEY] [Shopkeeper's Key]": { + reward: nothing, + requires: { + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[TRAM_PASS] [Tram Pass]': { + reward: nothing, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[Lumafly Lantern]': { + description: 'Bought from [Sly] in [Dirtmouth] for [GEO] 1800.', + reward: { geoReq: 1800 }, + requires: { geo: 1800 }, + }, + '[Delicate Flower]': { + reward: nothing, + requires: { + checks: { spells: { '[Desolate Dive]': checked } }, + }, + }, + '[PALE_ORE] [Pale Ore] in [Ancient Basin] below [Cloth]': { + reward: { paleOre: 1 }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[PALE_ORE] [Pale Ore] awarded by the [Seer]': { + reward: { paleOre: 1, essenceReq: [300] }, + requires: { + essence: 300, + checks: { + relics: { + '[HALLOWNEST_SEAL] [Hallownest Seal] awarded by the [Seer]': + checked, + }, + }, + }, + }, + "[PALE_ORE] [Pale Ore] on the [Hallownest's Crown]": { + reward: { paleOre: 1 }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[PALE_ORE] [Pale Ore] behind [Nosk]s lair': { + reward: { paleOre: 1 }, + requires: { checks: { bosses: { '[Nosk]': checked } } }, + }, + '[PALE_ORE] [Pale Ore] awarded by [Grubfather]': { + reward: { paleOre: 1 }, + requires: { grubs: 31 }, + }, + '[PALE_ORE] [Pale Ore] reward in [Trial of the Conqueror]': { + reward: { paleOre: 1 }, + requires: { + checks: { + colosseum: { '[Trial of the Conqueror]': checked }, + }, + }, + }, + '[CHARM_NOTCH] [Charm Notch] from [Salubra] #1': { + reward: nothing, + requires: { charms: 5 }, + }, + '[CHARM_NOTCH] [Charm Notch] from [Salubra] #2': { + reward: nothing, + requires: { charms: 10 }, + }, + '[CHARM_NOTCH] [Charm Notch] from [Salubra] #3': { + reward: nothing, + requires: { charms: 18 }, + }, + '[CHARM_NOTCH] [Charm Notch] from [Salubra] #4': { + reward: nothing, + requires: { charms: 25 }, + }, + '[CHARM_NOTCH] [Charm Notch] in [Fog Canyon]': { + reward: nothing, + requires: { + checks: { equipment: { "[Isma's Tear]": checked } }, + }, + }, + '[CHARM_NOTCH] [Charm Notch] in [Fungal Wastes]': { + reward: nothing, + requires: { + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[CHARM_NOTCH] [Charm Notch] from [Colosseum of Fools]': { + reward: nothing, + requires: { + checks: { + colosseum: { '[Trial of the Warrior]': checked }, + }, + }, + }, + '[CHARM_NOTCH] [Charm Notch] from [Grimm]': { + reward: nothing, + requires: { checks: { bosses: { '[Grimm]': checked } } }, + }, + }, + + relics: { + "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Greenpath] near a [Stag Station]": + { reward: { geo: 200 } }, + "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Greenpath] near [Fog Canyon] entrance": + { reward: { geo: 200 } }, + "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Fungal Wastes] near [Shrumal Ogre]s": + { reward: { geo: 200 } }, + "[WANDERER'S_JOURNAL] [Wanderer's Journal] north of the [Mantis Village]": + { + reward: { geo: 200 }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [City Storerooms]": { + reward: { geo: 200 }, + }, + "[WANDERER'S_JOURNAL] [Wanderer's Journal] north of [King's Station]": + { reward: { geo: 200 } }, + "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Pleasure House]": { + reward: { geo: 200, simpleKeysReq }, + requires: { simpleKeys }, + }, + "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Howling Cliffs]": { + reward: { geo: 200 }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Crystal Peak]": { + reward: { geo: 200 }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Crypts](Resting Grounds#Crypts)": + { reward: { geo: 200 } }, + "[WANDERER'S_JOURNAL] [Wanderer's Journal] in [Royal Waterways]": { + description: + 'Near the spikes in a room which connects to the [Ancient Basin].', + reward: { geo: 200 }, + }, + "[WANDERER'S_JOURNAL] [Wanderer's Journal] near [City of Tears] entrance": + { + reward: { geo: 200 }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + "[WANDERER'S_JOURNAL] [Wanderer's Journal] next to the [Cast-Off Shell] [Bench]": + { + reward: { geo: 200 }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + "[WANDERER'S_JOURNAL] [Wanderer's Journal] near [Markoth]": { + reward: { geo: 200 }, + requires: { + checks: { spells: { '[Desolate Dive]': checked } }, + }, + }, + '[HALLOWNEST_SEAL] [Hallownest Seal] awarded by [Grubfather]': { + reward: { geo: 450 }, + requires: { grubs: 23 }, + }, + '[HALLOWNEST_SEAL] [Hallownest Seal] in the well to [Forgotten Crossroads]': + { + reward: { geo: 450 }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[HALLOWNEST_SEAL] [Hallownest Seal] near [Thorns of Agony]': { + reward: { geo: 450 }, + requires: { + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + "[HALLOWNEST_SEAL] [Hallownest Seal] near [Queen's Station]": { + reward: { geo: 450 }, + }, + '[HALLOWNEST_SEAL] [Hallownest Seal] in [Mantis Village]': { + reward: { geo: 450 }, + requires: { + checks: { bosses: { '[Mantis Lords]': checked } }, + }, + }, + '[HALLOWNEST_SEAL] [Hallownest Seal] at the [Willoh]': { + reward: { geo: 450 }, + requires: { + checks: { equipment: { '[Monarch Wings]': checked } }, + }, + }, + '[HALLOWNEST_SEAL] [Hallownest Seal] near [Overgrown Mound]': { + reward: { geo: 450 }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[HALLOWNEST_SEAL] [Hallownest Seal] in [Forgotten Crossroads] in [Fog Canyon] entrance': + { + reward: { geo: 450 }, + requires: { + checks: { equipment: { "[Isma's Tear]": checked } }, + }, + }, + '[HALLOWNEST_SEAL] [Hallownest Seal] in [Crypts](Resting Grounds#Crypts)': + { + reward: { geo: 450 }, + requires: { + checks: { spells: { '[Desolate Dive]': checked } }, + }, + }, + '[HALLOWNEST_SEAL] [Hallownest Seal] awarded by the [Seer]': { + reward: { geo: 450, essenceReq: [100] }, + requires: { essence: 100 }, + }, + '[HALLOWNEST_SEAL] [Hallownest Seal] near [Relic Seeker Lemm]': { + reward: { geo: 450 }, + }, + "[HALLOWNEST_SEAL] [Hallownest Seal] above [King's Station] [Stag Station]": + { + reward: { geo: 450 }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + '[HALLOWNEST_SEAL] [Hallownest Seal] near [Soul Master]': { + reward: { geo: 450 }, + requires: { + checks: { spells: { '[Desolate Dive]': checked } }, + }, + }, + '[HALLOWNEST_SEAL] [Hallownest Seal] behind [Watcher Knight]': { + reward: { geo: 450 }, + requires: { + checks: { bosses: { '[Watcher Knight]': checked } }, + }, + }, + "[HALLOWNEST_SEAL] [Hallownest Seal] in [Beast's Den]": { + reward: { geo: 450 }, + requires: { + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + '[HALLOWNEST_SEAL] [Hallownest Seal] in [Deepnest] near [Mantis Lords]': + { + reward: { geo: 450 }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + "[HALLOWNEST_SEAL] [Hallownest Seal] in [Queen's Gardens]": { + reward: { geo: 450 }, + requires: { + checks: { equipment: { '[Monarch Wings]': checked } }, + }, + }, + "[KING'S_IDOL] [King's Idol] awarded by [Grubfather]": { + reward: { geo: 800 }, + requires: { grubs: 38 }, + }, + "[KING'S_IDOL] [King's Idol] in [Crystal Peak]": { + reward: { geo: 800 }, + requires: { + checks: { equipment: { '[Monarch Wings]': checked } }, + }, + }, + "[KING'S_IDOL] [King's Idol] in [Spirits' Glade]": { + reward: { geo: 800, essenceReq: [200] }, + requires: { + essence: 200, + checks: { equipment: { '[Mothwing Cloak]': checked } }, + }, + }, + "[KING'S_IDOL] [King's Idol] in [Dung Defender]'s secret room": { + reward: { geo: 800 }, + requires: { + checks: { spells: { '[Desolate Dive]': checked } }, + }, + }, + "[KING'S_IDOL] [King's Idol] in [Howling Cliffs]": { + reward: { geo: 800 }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + "[KING'S_IDOL] [King's Idol] under [Colosseum of Fools]": { + reward: { geo: 800 }, + requires: { + checks: { equipment: { '[Mantis Claw]': checked } }, + }, + }, + "[KING'S_IDOL] [King's Idol] near [Pale Lurker]": { + reward: { geo: 800 }, + requires: { + checks: { + equipment: { + '[Mantis Claw]': checked, + "[Isma's Tear]": checked, + }, + }, + }, + }, + "[KING'S_IDOL] [King's Idol] in [Deepnest] near [Zote]": { + reward: { geo: 800 }, + }, + '[ARCANE_EGG] [Arcane Egg] below [Lifeblood Core]': { + reward: { geo: 1200 }, + requires: { + checks: { + equipment: { + "[King's Brand]": checked, + '[Crystal Heart]': checked, + }, + charms: { "[Joni's Blessing]": checked }, + }, + }, + }, + '[ARCANE_EGG] [Arcane Egg] near [Shade Cloak]': { + reward: { geo: 1200 }, + requires: { + checks: { equipment: { '[Shade Cloak]': checked } }, + }, + }, + '[ARCANE_EGG] [Arcane Egg] in [Birthplace]': { + reward: { geo: 1200 }, + requires: { + checks: { + charms: { '[Kingsoul] / [Void Heart]': checked }, + }, + }, + }, + '[ARCANE_EGG] [Arcane Egg] awarded by the [Seer]': { + reward: { geo: 1200, essenceReq: [1200] }, + requires: { + essence: 1200, + checks: { vesselFragments: { '[Seer]': checked } }, + }, + }, + }, + + whisperingRoots: { + '[Ancestral Mound]': { + reward: { essence: 42 }, + requires: { + checks: { + dreamNail: { '[Dream Nail]': checked }, + spells: { '[Vengeful Spirit]': checked }, + }, + }, + }, + '[City of Tears]': { + reward: { essence: 28 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Crystal Peak]': { + reward: { essence: 21 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Deepnest]': { + reward: { essence: 45 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Forgotten Crossroads]': { + reward: { essence: 29 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Fungal Wastes] (near [Fog Canyon])': { + reward: { essence: 20 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Fungal Wastes] (above [Mantis Village])': { + reward: { essence: 18 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Greenpath]': { + reward: { essence: 44 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[The Hive]': { + reward: { essence: 20 }, + requires: { + checks: { + dreamNail: { '[Dream Nail]': checked }, + items: { '[TRAM_PASS] [Tram Pass]': checked }, + }, + }, + }, + '[Howling Cliffs]': { + reward: { essence: 46 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + "[Kingdom's Edge]": { + reward: { essence: 51 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + "[Queen's Gardens]": { + reward: { essence: 29 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Resting Grounds]': { + reward: { essence: 20 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + '[Royal Waterways]': { + reward: { essence: 35 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + "[Spirits' Glade]": { + reward: { essence: 34 }, + requires: { + checks: { dreamNail: { '[Dream Nail]': checked } }, + }, + }, + }, + }, +}; + +export default INITIAL_HOLLOW_KNIGHT_CHECKLIST_STATE; diff --git a/src/stores/INITIAL_SILKSONG_CHECKLIST_STATE.ts b/src/stores/INITIAL_SILKSONG_CHECKLIST_STATE.ts new file mode 100644 index 0000000..2089124 --- /dev/null +++ b/src/stores/INITIAL_SILKSONG_CHECKLIST_STATE.ts @@ -0,0 +1,1591 @@ +import { SilksongChecklistState } from '../types/checklist'; + +export const nothing = {} as const; +export const checked = { checked: true } as const; +export const grubs = { grubs: 1 } as const; +const maskShards = 1 as const; +const percent = 1 as const; +const simpleKeys = 1 as const; +const spoolFragments = 1 as const; +const tools = 1 as const; +const hearts = { hearts: 1 } as const; +const memoryLockets = 1 as const; +const paleOil = 1 as const; +const craftmetal = 1 as const; +const simpleKeysReq = 1 as const; +const paleOilReq = 1 as const; +const craftmetalReq = 1 as const; + +const REAPER_BASE_SLOTS = 4; +const WANDERER_BASE_SLOTS = 4; +const BEAST_BASE_SLOTS = 3; +const WITCH_CREST_BASE_SLOTS = 3; +const ARCHITECT_CREST_BASE_SLOTS = 3; +const SHAMAN_CREST_BASE_SLOTS = 3; + +const INITIAL_SILKSONG_CHECKLIST_STATE: SilksongChecklistState = { + game: 'silksong', + percent: 0, + + rosaries: 0, + simpleKeys: 0, + memoryLockets: 0, + paleOil: 0, + craftmetal: 0, + + acts: 1, + tools: 0, + fleas: 0, + hearts: 0, + maskShards: 0, + spoolFragments: 0, + + rosariesReq: 0, + simpleKeysReq: 0, + memoryLocketsReq: [0], + paleOilReq: 0, + craftmetalReq: 0, + + checks: { + bosses: { + '[Moss Mother]': { reward: nothing }, + '[Bell Beast]': { reward: nothing }, + '[Fourth Chorus]': { reward: nothing }, + '[Lace]': { reward: nothing }, + '[Sister Splinter]': { reward: nothing }, + '[Savage Beastfly]': { reward: nothing }, + '[Widow]': { reward: nothing }, + '[Last Judge] / [Phantom]': { + reward: { acts: 1 }, + requires: { + checks: { ancestralArts: { '[Needolin]': checked } }, + }, + }, + '[Savage Beastfly 2](Savage Beastfly#Far_Fields)': { + reward: nothing, + requires: { acts: 2 }, + }, + '[Cogwork Dancers]': { reward: nothing, requires: { acts: 2 } }, + '[Trobbio]': { reward: nothing, requires: { acts: 2 } }, + '[The Unravelled]': { reward: nothing, requires: { acts: 2 } }, + '[First Sinner]': { + reward: nothing, + requires: { + checks: { items: { '[Key of Apostate]': checked } }, + acts: 2, + }, + }, + '[Voltvyrm]': { + reward: nothing, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + }, + }, + '[Father of the Flame]': { + reward: nothing, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + items: { '[Faydown Cloak]': checked }, + }, + }, + }, + '[Lace 2](Lace#The_Cradle)': { + reward: nothing, + requires: { + checks: { + melodies: { + "[Conductor's Melody]": checked, + "[Architect's Melody]": checked, + "[Vaultkeeper's Melody]": checked, + }, + }, + acts: 2, + }, + }, + '[Grand Mother Silk]': { + reward: nothing, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + acts: 2, + }, + }, + '[Pinstress]': { + reward: nothing, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + acts: 3, + }, + }, + '[Shrine Guardian Seth]': { + reward: nothing, + requires: { + checks: { ancestralArts: { '[Silk Soar]': checked } }, + acts: 3, + }, + }, + '[Nyleth]': { + reward: nothing, + requires: { + checks: { bosses: { '[Shrine Guardian Seth]': checked } }, + acts: 3, + }, + }, + '[Crust King Khann]': { + reward: nothing, + requires: { + checks: { ancestralArts: { '[Silk Soar]': checked } }, + acts: 3, + }, + }, + '[Skarrsinger Karmelita]': { + reward: nothing, + requires: { + checks: { ancestralArts: { '[Silk Soar]': checked } }, + acts: 3, + }, + }, + '[Clover Dancers]': { + description: + '[Green Prince] must be freed and encountered in [The Citadel].', + reward: { simpleKeysReq }, + requires: { simpleKeys, acts: 3 }, + }, + }, + + melodies: { + "[Conductor's Melody]": { + reward: nothing, + requires: { + checks: { ancestralArts: { '[Clawline]': checked } }, + acts: 2, + }, + }, + "[Architect's Melody]": { + reward: nothing, + requires: { + checks: { ancestralArts: { '[Clawline]': checked } }, + acts: 2, + }, + }, + "[Vaultkeeper's Melody]": { + reward: nothing, + requires: { + checks: { items: { '[Sacred Cylinder]': checked } }, + acts: 2, + }, + }, + }, + + silkHearts: { + '[Bell Beast]': { + reward: { percent }, + requires: { checks: { bosses: { '[Bell Beast]': checked } } }, + }, + '[Lace 2](Lace#The_Cradle)': { + reward: { percent }, + requires: { + checks: { + bosses: { '[Lace 2](Lace#The_Cradle)': checked }, + }, + acts: 2, + }, + }, + '[The Unravelled]': { + reward: { percent }, + requires: { + checks: { bosses: { '[The Unravelled]': checked } }, + acts: 2, + }, + }, + }, + + tools: { + '[Straight Pin]': { reward: { percent, tools } }, + '[Threefold Pin]': { + reward: { percent, tools }, + requires: { + checks: { items: { "[Drifter's Cloak]": checked } }, + }, + }, + '[Sting Shard]': { + reward: { percent, tools, rosariesReq: 140, craftmetalReq }, + requires: { rosaries: 140, craftmetal }, + }, + '[Tacks]': { + reward: { percent, tools }, + requires: { checks: { wishes: { '[Roach Guts]': checked } } }, + }, + '[Longpin]': { + reward: { percent, tools }, + requires: { + checks: { items: { "[Drifter's Cloak]": checked } }, + }, + }, + '[Curveclaw] / [Curvesickle]': { + description: + 'In [Act 2], can be obtained for free after either defeating [Cogwork Dancers], ringing the bell in [Songclave] or acquiring [Clawline].', + reward: { percent, tools, rosariesReq: 140 }, + requires: { rosaries: 140 }, + }, + '[Throwing Ring]': { + reward: { percent, tools }, + requires: { + checks: { wishes: { "[Trail's End]": checked } }, + acts: 2, + }, + }, + '[Pimpillo]': { + reward: { percent, tools, craftmetalReq }, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + craftmetal, + }, + }, + '[Conchcutter]': { + reward: { percent, tools }, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + }, + }, + '[Silkshot]': { + description: + 'Alternatively, buy for [ROSARY] 130 and a [CRAFTMETAL] from [Twelfth Architect] or for [ROSARY] 240 and a [CRAFTMETAL] from [Forge Daughter], but easiest way is to get it for free in [Mount Fay]. Requires [Ruined Tool] found in [Weavenest Murglin] in [Bilewater].', + reward: { percent, tools, craftmetalReq }, + requires: { + craftmetal, + checks: { items: { '[Faydown Cloak]': checked } }, + acts: 2, + }, + }, + "[Delver's Drill]": { + reward: { percent, tools }, + requires: { acts: 2 }, + }, + '[Cogwork Wheel]': { + description: + 'Purchased from [Twelfth Architect] for [ROSARY] 360.', + reward: { percent, tools, craftmetalReq, rosariesReq: 360 }, + requires: { + craftmetal, + rosaries: 360, + checks: { ancestralArts: { '[Clawline]': checked } }, + acts: 2, + }, + }, + '[Cogfly]': { + description: 'Crafted in a room in [High Halls].', + reward: { percent, tools, craftmetalReq }, + requires: { + craftmetal, + checks: { ancestralArts: { '[Clawline]': checked } }, + acts: 2, + }, + }, + '[Rosary Cannon]': { + reward: { percent, tools, simpleKeysReq }, + requires: { + simpleKeys, + checks: { ancestralArts: { '[Clawline]': checked } }, + acts: 2, + }, + }, + '[Voltvessels]': { + reward: { percent, tools }, + requires: { + checks: { items: { '[Faydown Cloak]': checked } }, + acts: 2, + }, + }, + '[Flintslate]': { + reward: { percent, tools, simpleKeysReq }, + requires: { simpleKeys }, + }, + '[Flea Brew]': { + reward: { percent, tools }, + requires: { + checks: { wishes: { '[The Lost Fleas]': checked } }, + }, + }, + '[Plasmium Phial]': { + reward: { percent, tools }, + requires: { + checks: { wishes: { "[Alchemist's Assistant]": checked } }, + }, + }, + "[Druid's Eye] / [Druid's Eyes]": { + reward: { percent, tools }, + requires: { + checks: { wishes: { '[Berry Picking]': checked } }, + }, + }, + '[Magma Bell]': { + reward: { percent, tools, craftmetalReq, rosariesReq: 110 }, + requires: { + craftmetal, + rosaries: 110, + checks: { bosses: { '[Bell Beast]': checked } }, + }, + }, + '[Warding Bell]': { + reward: { percent, tools }, + requires: { checks: { bosses: { '[Bell Beast]': checked } } }, + }, + '[Pollip Pouch]': { + reward: { percent, tools }, + requires: { + checks: { wishes: { '[Rite of the Pollip]': checked } }, + }, + }, + '[Fractured Mask]': { + description: + 'In [Act 2], can be obtained for free after either defeating [Cogwork Dancers], ringing the bell in [Songclave] or acquiring [Clawline].', + reward: { percent, tools, rosariesReq: 260 }, + requires: { rosaries: 260 }, + }, + '[Multibinder]': { + description: + 'Bought from [Frey] in [Bellhart] for [ROSARY] 880.', + reward: { percent, tools, rosariesReq: 880 }, + requires: { + rosaries: 880, + checks: { wishes: { '[My Missing Courier]': checked } }, + }, + }, + '[Weavelight]': { + description: 'Reward for defeating a pair of [Moss Mothers].', + reward: { percent, tools }, + requires: { + checks: { ancestralArts: { '[Needolin]': checked } }, + }, + }, + '[Sawtooth Circlet]': { + description: + 'Purchased from [Twelfth Architect] for [ROSARY] 230.', + reward: { percent, tools, craftmetalReq, rosariesReq: 230 }, + requires: { + craftmetal, + rosaries: 230, + checks: { ancestralArts: { '[Clawline]': checked } }, + acts: 2, + }, + }, + '[Injector Band]': { + reward: { percent, tools }, + requires: { + checks: { items: { '[White Key]': checked } }, + acts: 2, + }, + }, + '[Spool Extender]': { + reward: { percent, tools, rosariesReq: 720 }, + requires: { + rosaries: 720, + checks: { wishes: { '[The Wandering Merchant]': checked } }, + acts: 2, + }, + }, + '[Reserve Bind]': { + reward: { percent, tools }, + requires: { + checks: { wishes: { '[Final Audience]': checked } }, + acts: 2, + }, + }, + '[Claw Mirror] / [Claw Mirrors]': { + reward: { percent, tools }, + requires: { + checks: { bosses: { '[Trobbio]': checked } }, + acts: 2, + }, + }, + '[Memory Crystal]': { + reward: { percent, tools }, + requires: { + checks: { ancestralArts: { '[Clawline]': checked } }, + acts: 2, + }, + }, + '[Snitch Pick]': { + reward: { percent, tools, rosariesReq: 740 }, + requires: { + rosaries: 740, + checks: { items: { '[Faydown Cloak]': checked } }, + }, + }, + '[Volt Filament]': { + reward: { percent, tools }, + requires: { checks: { bosses: { '[Voltvyrm]': checked } } }, + }, + '[Quick Sling]': { + reward: { percent, tools }, + requires: { + checks: { items: { '[Faydown Cloak]': checked } }, + acts: 2, + }, + }, + '[Wreath of Purity]': { + reward: { percent, tools }, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + items: { '[Faydown Cloak]': checked }, + }, + acts: 2, + }, + }, + '[Longclaw]': { + reward: { percent, tools }, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + items: { '[Faydown Cloak]': checked }, + wishes: { '[Broodfeast] / [Runtfeast]': checked }, + }, + acts: 2, + }, + }, + '[Wispfire Lantern]': { + reward: { percent, tools }, + requires: { + checks: { + bosses: { '[Father of the Flame]': checked }, + ancestralArts: { '[Cling Grip]': checked }, + items: { '[Faydown Cloak]': checked }, + }, + }, + }, + '[Egg of Flealia]': { + reward: { percent, tools }, + requires: { fleas: 30 }, + }, + '[Pin Badge]': { + reward: { percent, tools }, + requires: { + checks: { bosses: { '[Pinstress]': checked } }, + acts: 3, + }, + }, + '[Compass]': { + reward: { percent, tools, rosariesReq: 70 }, + requires: { rosaries: 70 }, + }, + '[Shard Pendant]': { + reward: { percent, tools }, + requires: { checks: { bosses: { '[Bell Beast]': checked } } }, + }, + '[Magnetite Brooch]': { + description: + 'Bought from [Pebb] in [Bone Bottom] for [ROSARY] 120 in [Acts] 1/2 or from [Grindle] in [Blasted Steps] for [ROSARY] 220 in [Act 3], also requiring [Faydown Cloak].', + reward: { percent, tools, rosariesReq: 120 }, + requires: { rosaries: 120 }, + }, + '[Weighted Belt]': { + description: + 'Bought from [Mort] in [Far Fields] for [ROSARY] 160 in [Acts] 1/2.', + reward: { percent, tools }, + }, + '[Barbed Bracelet]': { + reward: { percent, tools }, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + items: { "[Drifter's Cloak]": checked }, + }, + }, + }, + "[Dead Bug's Purse] / [Shell Satchel]": { + description: + 'Found in [Wormways] from [Shellwood] or behind a door requiring a [Simple Key].', + reward: { percent, tools }, + }, + '[Magnetite Dice]': { + description: + "In [Act 1], given by [Lumble the Lucky] after beating his game 10 times. In [Act 2], found beside [Lumble the Lucky]'s corpse. In [Act 3], purchased from [Grindle] for [ROSARY] 300.", + reward: { percent, tools }, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + }, + }, + }, + '[Scuttlebrace]': { + description: + 'Purchased from [Twelfth Architect] for [ROSARY] 140.', + reward: { percent, tools, craftmetalReq, rosariesReq: 140 }, + requires: { + craftmetal, + rosaries: 140, + checks: { ancestralArts: { '[Clawline]': checked } }, + acts: 2, + }, + }, + "[Ascendant's Grip]": { + description: + 'Bought from [Jubilana] in [Songclave] for [ROSARY] 350.', + reward: { percent, tools, rosariesReq: 350 }, + requires: { + rosaries: 350, + checks: { wishes: { '[The Lost Merchant]': checked } }, + acts: 2, + }, + }, + '[Spider Strings]': { + description: + 'Bought from [Jubilana] in [Songclave] for [ROSARY] 320.', + reward: { percent, tools, rosariesReq: 320 }, + requires: { + rosaries: 320, + checks: { wishes: { '[The Lost Merchant]': checked } }, + acts: 2, + }, + }, + '[Silkspeed Anklets]': { + reward: { percent, tools }, + requires: { + checks: { + bosses: { '[Lace]': checked }, + ancestralArts: { '[Swift Step]': checked }, + }, + }, + }, + "[Thief's Mark]": { + reward: { percent, tools, rosariesReq: 350 }, + requires: { + rosaries: 350, + checks: { items: { '[Faydown Cloak]': checked } }, + }, + }, + }, + + silkSkills: { + '[Silkspear]': { + reward: { percent }, + requires: { checks: { bosses: { '[Moss Mother]': checked } } }, + }, + '[Thread Storm]': { + reward: { percent }, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + items: { "[Drifter's Cloak]": checked }, + }, + }, + }, + '[Cross Stitch]': { + description: 'Of the two, requires [Phantom] to be defeated.', + reward: { percent }, + requires: { + checks: { + bosses: { '[Last Judge] / [Phantom]': checked }, + ancestralArts: { '[Cling Grip]': checked }, + }, + }, + }, + '[Sharpdart]': { + reward: { percent }, + requires: { + checks: { + ancestralArts: { '[Needolin]': checked }, + items: { '[Faydown Cloak]': checked }, + }, + acts: 2, + }, + }, + '[Rune Rage]': { + reward: { percent }, + requires: { + checks: { bosses: { '[First Sinner]': checked } }, + acts: 2, + }, + }, + '[Pale Nails]': { + reward: { percent }, + requires: { + checks: { + bosses: { '[Grand Mother Silk]': checked }, + ancestralArts: { '[Silk Soar]': checked }, + }, + acts: 3, + }, + }, + }, + + ancestralArts: { + '[Swift Step]': { + reward: { percent }, + requires: { checks: { bosses: { '[Moss Mother]': checked } } }, + }, + '[Cling Grip]': { + reward: { percent }, + requires: { + checks: { + ancestralArts: { '[Swift Step]': checked }, + bosses: { '[Sister Splinter]': checked }, + }, + }, + }, + '[Needolin]': { + reward: { percent }, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + bosses: { '[Widow]': checked }, + }, + }, + }, + '[Clawline]': { + description: + 'Alternatively, there is a path in [Whiteward]. At the end of the day, both are required.', + reward: { percent }, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + bosses: { '[Trobbio]': checked }, + }, + acts: 2, + }, + }, + '[Silk Soar]': { + reward: { percent }, + requires: { + checks: { + ancestralArts: { + '[Swift Step]': checked, + '[Cling Grip]': checked, + }, + }, + acts: 3, + }, + }, + '[Needle Strike]': { + reward: { percent }, + requires: { + checks: { + ancestralArts: { + '[Swift Step]': checked, + '[Cling Grip]': checked, + }, + }, + }, + }, + '[Sylphsong]': { + reward: { percent, memoryLocketsReq: [32] }, + requires: { + memoryLockets: 32, + checks: { + eva: { 'Further evolved [Hunter Crest]': checked }, + }, + }, + }, + }, + + crests: { + '[Reaper Crest]': { + reward: { percent, memoryLockets: REAPER_BASE_SLOTS }, + requires: { + checks: { items: { "[Drifter's Cloak]": checked } }, + }, + }, + '[Wanderer Crest]': { + description: 'Alternatively, there is a path in [Wormways].', + reward: { percent, memoryLockets: WANDERER_BASE_SLOTS }, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + }, + }, + '[Beast Crest]': { + reward: { percent, memoryLockets: BEAST_BASE_SLOTS }, + requires: { + checks: { + bosses: { '[Savage Beastfly]': checked }, + items: { "[Drifter's Cloak]": checked }, + }, + }, + }, + '[Witch Crest]': { + reward: { percent, memoryLockets: WITCH_CREST_BASE_SLOTS }, + requires: { + checks: { wishes: { '[Infestation Operation]': checked } }, + }, + }, + '[Architect Crest]': { + reward: { percent, memoryLockets: ARCHITECT_CREST_BASE_SLOTS }, + requires: { checks: { items: { '[Architect Key]': checked } } }, + }, + '[Shaman Crest]': { + reward: { percent, memoryLockets: SHAMAN_CREST_BASE_SLOTS }, + requires: { + checks: { ancestralArts: { '[Silk Soar]': checked } }, + acts: 3, + }, + }, + }, + + eva: { + 'Evolved [Hunter Crest]': { + reward: nothing, + requires: { + checks: { ancestralArts: { '[Needolin]': checked } }, + }, + }, + '[Vesticrest] yellow slot': { + reward: { memoryLocketsReq: [12] }, + requires: { + checks: { eva: { 'Evolved [Hunter Crest]': checked } }, + memoryLockets: 12, + }, + }, + '[Vesticrest] blue slot': { + reward: { memoryLocketsReq: [20] }, + requires: { + checks: { eva: { '[Vesticrest] yellow slot': checked } }, + memoryLockets: 20, + }, + }, + 'Further evolved [Hunter Crest]': { + reward: { memoryLocketsReq: [27] }, + requires: { + checks: { eva: { '[Vesticrest] blue slot': checked } }, + memoryLockets: 27, + }, + }, + }, + + maskShards: { + '[Pebb] from [Bone Bottom] for [ROSARY] 300': { + description: + 'Bought from [Pebb] in [Bone Bottom] for [ROSARY] 300 in [Acts] 1/2 or from [Grindle] in [Blasted Steps] for [ROSARY] 320 in [Act 3], also requiring [Faydown Cloak].', + reward: { maskShards, rosariesReq: 300 }, + requires: { + rosaries: 300, + checks: { bosses: { '[Moss Mother]': checked } }, + }, + }, + '[Wormways]': { + description: + 'Breakable wall just before the door requiring [Simple Key], after a room with [Craggler], accessed from [Mosshome].', + reward: { maskShards }, + }, + '[Deep Docks] entrance': { + description: 'A top passageway accessed from [The Marrow].', + reward: { maskShards }, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + }, + }, + '[Far Fields] [Seamstress]': { + reward: { maskShards }, + requires: { + checks: { items: { "[Drifter's Cloak]": checked } }, + }, + }, + '[Shellwood]': { reward: { maskShards } }, + '[Weavenest Alta]': { + description: 'Platforming challenge to the right of [Eva].', + reward: { maskShards }, + requires: { + checks: { + ancestralArts: { + '[Needolin]': checked, + '[Cling Grip]': checked, + }, + }, + }, + }, + '[Jubilana] from [Songclave] for [ROSARY] 750': { + reward: { maskShards, rosariesReq: 750 }, + requires: { + rosaries: 750, + acts: 2, + }, + }, + 'West [Cogwork Core]': { + reward: { maskShards }, + requires: { + checks: { bosses: { '[Cogwork Dancers]': checked } }, + acts: 2, + }, + }, + '[Whispering Vaults]': { + reward: { maskShards }, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + acts: 2, + }, + }, + '[Savage Beastfly] [Wish]': { + reward: { maskShards }, + requires: { + checks: { + wishes: { + '[Savage Beastfly](Wishes#Grand_Hunt_Wishes)': + checked, + }, + }, + acts: 2, + }, + }, + '[Far Fields] rising lava escape sequence': { + reward: { maskShards }, + requires: { + checks: { + ancestralArts: { '[Clawline]': checked }, + items: { "[Drifter's Cloak]": checked }, + }, + acts: 2, + }, + }, + 'West [Mount Fay]': { + reward: { maskShards }, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + items: { '[Faydown Cloak]': checked }, + }, + acts: 2, + }, + }, + '[Slab]': { + description: + 'Northeast part of [The Slab], inaccessible when first captured.', + reward: { maskShards }, + requires: { + checks: { + items: { + '[Faydown Cloak]': checked, + '[Key of Apostate]': checked, + }, + }, + acts: 2, + }, + }, + '[Bilewater]': { + description: + 'At the end of a hallway filled with [Slubberlugs].', + reward: { maskShards }, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + items: { '[Faydown Cloak]': checked }, + }, + acts: 2, + }, + }, + '[Wisp Thicket]': { + reward: { maskShards }, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + items: { '[Faydown Cloak]': checked }, + }, + acts: 2, + }, + }, + '[Blasted Steps]': { + description: 'Above the entrance to [Pharloom].', + reward: { maskShards }, + requires: { + checks: { + ancestralArts: { '[Clawline]': checked }, + items: { '[Faydown Cloak]': checked }, + }, + acts: 2, + }, + }, + '[Mount Fay] [Brightvein]': { + reward: { maskShards }, + requires: { + checks: { + ancestralArts: { + '[Clawline]': checked, + '[Silk Soar]': checked, + }, + }, + acts: 3, + }, + }, + '[Fastest in Pharloom] [Wish]': { + reward: { maskShards }, + requires: { + checks: { + wishes: { + '[Fastest in Pharloom]': checked, + }, + }, + acts: 3, + }, + }, + '[Dark Hearts] [Wish]': { + reward: { maskShards }, + requires: { + checks: { wishes: { '[Dark Hearts]': checked } }, + acts: 3, + }, + }, + '[The Hidden Hunter] [Wish]': { + reward: { maskShards }, + requires: { + checks: { wishes: { '[The Hidden Hunter]': checked } }, + acts: 3, + }, + }, + }, + + needle: { + '[Sharpened Needle](Needle#Upgrades)': { + reward: { percent }, + requires: { checks: { bosses: { '[Widow]': checked } } }, + }, + '[Shining Needle](Needle#Upgrades)': { + reward: { percent, paleOilReq }, + requires: { + paleOil, + checks: { + needle: { + '[Sharpened Needle](Needle#Upgrades)': checked, + }, + }, + }, + }, + '[Hivesteel Needle](Needle#Upgrades)': { + reward: { percent, paleOilReq, rosariesReq: 450 }, + requires: { + paleOil, + rosaries: 450, + checks: { + needle: { + '[Shining Needle](Needle#Upgrades)': checked, + }, + }, + }, + }, + '[Palesteel Needle](Needle#Upgrades)': { + reward: { percent, paleOilReq, rosariesReq: 680 }, + requires: { + paleOil, + rosaries: 680, + checks: { + needle: { + '[Hivesteel Needle](Needle#Upgrades)': checked, + }, + }, + acts: 3, + }, + }, + }, + + spoolFragments: { + '[Bone Bottom]': { + description: 'In a shortcut below [Mosshome].', + reward: { spoolFragments }, + }, + '[Deep Docks] hot floor': { + reward: { spoolFragments }, + }, + '[Weavenest Alta]': { + reward: { spoolFragments }, + requires: { + checks: { + ancestralArts: { + '[Needolin]': checked, + '[Cling Grip]': checked, + }, + }, + }, + }, + '[Frey] from [Bellhart] for [ROSARY] 270': { + reward: { spoolFragments, rosariesReq: 270 }, + requires: { + rosaries: 270, + checks: { wishes: { '[My Missing Courier]': checked } }, + }, + }, + '[Greymoor]': { + reward: { spoolFragments }, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + }, + }, + '[Slab]': { + reward: { spoolFragments }, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + }, + }, + '[Grand Gate]': { + reward: { spoolFragments }, + requires: { acts: 2 }, + }, + '[Underworks]': { + reward: { spoolFragments }, + requires: { acts: 2 }, + }, + 'From [Mooshka] at [Grand Gate]': { + description: + '[Last Judge] cannot be skipped by finding 10 [Fleas] and travelling with the caravan.', + reward: { spoolFragments }, + requires: { + fleas: 12, + checks: { bosses: { '[Last Judge] / [Phantom]': checked } }, + acts: 2, + }, + }, + '[Whiteward]': { + description: 'Under the elevator.', + reward: { spoolFragments }, + requires: { + checks: { items: { '[White Key]': checked } }, + acts: 2, + }, + }, + '[Cogwork Core]': { + reward: { spoolFragments }, + requires: { + checks: { bosses: { '[Cogwork Dancers]': checked } }, + acts: 2, + }, + }, + '[Underworks] near [The Cauldron]': { + reward: { spoolFragments }, + requires: { acts: 2 }, + }, + '[Balm for the Wounded] [Wish]': { + reward: { spoolFragments }, + requires: { + checks: { wishes: { '[Balm for the Wounded]': checked } }, + acts: 2, + }, + }, + '[Jubilana] from [Songclave] for [ROSARY] 500': { + reward: { spoolFragments, rosariesReq: 500 }, + requires: { + rosaries: 500, + checks: { wishes: { '[The Lost Merchant]': checked } }, + acts: 2, + }, + }, + '[Deep Docks] behind [Simple Key]': { + reward: { spoolFragments, simpleKeysReq }, + requires: { + simpleKeys, + checks: { + ancestralArts: { + '[Clawline]': checked, + '[Cling Grip]': checked, + }, + }, + acts: 2, + }, + }, + '[High Halls]': { + reward: { spoolFragments }, + requires: { + checks: { + ancestralArts: { '[Clawline]': checked }, + items: { '[Faydown Cloak]': checked }, + }, + acts: 2, + }, + }, + '[Memorium]': { + reward: { spoolFragments }, + requires: { + checks: { + ancestralArts: { '[Clawline]': checked }, + items: { '[Faydown Cloak]': checked }, + }, + acts: 2, + }, + }, + '[Grindle] for [ROSARY] 680': { + reward: { spoolFragments, rosariesReq: 680 }, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + items: { '[Faydown Cloak]': checked }, + }, + rosaries: 680, + acts: 2, + }, + }, + }, + + toolPouch: { + "[Mort] from [Pilgrim's Rest] for [ROSARY] 220": { + description: + 'Bought from [Mort] in [Far Fields] for [ROSARY] 220 in [Acts] 1/2 or from [Grindle] in [Blasted Steps] for [ROSARY] 220 in [Act 3], also requiring [Faydown Cloak].', + reward: { percent, rosariesReq: 220 }, + requires: { + rosaries: 220, + checks: { items: { "[Drifter's Cloak]": checked } }, + }, + }, + "[Loddie]'s pin challenge": { + reward: { percent }, + requires: { checks: { bosses: { '[Widow]': checked } } }, + }, + "[Nuu]'s wish": { + reward: { percent }, + requires: { + checks: { wishes: { '[Bugs of Pharloom]': checked } }, + }, + }, + 'From [Mooshka] in [Fleatopia]': { + reward: { percent }, + requires: { fleas: 22, acts: 2 }, + }, + '[Forge Daughter] for [ROSARY] 180': { + reward: { percent, rosariesReq: 180 }, + requires: { rosaries: 180 }, + }, + '[Crawbug Clearing] [Wish]': { + reward: { percent }, + requires: { + checks: { wishes: { '[Crawbug Clearing]': checked } }, + }, + }, + '[Twelfth Architect] for [ROSARY] 450': { + reward: { percent, rosariesReq: 450 }, + requires: { + rosaries: 450, + checks: { ancestralArts: { '[Clawline]': checked } }, + acts: 2, + }, + }, + '[Grindle] for [ROSARY] 700': { + reward: { percent, rosariesReq: 700 }, + requires: { + rosaries: 700, + checks: { items: { '[Faydown Cloak]': checked } }, + acts: 2, + }, + }, + }, + + items: { + "[Drifter's Cloak]": { + reward: nothing, + requires: { + checks: { wishes: { '[Flexile Spines]': checked } }, + }, + }, + '[Faydown Cloak]': { + reward: nothing, + requires: { + checks: { + ancestralArts: { + '[Cling Grip]': checked, + '[Clawline]': checked, + }, + }, + acts: 2, + }, + }, + '[White Key]': { + description: + 'If [The Wandering Merchant] wish has already been granted, it can be bought from [Jubilana] in [Songclave] for [ROSARY] 220.', + reward: nothing, + }, + '[Key of Apostate]': { reward: nothing }, + '[Sacred Cylinder]': { + reward: nothing, + requires: { + checks: { + bosses: { '[Trobbio]': checked }, + ancestralArts: { '[Cling Grip]': checked }, + }, + }, + }, + '[Twisted Bud]': { reward: nothing, requires: { acts: 2 } }, + '[Steel Spines]': { + reward: { rosariesReq: 160 }, + requires: { + rosaries: 160, + checks: { ancestralArts: { '[Cling Grip]': checked } }, + }, + }, + '[Architect Key]': { reward: nothing, requires: { tools: 25 } }, + '[CRAFTMETAL] [Craftmetal] from [Pebb] in [Bone Bottom] for [ROSARY] 60': + { + reward: { craftmetal, rosariesReq: 60 }, + requires: { + rosaries: 60, + checks: { bosses: { '[Moss Mother]': checked } }, + }, + }, + "[CRAFTMETAL] [Craftmetal] in [The Marrow] near [Hunter's March]": { + reward: { craftmetal }, + }, + '[CRAFTMETAL] [Craftmetal] in [Deep Docks] near [Far Fields]': { + reward: { craftmetal }, + }, + '[CRAFTMETAL] [Craftmetal] in [Blasted Steps] near [Last Judge]': { + reward: { craftmetal }, + }, + '[CRAFTMETAL] [Craftmetal] from [Jubilana] for [ROSARY] 180': { + reward: { craftmetal, rosariesReq: 180 }, + requires: { + rosaries: 180, + checks: { wishes: { '[The Lost Merchant]': checked } }, + acts: 2, + }, + }, + '[CRAFTMETAL] [Craftmetal] in [Wisp Thicket]': { + reward: { craftmetal }, + requires: { + checks: { items: { '[Faydown Cloak]': checked } }, + acts: 2, + }, + }, + '[CRAFTMETAL] [Craftmetal] in [Underworks] south of [The Cauldron]': + { + reward: { craftmetal }, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + acts: 2, + }, + }, + '[CRAFTMETAL] [Craftmetal] in [Pale Lake]': { + reward: { craftmetal }, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + acts: 2, + }, + }, + '[MEMORY_LOCKET] [Memory Locket] for [Volatile Flintbeetles] [Wish]': + { + reward: { memoryLockets }, + requires: { + checks: { + wishes: { '[Volatile Flintbeetles]': checked }, + }, + }, + }, + '[MEMORY_LOCKET] [Memory Locket] in [The Marrow]': { + reward: { memoryLockets }, + }, + "[MEMORY_LOCKET] [Memory Locket] in [Hunter's March]": { + reward: { memoryLockets }, + }, + '[MEMORY_LOCKET] [Memory Locket] in [Deep Docks] behind [Simple Key]': + { + reward: { memoryLockets, simpleKeysReq }, + requires: { + simpleKeys, + checks: { ancestralArts: { '[Clawline]': checked } }, + }, + }, + '[MEMORY_LOCKET] [Memory Locket] from [Mort] in [Far Fields] for [ROSARY] 150': + { + description: + 'Bought from [Mort] in [Far Fields] for [ROSARY] 150 in [Acts] 1/2 or from [Grindle] in [Blasted Steps] for [ROSARY] 250 in [Act 3], also requiring [Faydown Cloak].', + reward: { memoryLockets, rosariesReq: 150 }, + requires: { rosaries: 150 }, + }, + '[MEMORY_LOCKET] [Memory Locket] in [Far Fields] near [Skarrsinger Karmelita]': + { + reward: { memoryLockets }, + requires: { + checks: { ancestralArts: { '[Silk Soar]': checked } }, + acts: 3, + }, + }, + '[MEMORY_LOCKET] [Memory Locket] in [Greymoor] near [Bellway]': { + reward: { memoryLockets }, + }, + '[MEMORY_LOCKET] [Memory Locket] in [Greymoor] inside [Halfway Home]': + { + reward: { memoryLockets }, + requires: { + checks: { items: { '[Faydown Cloak]': checked } }, + }, + }, + '[MEMORY_LOCKET] [Memory Locket] from [Frey] in [Bellhart] for [ROSARY] 330': + { + reward: { memoryLockets, rosariesReq: 330 }, + requires: { + rosaries: 330, + checks: { + bosses: { '[Widow]': checked }, + }, + }, + }, + "[MEMORY_LOCKET] [Memory Locket] in [Bellhart]'s ceiling": { + reward: { memoryLockets }, + requires: { + checks: { ancestralArts: { '[Silk Soar]': checked } }, + acts: 3, + }, + }, + '[MEMORY_LOCKET] [Memory Locket] in [Blasted Steps]': { + reward: { memoryLockets }, + }, + '[MEMORY_LOCKET] [Memory Locket] in the [Sands of Karak]': { + reward: { memoryLockets }, + }, + '[MEMORY_LOCKET] [Memory Locket] in [Wormways]': { + reward: { memoryLockets, simpleKeysReq }, + requires: { simpleKeys }, + }, + '[MEMORY_LOCKET] [Memory Locket] in the [Underworks]': { + reward: { memoryLockets }, + requires: { acts: 2 }, + }, + '[MEMORY_LOCKET] [Memory Locket] at [Grand Bellway]': { + reward: { memoryLockets }, + requires: { acts: 2 }, + }, + '[MEMORY_LOCKET] [Memory Locket] in [Memorium]': { + reward: { memoryLockets }, + requires: { + checks: { items: { '[Faydown Cloak]': checked } }, + }, + }, + '[MEMORY_LOCKET] [Memory Locket] in [The Slab]': { + reward: { memoryLockets }, + requires: { + checks: { items: { '[Faydown Cloak]': checked } }, + }, + }, + '[MEMORY_LOCKET] [Memory Locket] in [Whispering Vaults]': { + reward: { memoryLockets }, + requires: { acts: 2 }, + }, + '[MEMORY_LOCKET] [Memory Locket] in [Bilewater] secret room': { + reward: { memoryLockets }, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + items: { '[Faydown Cloak]': checked }, + }, + }, + }, + '[MEMORY_LOCKET] [Memory Locket] in [Bilewater] near the bench shortcut': + { + reward: { memoryLockets }, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + items: { '[Faydown Cloak]': checked }, + }, + }, + }, + }, + + everbloom: { + '[Pollen Heart]': { + reward: hearts, + requires: { + checks: { bosses: { '[Nyleth]': checked } }, + acts: 3, + }, + }, + '[Encrusted Heart]': { + reward: hearts, + requires: { + checks: { bosses: { '[Crust King Khann]': checked } }, + acts: 3, + }, + }, + "[Hunter's Heart]": { + reward: hearts, + requires: { + checks: { bosses: { '[Skarrsinger Karmelita]': checked } }, + acts: 3, + }, + }, + '[Conjoined Heart]': { + reward: hearts, + requires: { + checks: { bosses: { '[Clover Dancers]': checked } }, + acts: 3, + }, + }, + '[Everbloom]': { reward: { percent }, requires: { hearts: 3 } }, + }, + + wishes: { + '[Berry Picking]': { reward: nothing }, + '[The Lost Fleas]': { reward: nothing, requires: { fleas: 5 } }, + '[Flexile Spines]': { + reward: nothing, + requires: { + checks: { + ancestralArts: { '[Swift Step]': checked }, + bosses: { '[Lace]': checked }, + }, + }, + }, + '[Rite of the Pollip]': { + reward: nothing, + requires: { + checks: { items: { "[Drifter's Cloak]": checked } }, + }, + }, + '[My Missing Courier]': { + reward: nothing, + requires: { checks: { bosses: { '[Widow]': checked } } }, + }, + '[Volatile Flintbeetles]': { + description: + 'The actual requirement is discovering [Shellwood] after [Greymoor].', + reward: nothing, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + }, + }, + '[Bugs of Pharloom]': { + reward: nothing, + requires: { + checks: { items: { "[Drifter's Cloak]": checked } }, + }, + }, + '[The Threadspun Town]': { + reward: nothing, + requires: { checks: { bosses: { '[Widow]': checked } } }, + }, + '[Crawbug Clearing]': { + reward: nothing, + requires: { + checks: { wishes: { '[The Threadspun Town]': checked } }, + }, + }, + "[Alchemist's Assistant]": { + reward: nothing, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + }, + }, + '[Roach Guts]': { + reward: nothing, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + }, + }, + '[The Wandering Merchant]': { + reward: nothing, + requires: { acts: 2 }, + }, + '[Savage Beastfly](Wishes#Grand_Hunt_Wishes)': { + reward: nothing, + requires: { + checks: { + bosses: { + '[Fourth Chorus]': checked, + '[Savage Beastfly]': checked, + '[Savage Beastfly 2](Savage Beastfly#Far_Fields)': + checked, + }, + }, + acts: 2, + }, + }, + '[Fine Pins]': { reward: nothing, requires: { acts: 2 } }, + '[Balm for the Wounded]': { + reward: nothing, + requires: { + checks: { items: { '[White Key]': checked } }, + acts: 2, + }, + }, + '[Building Up Songclave]': { + reward: { rosariesReq: 300 }, + requires: { rosaries: 300, acts: 2 }, + }, + '[Cloaks of the Choir]': { + reward: nothing, + requires: { + checks: { wishes: { '[Building Up Songclave]': checked } }, + acts: 2, + }, + }, + '[Strengthening Songclave]': { + reward: { rosariesReq: 500 }, + requires: { + rosaries: 500, + checks: { + wishes: { + '[Fine Pins]': checked, + '[Cloaks of the Choir]': checked, + '[Balm for the Wounded]': checked, + '[Building Up Songclave]': checked, + }, + }, + acts: 2, + }, + }, + '[The Lost Merchant]': { + reward: nothing, + requires: { + checks: { + items: { '[Faydown Cloak]': checked }, + wishes: { + '[The Wandering Merchant]': checked, + '[Strengthening Songclave]': checked, + }, + }, + acts: 2, + }, + }, + '[Broodfeast] / [Runtfeast]': { + reward: nothing, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + items: { '[Faydown Cloak]': checked }, + }, + }, + }, + '[Rite of Rebirth]': { + reward: nothing, + requires: { checks: { items: { '[Twisted Bud]': checked } } }, + }, + '[Infestation Operation]': { + reward: nothing, + requires: { + checks: { + items: { '[Steel Spines]': checked }, + wishes: { '[Rite of Rebirth]': checked }, + }, + acts: 2, + }, + }, + "[Trail's End]": { + description: + 'Requires 2 parts of the [Threefold Melody] and all maps from [Shakra].', + reward: nothing, + requires: { + checks: { + ancestralArts: { '[Cling Grip]': checked }, + items: { '[Faydown Cloak]': checked }, + }, + acts: 2, + }, + }, + '[Fastest in Pharloom]': { + reward: nothing, + requires: { + checks: { ancestralArts: { '[Silk Soar]': checked } }, + acts: 3, + }, + }, + '[Final Audience]': { + description: 'Collect all 3 [Cogheart] pieces.', + reward: nothing, + requires: { + checks: { ancestralArts: { '[Cling Grip]': checked } }, + acts: 2, + }, + }, + '[Dark Hearts]': { reward: nothing, requires: { acts: 3 } }, + '[The Hidden Hunter]': { reward: nothing, requires: { acts: 3 } }, + }, + + relics: {}, + + fleas: {}, + }, +}; + +export default INITIAL_SILKSONG_CHECKLIST_STATE; diff --git a/src/stores/checklistStore.ts b/src/stores/checklistStore.ts index e480bf4..e4d2f5d 100644 --- a/src/stores/checklistStore.ts +++ b/src/stores/checklistStore.ts @@ -1,3 +1,4 @@ +import { Draft } from 'immer'; import { merge as deepMerge } from 'object-deep-merge'; import { PartialDeep } from 'type-fest'; import { temporal } from 'zundo'; @@ -22,6 +23,7 @@ import { SaveFileData, SectionNames, } from '../types/checklist'; +import { ExtractNumberKeys } from '../types/util'; import partialDeepEqual, { Comparable } from '../util/partialDeepEqual'; import { typedEntries, typedKeys, typedValues } from '../util/typedObject'; import INITIAL_CHECKLIST_STATE from './INITIAL_CHECKLIST_STATE'; @@ -117,8 +119,10 @@ const validateCheck = ( const hkRequires = check.requires as PartialDeep; - /// special case for "consumable items", we don't want to just check if the - /// value is greater, we wanna know that we have enough of it + // TODO: silksong + + // special case for "consumable items", we don't want to just check if the + // value is greater, we wanna know that we have enough of it const reqs: Record< 'paleOre' | 'geo' | 'simpleKeys', [number, number, number] @@ -193,7 +197,7 @@ const validateChecks = ( }; const applyReward = ( - state: State, + state: Draft>, reward: CheckRewards, willCheck: boolean ) => { @@ -204,44 +208,37 @@ const applyReward = ( } }; -const grubRewards = (state: State<'hollow-knight'>, willCheck: boolean) => { - const grubs = state.grubs; - - const grubReward = willCheck - ? GRUB_REWARDS[grubs - 1] - : GRUB_REWARDS[grubs]; - - applyReward(state, { geo: grubReward }, willCheck); -}; - -const maskShardRewards = ( - state: State<'hollow-knight'>, +const MASK_SHARD_REWARDS = [ + 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, +] as const; +const VESSEL_FRAGMENT_REWARDS = [0, 0, 1, 0, 0, 1, 0, 0, 1] as const; +const SILK_SPOOL_PART_REWARDS = [ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, +] as const; + +const applyPartialReward = < + Game extends GameKey, + Rewards extends readonly number[] +>( + state: Draft>, + key: ExtractNumberKeys>, + rewards: Rewards, + rewardKey: ExtractNumberKeys>, willCheck: boolean ) => { - const maskShards = state.maskShards; + const part = (state as State)[key] as number; - const percent = willCheck - ? [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1][maskShards - 1] - : [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1][maskShards]; + const reward = willCheck ? rewards[part - 1] : rewards[part]; - applyReward(state, { percent }, willCheck); -}; - -const vesselFragmentRewards = ( - state: State<'hollow-knight'>, - willCheck: boolean -) => { - const vesselFragments = state.vesselFragments; - - const percent = willCheck - ? [0, 0, 1, 0, 0, 1, 0, 0, 1][vesselFragments - 1] - : [0, 0, 1, 0, 0, 1, 0, 0, 1][vesselFragments]; - - applyReward(state, { percent }, willCheck); + applyReward( + state, + { [rewardKey]: reward } as CheckRewards, + willCheck + ); }; const handleCheck = ( - state: State, + state: Draft>, sectionName: SectionNames, check: Check, willCheck: boolean @@ -252,16 +249,47 @@ const handleCheck = ( applyReward(state, check.reward, willCheck); check.checked = willCheck; + if (sectionName === 'maskShards') { + // TODO: fix type cast? + applyPartialReward( + state, + 'maskShards' as ExtractNumberKeys>, + MASK_SHARD_REWARDS, + 'percent' as ExtractNumberKeys>, + willCheck + ); + } + if (state.game === 'hollow-knight') { const hkState = state as State<'hollow-knight'>; - if (sectionName === 'grubs') { - grubRewards(hkState, willCheck); - } - if (sectionName === 'maskShards') { - maskShardRewards(hkState, willCheck); + const typedSectionName = sectionName as SectionNames<'hollow-knight'>; + if (typedSectionName === 'grubs') { + applyPartialReward( + hkState, + 'grubs', + GRUB_REWARDS, + 'geo', + willCheck + ); + } else if (typedSectionName === 'vesselFragments') { + applyPartialReward( + hkState, + 'vesselFragments', + VESSEL_FRAGMENT_REWARDS, + 'percent', + willCheck + ); } - if (sectionName === 'vesselFragments') { - vesselFragmentRewards(hkState, willCheck); + } else if (state.game === 'silksong') { + const typedSectionName = sectionName as SectionNames<'silksong'>; + if (typedSectionName === 'spoolFragments') { + applyPartialReward( + state as State<'silksong'>, + 'spoolFragments', + SILK_SPOOL_PART_REWARDS, + 'percent', + willCheck + ); } } }; @@ -282,39 +310,6 @@ const createChecklistStore = ( immer(set => ({ ...initialState, - setFromSaveFile: (savefile: SaveFile) => { - const game = typedKeys(savefile)[0]!; - type Save = typeof game; - const save = savefile[game]! as SaveFileData; - - useChecklistStore(game).setState(state => { - typedEntries(save).forEach( - ([sectionName, section]) => { - Array.from(section.entries()).forEach( - ([checkName, checked]) => { - const section = state.checks[ - sectionName - ] as ChecksSection< - Save, - SectionNames - >; - const check = section[checkName]; - - handleCheck( - state, - sectionName, - check, - checked - ); - } - ); - } - ); - }); - - useUiStore.getState().setCurrentTab(game); - }, - reset: (sectionName?: Section) => { if (sectionName) { set(state => { @@ -323,7 +318,7 @@ const createChecklistStore = ( ] as ChecksSection; typedValues(section).forEach(check => handleCheck( - state as State, + state, sectionName, check, false @@ -346,12 +341,7 @@ const createChecklistStore = ( sectionName ] as ChecksSection; typedValues(section).forEach(check => - handleCheck( - state as State, - sectionName, - check, - true - ) + handleCheck(state, sectionName, check, true) ); }); } else { @@ -361,7 +351,7 @@ const createChecklistStore = ( ).forEach(([sectionName, section]) => { typedValues(section).forEach(check => handleCheck( - state as State, + state, sectionName, check, true @@ -383,18 +373,76 @@ const createChecklistStore = ( ][name]; const willCheck = !check.checked; - handleCheck( - state as State, - section, - check, - willCheck - ); + handleCheck(state, section, check, willCheck); }); }, validateCheck, validateChecks, + + setFromSaveFile: ( + savefile: SaveFile + ) => { + const game = typedKeys(savefile)[0]! as Game; + const save = savefile[game]! as SaveFileData; + + useChecklistStore(game).setState(state => { + typedEntries(save).forEach( + ([sectionName, section]) => { + Array.from(section.entries()).forEach( + ([checkName, checked]) => { + if ( + !(sectionName in state.checks) + ) { + console.error(sectionName); + throw new Error( + `Section \`${sectionName}\` not found` + ); + } + + const existingSectionName = + sectionName as SectionNames; + + const section = ( + state.checks as Checks + )[ + existingSectionName + ] as ChecksSection< + Game, + SectionNames + >; + + if (!(checkName in section)) { + console.error(checkName); + throw new Error( + `Check \`${checkName}\` not found in section \`${existingSectionName}\`` + ); + } + + const existingCheckName = + checkName as CheckNames< + Game, + SectionNames + >; + + const check = + section[existingCheckName]; + + handleCheck( + state, + existingSectionName, + check, + checked + ); + } + ); + } + ); + }); + + useUiStore.getState().setCurrentTab(game); + }, })) ), { diff --git a/src/types/checklist.ts b/src/types/checklist.ts index 0beec5d..0d9b884 100644 --- a/src/types/checklist.ts +++ b/src/types/checklist.ts @@ -231,10 +231,10 @@ type HollowKnightChecksKeys = { | '[Tower of Love] #2' | '[Tower of Love] #3'; items: - | '[SIMPLE_KEY] [Simple Key] from [Sly]' - | '[SIMPLE_KEY] [Simple Key] near [City Storerooms]' - | '[SIMPLE_KEY] [Simple Key] in the [Ancient Basin]' - | '[SIMPLE_KEY] [Simple Key] behind [Pale Lurker]' + | '[SIMPLE_KEY_(HOLLOW_KNIGHT)] [Simple Key] from [Sly]' + | '[SIMPLE_KEY_(HOLLOW_KNIGHT)] [Simple Key] near [City Storerooms]' + | '[SIMPLE_KEY_(HOLLOW_KNIGHT)] [Simple Key] in the [Ancient Basin]' + | '[SIMPLE_KEY_(HOLLOW_KNIGHT)] [Simple Key] behind [Pale Lurker]' | '[ELEGANT_KEY] [Elegant Key]' | '[LOVE_KEY] [Love Key]' | "[SHOPKEEPER'S_KEY] [Shopkeeper's Key]" @@ -325,7 +325,244 @@ type HollowKnightChecksKeys = { /** Silksong specific check keys */ type SilksongChecksKeys = { - bosses: '[Lace]'; // TODO + bosses: + | '[Moss Mother]' + | '[Bell Beast]' + | '[Fourth Chorus]' + | '[Lace]' + | '[Sister Splinter]' + | '[Savage Beastfly]' + | '[Widow]' + | '[Last Judge] / [Phantom]' + | '[Savage Beastfly 2](Savage Beastfly#Far_Fields)' + | '[Cogwork Dancers]' + | '[Trobbio]' + | '[The Unravelled]' + | '[First Sinner]' + | '[Voltvyrm]' + | '[Father of the Flame]' + | '[Lace 2](Lace#The_Cradle)' + | '[Grand Mother Silk]' + | '[Pinstress]' + | '[Shrine Guardian Seth]' + | '[Nyleth]' + | '[Crust King Khann]' + | '[Skarrsinger Karmelita]' + | '[Clover Dancers]'; + melodies: + | "[Conductor's Melody]" + | "[Architect's Melody]" + | "[Vaultkeeper's Melody]"; + silkHearts: + | '[Bell Beast]' + | '[Lace 2](Lace#The_Cradle)' + | '[The Unravelled]'; + tools: + | '[Straight Pin]' + | '[Threefold Pin]' + | '[Sting Shard]' + | '[Tacks]' + | '[Longpin]' + | '[Curveclaw] / [Curvesickle]' + | '[Throwing Ring]' + | '[Pimpillo]' + | '[Conchcutter]' + | '[Silkshot]' + | "[Delver's Drill]" + | '[Cogwork Wheel]' + | '[Cogfly]' + | '[Rosary Cannon]' + | '[Voltvessels]' + | '[Flintslate]' + | '[Flea Brew]' + | '[Plasmium Phial]' + | "[Druid's Eye] / [Druid's Eyes]" + | '[Magma Bell]' + | '[Warding Bell]' + | '[Pollip Pouch]' + | '[Fractured Mask]' + | '[Multibinder]' + | '[Weavelight]' + | '[Sawtooth Circlet]' + | '[Injector Band]' + | '[Spool Extender]' + | '[Reserve Bind]' + | '[Claw Mirror] / [Claw Mirrors]' + | '[Memory Crystal]' + | '[Snitch Pick]' + | '[Volt Filament]' + | '[Quick Sling]' + | '[Wreath of Purity]' + | '[Longclaw]' + | '[Wispfire Lantern]' + | '[Egg of Flealia]' + | '[Pin Badge]' + | '[Compass]' + | '[Shard Pendant]' + | '[Magnetite Brooch]' + | '[Weighted Belt]' + | '[Barbed Bracelet]' + | "[Dead Bug's Purse] / [Shell Satchel]" + | '[Magnetite Dice]' + | '[Scuttlebrace]' + | "[Ascendant's Grip]" + | '[Spider Strings]' + | '[Silkspeed Anklets]' + | "[Thief's Mark]"; + silkSkills: + | '[Silkspear]' + | '[Thread Storm]' + | '[Cross Stitch]' + | '[Sharpdart]' + | '[Rune Rage]' + | '[Pale Nails]'; + ancestralArts: + | '[Swift Step]' + | '[Cling Grip]' + | '[Needolin]' + | '[Clawline]' + | '[Silk Soar]' + | '[Needle Strike]' + | '[Sylphsong]'; + crests: + | '[Reaper Crest]' + | '[Wanderer Crest]' + | '[Beast Crest]' + | '[Witch Crest]' + | '[Architect Crest]' + | '[Shaman Crest]'; + eva: + | 'Evolved [Hunter Crest]' + | '[Vesticrest] yellow slot' + | '[Vesticrest] blue slot' + | 'Further evolved [Hunter Crest]'; + maskShards: + | '[Pebb] from [Bone Bottom] for [ROSARY] 300' + | '[Wormways]' + | '[Deep Docks] entrance' + | '[Far Fields] [Seamstress]' + | '[Shellwood]' + | '[Weavenest Alta]' + | '[Jubilana] from [Songclave] for [ROSARY] 750' + | 'West [Cogwork Core]' + | '[Whispering Vaults]' + | '[Savage Beastfly] [Wish]' + | '[Far Fields] rising lava escape sequence' + | 'West [Mount Fay]' + | '[Slab]' + | '[Bilewater]' + | '[Wisp Thicket]' + | '[Blasted Steps]' + | '[Mount Fay] [Brightvein]' + | '[Fastest in Pharloom] [Wish]' + | '[Dark Hearts] [Wish]' + | '[The Hidden Hunter] [Wish]'; + needle: + | '[Sharpened Needle](Needle#Upgrades)' + | '[Shining Needle](Needle#Upgrades)' + | '[Hivesteel Needle](Needle#Upgrades)' + | '[Palesteel Needle](Needle#Upgrades)'; + spoolFragments: + | '[Bone Bottom]' + | '[Deep Docks] hot floor' + | '[Weavenest Alta]' + | '[Greymoor]' + | '[Slab]' + | '[Frey] from [Bellhart] for [ROSARY] 270' + | '[Grand Gate]' + | '[Underworks]' + | 'From [Mooshka] at [Grand Gate]' + | '[Whiteward]' + | '[Cogwork Core]' + | '[Underworks] near [The Cauldron]' + | '[Balm for the Wounded] [Wish]' + | '[Jubilana] from [Songclave] for [ROSARY] 500' + | '[Deep Docks] behind [Simple Key]' + | '[High Halls]' + | '[Memorium]' + | '[Grindle] for [ROSARY] 680'; + toolPouch: + | "[Mort] from [Pilgrim's Rest] for [ROSARY] 220" + | "[Loddie]'s pin challenge" + | "[Nuu]'s wish" + | 'From [Mooshka] in [Fleatopia]' + | '[Forge Daughter] for [ROSARY] 180' + | '[Crawbug Clearing] [Wish]' + | '[Twelfth Architect] for [ROSARY] 450' + | '[Grindle] for [ROSARY] 700'; + items: + | "[Drifter's Cloak]" + | '[Faydown Cloak]' + | '[White Key]' + | '[Key of Apostate]' + | '[Sacred Cylinder]' + | '[Twisted Bud]' + | '[Steel Spines]' + | '[Architect Key]' + | '[CRAFTMETAL] [Craftmetal] from [Pebb] in [Bone Bottom] for [ROSARY] 60' + | "[CRAFTMETAL] [Craftmetal] in [The Marrow] near [Hunter's March]" + | '[CRAFTMETAL] [Craftmetal] in [Deep Docks] near [Far Fields]' + | '[CRAFTMETAL] [Craftmetal] in [Blasted Steps] near [Last Judge]' + | '[CRAFTMETAL] [Craftmetal] from [Jubilana] for [ROSARY] 180' + | '[CRAFTMETAL] [Craftmetal] in [Wisp Thicket]' + | '[CRAFTMETAL] [Craftmetal] in [Underworks] south of [The Cauldron]' + | '[CRAFTMETAL] [Craftmetal] in [Pale Lake]' + | '[MEMORY_LOCKET] [Memory Locket] for [Volatile Flintbeetles] [Wish]' + | '[MEMORY_LOCKET] [Memory Locket] in [The Marrow]' + | "[MEMORY_LOCKET] [Memory Locket] in [Hunter's March]" + | '[MEMORY_LOCKET] [Memory Locket] in [Deep Docks] behind [Simple Key]' + | '[MEMORY_LOCKET] [Memory Locket] from [Mort] in [Far Fields] for [ROSARY] 150' + | '[MEMORY_LOCKET] [Memory Locket] in [Far Fields] near [Skarrsinger Karmelita]' + | '[MEMORY_LOCKET] [Memory Locket] in [Greymoor] near [Bellway]' + | '[MEMORY_LOCKET] [Memory Locket] in [Greymoor] inside [Halfway Home]' + | '[MEMORY_LOCKET] [Memory Locket] from [Frey] in [Bellhart] for [ROSARY] 330' + | "[MEMORY_LOCKET] [Memory Locket] in [Bellhart]'s ceiling" + | '[MEMORY_LOCKET] [Memory Locket] in [Blasted Steps]' + | '[MEMORY_LOCKET] [Memory Locket] in the [Sands of Karak]' + | '[MEMORY_LOCKET] [Memory Locket] in [Wormways]' + | '[MEMORY_LOCKET] [Memory Locket] in the [Underworks]' + | '[MEMORY_LOCKET] [Memory Locket] at [Grand Bellway]' + | '[MEMORY_LOCKET] [Memory Locket] in [Memorium]' + | '[MEMORY_LOCKET] [Memory Locket] in [The Slab]' + | '[MEMORY_LOCKET] [Memory Locket] in [Whispering Vaults]' + | '[MEMORY_LOCKET] [Memory Locket] in [Bilewater] secret room' + | '[MEMORY_LOCKET] [Memory Locket] in [Bilewater] near the bench shortcut'; + everbloom: + | '[Pollen Heart]' + | '[Encrusted Heart]' + | "[Hunter's Heart]" + | '[Conjoined Heart]' + | '[Everbloom]'; + wishes: + | '[Berry Picking]' + | '[The Lost Fleas]' + | '[Flexile Spines]' + | '[Rite of the Pollip]' + | '[My Missing Courier]' + | '[Volatile Flintbeetles]' + | '[Bugs of Pharloom]' + | '[The Threadspun Town]' + | '[Crawbug Clearing]' + | "[Alchemist's Assistant]" + | '[Roach Guts]' + | '[The Wandering Merchant]' + | '[Savage Beastfly](Wishes#Grand_Hunt_Wishes)' + | '[Fine Pins]' + | '[Balm for the Wounded]' + | '[Building Up Songclave]' + | '[Cloaks of the Choir]' + | '[Strengthening Songclave]' + | '[The Lost Merchant]' + | '[Broodfeast] / [Runtfeast]' + | '[Rite of Rebirth]' + | '[Infestation Operation]' + | "[Trail's End]" + | '[Fastest in Pharloom]' + | '[Dark Hearts]' + | '[Final Audience]' + | '[The Hidden Hunter]'; + fleas: never; + relics: never; }; /** Union type for all possible check keys */ @@ -369,6 +606,12 @@ type CommonChecklistState = { percent: number; /** The checks that make up the checklist. */ checks: Checks; + /** The amount of mask shards collected. */ + maskShards: number; + /** The amount of simple keys collected. */ + simpleKeys: number; + /** The required amount of simple keys. */ + simpleKeysReq: number; }; /** Hollow Knight specific state properties. */ @@ -382,22 +625,16 @@ export type HollowKnightChecklistState = paleOre: number; /** The amount of charms collected. */ charms: number; - /** The amount of grubs collected. */ + /** The amount of grubs freed. */ grubs: number; - /** The amount of mask shards collected. */ - maskShards: number; /** The amount of vessel fragments collected. */ vesselFragments: number; - /** The amount of simple keys collected. */ - simpleKeys: number; /** The required amount of geo. */ geoReq: number; /** The required amount of essence (array to track history and compute max). */ - essenceReq: [number]; + essenceReq: number[]; /** The required amount of pale ore. */ paleOreReq: number; - /** The required amount of simple keys. */ - simpleKeysReq: number; }; /** Silksong specific state properties. */ @@ -406,6 +643,28 @@ export type SilksongChecklistState = CommonChecklistState<'silksong'> & { rosaries: number; /** The required amount of rosaries. */ rosariesReq: number; + /** The amount of Spool Fragments collected. */ + spoolFragments: number; + /** The amount of Acts started. */ + acts: number; + /** The amount of tools collected. */ + tools: number; + /** The amount of hearts collected. */ + hearts: number; + /** The amount of fleas found. */ + fleas: number; + /** The amount of pail oil collected. */ + paleOil: number; + /** The amount of craft metal collected. */ + craftmetal: number; + /** The amount of memory lockets collected and used. */ + memoryLockets: number; + /** The required amount of pail oil. */ + paleOilReq: number; + /** The required amount of craft metal. */ + craftmetalReq: number; + /** The required amount of memory lockets. */ + memoryLocketsReq: number[]; }; /** Represents the state of the checklist, including progress and requirements. */ @@ -470,7 +729,9 @@ export type RequirementCheckErrors = { export type AnyObject = Record; export type SaveFileData = { - [Section in SectionNames]: Map, boolean>; + [Section in SectionNames | string]: Section extends SectionNames + ? Map, boolean> + : Map; }; /** A save file serialized by webasm savefile parser. */ diff --git a/src/types/util.ts b/src/types/util.ts index 285e82e..e0c5eb8 100644 --- a/src/types/util.ts +++ b/src/types/util.ts @@ -24,3 +24,7 @@ type AtLeastOne }> = Partial & U[keyof U]; export type ExactlyOne = AtMostOne & AtLeastOne; + +export type ExtractNumberKeys = { + [K in keyof T]: T[K] extends number ? K : never; +}[keyof T]; diff --git a/src/util/formatCheckListError.ts b/src/util/formatCheckListError.ts index 48aeeeb..08c1fff 100644 --- a/src/util/formatCheckListError.ts +++ b/src/util/formatCheckListError.ts @@ -1,22 +1,21 @@ +import { PartialDeep } from 'type-fest'; + import { OFFICIAL_TM_GRUB_NAMES } from '../constants'; +import INITIAL_CHECKLIST_STATE from '../stores/INITIAL_CHECKLIST_STATE'; import useUiStore from '../stores/uiStore'; import { + ChecklistState, CheckNames, Checks, ChecksSection, GameKey, - RequirementCheckErrors, SectionNames, } from '../types/checklist'; import { typedEntries } from './typedObject'; const formatCheckListError = ( checkName: CheckNames>, - errors: - | NonNullable< - NonNullable[SectionNames] - >[CheckNames>] // | string - | undefined + errors: PartialDeep> | undefined ): string | undefined => { if (errors && typeof errors === 'object') { const useOfficialTMGrubNames = @@ -31,57 +30,117 @@ const formatCheckListError = ( const requires = typedEntries(errors) .map(([requirement, error]) => { - // TODO: types? - const typedRequirement = requirement as SectionNames; - switch (typedRequirement) { - case 'geo': - return `[GEO] ${error}`; - case 'essence': - return `[ESSENCE] ${error}`; - case 'paleOre': - return `[PALE_ORE] ${error}`; - case 'grubs': - return `${error} grubs rescued`; - case 'simpleKeys': - return `${error} simple key(s) collected`; - case 'maskShards': - return `${error} mask shard(s) collected`; - case 'charms': - return `${error} charms collected`; - case 'vesselFragments': - case 'geoReq': - case 'essenceReq': - case 'paleOreReq': - case 'simpleKeysReq': - case 'game': - case 'percent': - throw new Error( - `Nothing should require ${typedRequirement}` - ); - case 'checks': { - return typedEntries(error as Checks) - .map(([section, sectionErrors]) => { - const positive = getEntriesText( - section, - sectionErrors, - true - ); - const negative = getEntriesText( - section, - sectionErrors, - false - ); + const checks = () => + typedEntries(error as Checks) + .map(([section, sectionErrors]) => { + const positive = getEntriesText( + section, + sectionErrors, + true + ); + const negative = getEntriesText( + section, + sectionErrors, + false + ); + + return positive + negative; + }) + .join('; '); - return positive + negative; - }) - .join('; '); + if ( + INITIAL_CHECKLIST_STATE['hollow-knight'].hasOwnProperty( + requirement + ) + ) { + const typedRequirement = + requirement as keyof ChecklistState<'hollow-knight'>; + switch (typedRequirement) { + case 'geo': + return `[GEO] ${error}`; + case 'essence': + return `[ESSENCE] ${error}`; + case 'paleOre': + return `[PALE_ORE] ${error}`; + case 'grubs': + return `${error} [grubs] rescued`; + case 'simpleKeys': + return `${error} [SIMPLE_KEY_(HOLLOW_KNIGHT)] [Simple Key](Simple Key (Hollow Knight))(s) collected`; + case 'maskShards': + return `${error} [Mask Shard](Mask Shard (Hollow Knight))(s) collected`; + case 'charms': + return `${error} [Charms] collected`; + case 'vesselFragments': + case 'geoReq': + case 'essenceReq': + case 'paleOreReq': + case 'simpleKeysReq': + case 'game': + case 'percent': + throw new Error( + `Nothing should require ${typedRequirement}` + ); + case 'checks': + return checks(); + default: + throw new Error( + `Unimplemented requirement for '${ + typedRequirement satisfies never + }' type` + ); + } + } else if ( + INITIAL_CHECKLIST_STATE['silksong'].hasOwnProperty( + requirement + ) + ) { + const typedRequirement = + requirement as keyof ChecklistState<'silksong'>; + switch (typedRequirement) { + case 'rosaries': + return `[ROSARY] ${error}`; + case 'fleas': + return `${error} [fleas] rescued`; + case 'simpleKeys': + return `${error} [SIMPLE_KEY_(SILKSONG)] [Simple Key](Simple Key (Silksong))(s) collected`; + case 'maskShards': + return `${error} [Mask Shard](Mask Shard (Silksong))(s) collected`; + case 'memoryLockets': + return `${error} [MEMORY_LOCKET] [Memory Locket](Memory Locket (Silksong))(s) collected`; + case 'paleOil': + return `[PALE_OIL] ${error}`; + case 'craftmetal': + return `[CRAFTMETAL] ${error}`; + case 'tools': + return `${error} [Tools] collected`; + case 'hearts': + return `${error} [Hearts](Items (Silksong)#Deliverables) collected`; + case 'spoolFragments': + case 'rosariesReq': + case 'simpleKeysReq': + case 'memoryLocketsReq': + case 'paleOilReq': + case 'craftmetalReq': + case 'game': + case 'percent': + throw new Error( + `Nothing should require ${typedRequirement}` + ); + case 'acts': + return `[Act ${error}] being started`; + case 'checks': + return checks(); + default: + throw new Error( + `Unimplemented requirement for '${ + typedRequirement satisfies never + }' type` + ); } - default: - throw new Error( - `Unimplemented requirement for '${ - typedRequirement /* satisfies never */ // :( - }' type` - ); + } else { + throw new Error( + `${String(requirement)} is not implemented` + ); } }) .filter(Boolean) @@ -114,55 +173,109 @@ const getEntriesText = ( const requirementTextForSection = ( section: SectionNames, joined: string -) => { - // TODO: isHollowKnightState (?) - switch (section) { - case 'bosses': - case 'optionalBosses': - case 'dreamers': - case 'dreamWarriors': - case 'dreamBosses': { - joined += 'defeated'; - break; - } - case 'equipment': - case 'charms': - case 'items': - case 'vesselFragments': - case 'maskShards': { - joined += 'acquired'; - break; +): string => { + if ( + INITIAL_CHECKLIST_STATE['hollow-knight'].checks.hasOwnProperty(section) + ) { + const typedSection = section as SectionNames<'hollow-knight'>; + switch (typedSection) { + case 'bosses': + case 'optionalBosses': + case 'dreamers': + case 'dreamWarriors': + case 'dreamBosses': { + joined += 'defeated'; + break; + } + case 'equipment': + case 'charms': + case 'items': + case 'vesselFragments': + case 'maskShards': + case 'relics': + case 'whisperingRoots': { + joined += 'collected'; + break; + } + case 'spells': { + joined += 'learned'; + break; + } + case 'nail': + case 'nailArts': + case 'dreamNail': { + joined += 'obtained'; + break; + } + case 'grubs': { + joined += 'rescued'; + break; + } + case 'colosseum': + case 'godhome': { + joined += 'completed'; + break; + } + default: + throw new Error( + `Unimplemented requirement for '${ + typedSection satisfies never + }' section` + ); } - case 'relics': - case 'whisperingRoots': - joined += 'collected'; - break; - case 'spells': { - joined += 'learned'; - break; + } else if ( + INITIAL_CHECKLIST_STATE['silksong'].checks.hasOwnProperty(section) + ) { + const typedSection = section as SectionNames<'silksong'>; + switch (typedSection) { + case 'bosses': + case 'ancestralArts': + case 'tools': + case 'items': + case 'spoolFragments': + case 'maskShards': + case 'toolPouch': { + joined += 'acquired'; + break; + } + case 'relics': + case 'silkHearts': + case 'everbloom': { + joined += 'collected'; + break; + } + case 'silkSkills': + case 'crests': { + joined += 'bound'; + break; + } + case 'needle': + case 'eva': { + joined += 'obtained'; + break; + } + case 'fleas': { + joined += 'found'; + break; + } + case 'wishes': { + joined += 'granted'; + break; + } + case 'melodies': { + joined += 'learned'; + break; + } + default: + throw new Error( + `Unimplemented requirement for '${ + typedSection satisfies never + }' section` + ); } - case 'nail': - case 'nailArts': - case 'dreamNail': { - joined += 'obtained'; - break; - } - case 'grubs': { - joined += 'rescued'; - break; - } - case 'colosseum': - case 'godhome': { - joined += 'completed'; - break; - } - default: - throw new Error( - `Unimplemented requirement for '${ - section /* satisfies never */ // :( - }' section` - ); + } else { } + return joined; }; diff --git a/src/util/renderLink.ts b/src/util/renderLink.ts index f612d9d..9f6deb4 100644 --- a/src/util/renderLink.ts +++ b/src/util/renderLink.ts @@ -1,16 +1,20 @@ import { ARCANE_EGG, CHARM_NOTCH, + CRAFTMETAL, ELEGANT_KEY, ESSENCE, GEO, JOURNAL, KINGS_IDOL, LOVE_KEY, + MEMORY_LOCKET, + PALE_OIL, PALE_ORE, - ROSARIES, + ROSARY, SEAL, - SIMPLE_KEY, + SIMPLE_KEY_HK, + SIMPLE_KEY_SS, SLY_KEY, TRAM_PASS, } from '../assets'; @@ -18,21 +22,25 @@ import { const WIKI_URL_BASE = 'https://hollowknight.wiki/w/'; const ICONS: Record = { - GEO: GEO, - ESSENCE: ESSENCE, - PALE_ORE: PALE_ORE, - CHARM_NOTCH: CHARM_NOTCH, - TRAM_PASS: TRAM_PASS, + GEO, + ESSENCE, + PALE_ORE, + CHARM_NOTCH, + TRAM_PASS, "WANDERER'S_JOURNAL": JOURNAL, HALLOWNEST_SEAL: SEAL, "KING'S_IDOL": KINGS_IDOL, - ARCANE_EGG: ARCANE_EGG, - LOVE_KEY: LOVE_KEY, - SIMPLE_KEY: SIMPLE_KEY, - ELEGANT_KEY: ELEGANT_KEY, + ARCANE_EGG, + LOVE_KEY, + 'SIMPLE_KEY_(HOLLOW_KNIGHT)': SIMPLE_KEY_HK, + ELEGANT_KEY, "SHOPKEEPER'S_KEY": SLY_KEY, - ROSARIES: ROSARIES, + ROSARY, + MEMORY_LOCKET, + PALE_OIL, + 'SIMPLE_KEY_(SILKSONG)': SIMPLE_KEY_SS, + CRAFTMETAL, }; type ParsedItemType = 'link' | 'text' | 'icon' | 'monospace'; @@ -44,7 +52,7 @@ interface ParsedItem { } const toTitleCase = (s: string) => - s.replace(/^_*(.)|_+(.)/g, (_, c, d) => + s.replace(/^(.)|_+(\(?.)/g, (_, c, d) => c ? c.toUpperCase() : ' ' + d.toUpperCase() ); diff --git a/yarn.lock b/yarn.lock index 26dd400..4d9956e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -424,7 +424,7 @@ __metadata: languageName: node linkType: hard -"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.7.0": +"@eslint-community/eslint-utils@npm:^4.7.0": version: 4.7.0 resolution: "@eslint-community/eslint-utils@npm:4.7.0" dependencies: @@ -435,6 +435,17 @@ __metadata: languageName: node linkType: hard +"@eslint-community/eslint-utils@npm:^4.8.0": + version: 4.9.0 + resolution: "@eslint-community/eslint-utils@npm:4.9.0" + dependencies: + eslint-visitor-keys: "npm:^3.4.3" + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: 10c0/8881e22d519326e7dba85ea915ac7a143367c805e6ba1374c987aa2fbdd09195cc51183d2da72c0e2ff388f84363e1b220fd0d19bef10c272c63455162176817 + languageName: node + linkType: hard + "@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.12.1": version: 4.12.1 resolution: "@eslint-community/regexpp@npm:4.12.1" @@ -486,10 +497,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.34.0, @eslint/js@npm:^9.34.0": - version: 9.34.0 - resolution: "@eslint/js@npm:9.34.0" - checksum: 10c0/53f1bfd2a374683d9382a6850354555f6e89a88416c34a5d34e9fbbaf717e97c2b06300e8f93e5eddba8bda8951ccab7f93a680e56ded1a3d21d526019e69bab +"@eslint/js@npm:9.35.0, @eslint/js@npm:^9.35.0": + version: 9.35.0 + resolution: "@eslint/js@npm:9.35.0" + checksum: 10c0/d40fe38724bc76c085c0b753cdf937fa35c0d6807ae76b2632e3f5f66c3040c91adcf1aff2ce70b4f45752e60629fadc415eeec9af3be3c274bae1cac54b9840 languageName: node linkType: hard @@ -879,12 +890,12 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:^19.1.12": - version: 19.1.12 - resolution: "@types/react@npm:19.1.12" +"@types/react@npm:^19.1.13": + version: 19.1.13 + resolution: "@types/react@npm:19.1.13" dependencies: csstype: "npm:^3.0.2" - checksum: 10c0/e35912b43da0caaab5252444bab87a31ca22950cde2822b3b3dc32e39c2d42dad1a4cf7b5dde9783aa2d007f0b2cba6ab9563fc6d2dbcaaa833b35178118767c + checksum: 10c0/75e35b54883f5ed07d3b5cb16a4711b6dbb7ec6b74301bcb9bfa697c9d9fff022ec508e1719e7b2c69e2e8b042faac1125be7717b5e5e084f816a2c88e136920 languageName: node linkType: hard @@ -895,106 +906,106 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:8.41.0": - version: 8.41.0 - resolution: "@typescript-eslint/eslint-plugin@npm:8.41.0" +"@typescript-eslint/eslint-plugin@npm:8.43.0": + version: 8.43.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.43.0" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:8.41.0" - "@typescript-eslint/type-utils": "npm:8.41.0" - "@typescript-eslint/utils": "npm:8.41.0" - "@typescript-eslint/visitor-keys": "npm:8.41.0" + "@typescript-eslint/scope-manager": "npm:8.43.0" + "@typescript-eslint/type-utils": "npm:8.43.0" + "@typescript-eslint/utils": "npm:8.43.0" + "@typescript-eslint/visitor-keys": "npm:8.43.0" graphemer: "npm:^1.4.0" ignore: "npm:^7.0.0" natural-compare: "npm:^1.4.0" ts-api-utils: "npm:^2.1.0" peerDependencies: - "@typescript-eslint/parser": ^8.41.0 + "@typescript-eslint/parser": ^8.43.0 eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/29812ee5deeae65e67db29faa8d96bc70255c45788f342b11838850ea29a96e4331622cad3e703ffacaa895372845d44fd6b04786117c78f1a027595adff2e62 + checksum: 10c0/9823f6e917d16f95a87fb1fd6c224f361a9f17386453ac97d7d457774cf2ea7bdbcfad37ad063b71ec01a4292127a8bfe69d1987b948e85def2410de8fe353dd languageName: node linkType: hard -"@typescript-eslint/parser@npm:8.41.0": - version: 8.41.0 - resolution: "@typescript-eslint/parser@npm:8.41.0" +"@typescript-eslint/parser@npm:8.43.0": + version: 8.43.0 + resolution: "@typescript-eslint/parser@npm:8.43.0" dependencies: - "@typescript-eslint/scope-manager": "npm:8.41.0" - "@typescript-eslint/types": "npm:8.41.0" - "@typescript-eslint/typescript-estree": "npm:8.41.0" - "@typescript-eslint/visitor-keys": "npm:8.41.0" + "@typescript-eslint/scope-manager": "npm:8.43.0" + "@typescript-eslint/types": "npm:8.43.0" + "@typescript-eslint/typescript-estree": "npm:8.43.0" + "@typescript-eslint/visitor-keys": "npm:8.43.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/ca13ff505e9253aee761741f96714cd65a296bbfcac961efbbf7a909ff3d180b2142a23db0a2a5e50b928fa56586528b7e47ba6301089dd850945018dbf2ef50 + checksum: 10c0/b8296d3fac08f6e03c931843264a4219469a6a7d5c4d269fb14fe4c1547477a0dd1c259e6929c749efa043fb4e272436adfc94afdf07039d3b1d9e6956a6a0ea languageName: node linkType: hard -"@typescript-eslint/project-service@npm:8.41.0": - version: 8.41.0 - resolution: "@typescript-eslint/project-service@npm:8.41.0" +"@typescript-eslint/project-service@npm:8.43.0": + version: 8.43.0 + resolution: "@typescript-eslint/project-service@npm:8.43.0" dependencies: - "@typescript-eslint/tsconfig-utils": "npm:^8.41.0" - "@typescript-eslint/types": "npm:^8.41.0" + "@typescript-eslint/tsconfig-utils": "npm:^8.43.0" + "@typescript-eslint/types": "npm:^8.43.0" debug: "npm:^4.3.4" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/907ba880fcaf0805fc97012b431536b5b06db6ae4a0095708f9d9a4406feddabd964f09ea4ca99d8fa7bd141dbcc9496f1a9eb6683361a6bb01fb714a361126c + checksum: 10c0/c9058b5fbf9642c35a303641e4ff2d0df1ddac337275bab84b56167f1019fbcb7e69959239fed82e53c747f58d6ee4c1859cf5b018803cba1b1aab430439d728 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.41.0": - version: 8.41.0 - resolution: "@typescript-eslint/scope-manager@npm:8.41.0" +"@typescript-eslint/scope-manager@npm:8.43.0": + version: 8.43.0 + resolution: "@typescript-eslint/scope-manager@npm:8.43.0" dependencies: - "@typescript-eslint/types": "npm:8.41.0" - "@typescript-eslint/visitor-keys": "npm:8.41.0" - checksum: 10c0/6b339ac1fc37a1e05dc6de421db9f9b138c357497ec87af2471ad30e48c78b4979d3da40943a1c81fc85d1537326a4f938843434db63d29eff414b9364daf8e8 + "@typescript-eslint/types": "npm:8.43.0" + "@typescript-eslint/visitor-keys": "npm:8.43.0" + checksum: 10c0/f87b3c3a5d3ad18326945288fa5b9b9fa662d87f466dc159e1514e00e359e830b80557f213acb3d23d5d600826b4cc4cfa5d2d479f8aba1b9834df19a640a779 languageName: node linkType: hard -"@typescript-eslint/tsconfig-utils@npm:8.41.0, @typescript-eslint/tsconfig-utils@npm:^8.41.0": - version: 8.41.0 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.41.0" +"@typescript-eslint/tsconfig-utils@npm:8.43.0, @typescript-eslint/tsconfig-utils@npm:^8.43.0": + version: 8.43.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.43.0" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/98618a536b9cb071eacba2970ce2ca1b9243de78f4604c2e350823a5275b9d7d15238dbe6acd197c30c0b6cbbf37782c247d14984e1015a109431e4180d76af6 + checksum: 10c0/b3a472368ad31e31e58ef019f6afec7387f5885e3fd423c71f3910b6d6b767324fde8bd60bec2e7505cc130317ece7fbc91314c44cdfea74ff76b5039bf26d52 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.41.0": - version: 8.41.0 - resolution: "@typescript-eslint/type-utils@npm:8.41.0" +"@typescript-eslint/type-utils@npm:8.43.0": + version: 8.43.0 + resolution: "@typescript-eslint/type-utils@npm:8.43.0" dependencies: - "@typescript-eslint/types": "npm:8.41.0" - "@typescript-eslint/typescript-estree": "npm:8.41.0" - "@typescript-eslint/utils": "npm:8.41.0" + "@typescript-eslint/types": "npm:8.43.0" + "@typescript-eslint/typescript-estree": "npm:8.43.0" + "@typescript-eslint/utils": "npm:8.43.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^2.1.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/d4f9ae07a30f1cf331c3e3a67f8749b38f199ba5000f7a600492c27f6bec774f15c3553f293c520fb999fb88108665f2785d5261daec1445b17af14a7bb0bfac + checksum: 10c0/70e61233fd586c4545b0ee11871001ba603816fccb69b9fe883a653b32aa049e957a97f208f522b58480a4f4e1c6322b9a3ef60a389925eaefba94abcd44ff7e languageName: node linkType: hard -"@typescript-eslint/types@npm:8.41.0, @typescript-eslint/types@npm:^8.41.0": - version: 8.41.0 - resolution: "@typescript-eslint/types@npm:8.41.0" - checksum: 10c0/4945a7ed7789e0527833ee378b962416d6d0d61eb6c891fe49cb6c8dc8a9adbfc58676080ca767a1f034f74f9a981caf5f4d4706cba5025c0520a801fb45d7e1 +"@typescript-eslint/types@npm:8.43.0, @typescript-eslint/types@npm:^8.43.0": + version: 8.43.0 + resolution: "@typescript-eslint/types@npm:8.43.0" + checksum: 10c0/60d19b695affce128fe1076ebe4cff7e05d38dd50155d653fc9e995eafa56c299fd49ad4d9d2997f118a75fb57e3ca18001623bc3ef3fa0111f863079203e4b2 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.41.0": - version: 8.41.0 - resolution: "@typescript-eslint/typescript-estree@npm:8.41.0" +"@typescript-eslint/typescript-estree@npm:8.43.0": + version: 8.43.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.43.0" dependencies: - "@typescript-eslint/project-service": "npm:8.41.0" - "@typescript-eslint/tsconfig-utils": "npm:8.41.0" - "@typescript-eslint/types": "npm:8.41.0" - "@typescript-eslint/visitor-keys": "npm:8.41.0" + "@typescript-eslint/project-service": "npm:8.43.0" + "@typescript-eslint/tsconfig-utils": "npm:8.43.0" + "@typescript-eslint/types": "npm:8.43.0" + "@typescript-eslint/visitor-keys": "npm:8.43.0" debug: "npm:^4.3.4" fast-glob: "npm:^3.3.2" is-glob: "npm:^4.0.3" @@ -1003,32 +1014,32 @@ __metadata: ts-api-utils: "npm:^2.1.0" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/e86233d895403ec4986ced25f56898b2704a84545bb7dfe933f5c64f2ab969dcb7ada7e21ea7e015c875cc94a0767e70573442724960c631b7b3fc556a984c9c + checksum: 10c0/184ba925067d7fbcb377450195a89511f030a49d080e27058fa78078a069d86c1936b1a82ce6f19ff24c30c4de8b779deb050c36b06db5372c95fc7e5be7115a languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.41.0": - version: 8.41.0 - resolution: "@typescript-eslint/utils@npm:8.41.0" +"@typescript-eslint/utils@npm:8.43.0": + version: 8.43.0 + resolution: "@typescript-eslint/utils@npm:8.43.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.7.0" - "@typescript-eslint/scope-manager": "npm:8.41.0" - "@typescript-eslint/types": "npm:8.41.0" - "@typescript-eslint/typescript-estree": "npm:8.41.0" + "@typescript-eslint/scope-manager": "npm:8.43.0" + "@typescript-eslint/types": "npm:8.43.0" + "@typescript-eslint/typescript-estree": "npm:8.43.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/3a2ed9b5f801afeccde44dbacdeae0b9c82cc3e1af5e92926929ad86384dc0fb0027152e68c5edfabe904647c2160c0c45ec9c848a8d67c3efb86b78a1343acb + checksum: 10c0/42fc8c60551361d80b5c53b303ba8cd20cf914665168416ad0a278cd44aae587311af9e4461f92ed28b5f36091d275a0e9974482d5e9ba95fc00108a537cdd36 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.41.0": - version: 8.41.0 - resolution: "@typescript-eslint/visitor-keys@npm:8.41.0" +"@typescript-eslint/visitor-keys@npm:8.43.0": + version: 8.43.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.43.0" dependencies: - "@typescript-eslint/types": "npm:8.41.0" + "@typescript-eslint/types": "npm:8.43.0" eslint-visitor-keys: "npm:^4.2.1" - checksum: 10c0/cfe52e77b9e07c23a4d9f4adf9e6bf27822e58694c9a34fefa4b9fc96d553e9df561971c4da5fc78392522e34696fc1149a76f6a02c328136771c5efe0fd1029 + checksum: 10c0/5d576eaf7bea41933ba726f4b24410bd3fc2521ef286967c3dc630c6a90fabff2a2d7c4d12cb841d3f946d2e5e6fb2605e7edd84e3360308fe379dbf2b8dc2fa languageName: node linkType: hard @@ -1553,17 +1564,17 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^9.34.0": - version: 9.34.0 - resolution: "eslint@npm:9.34.0" +"eslint@npm:^9.35.0": + version: 9.35.0 + resolution: "eslint@npm:9.35.0" dependencies: - "@eslint-community/eslint-utils": "npm:^4.2.0" + "@eslint-community/eslint-utils": "npm:^4.8.0" "@eslint-community/regexpp": "npm:^4.12.1" "@eslint/config-array": "npm:^0.21.0" "@eslint/config-helpers": "npm:^0.3.1" "@eslint/core": "npm:^0.15.2" "@eslint/eslintrc": "npm:^3.3.1" - "@eslint/js": "npm:9.34.0" + "@eslint/js": "npm:9.35.0" "@eslint/plugin-kit": "npm:^0.3.5" "@humanfs/node": "npm:^0.16.6" "@humanwhocodes/module-importer": "npm:^1.0.1" @@ -1599,7 +1610,7 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10c0/ba3e54fa0c8ed23d062f91519afaae77fed922a6c4d76130b6cd32154bcb406aaea4b3c5ed88e0be40828c1d5b6921592f3947dbdc5e2043de6bd7aa341fe5ea + checksum: 10c0/798c527520ccf62106f8cd210bd1db1f8eb1b0e7a56feb0a8b322bf3a1e6a0bc6dc3a414542c22b1b393d58d5e3cd0252c44c023049de9067b836450503a2f03 languageName: node linkType: hard @@ -1879,10 +1890,10 @@ __metadata: languageName: node linkType: hard -"globals@npm:^16.3.0": - version: 16.3.0 - resolution: "globals@npm:16.3.0" - checksum: 10c0/c62dc20357d1c0bf2be4545d6c4141265d1a229bf1c3294955efb5b5ef611145391895e3f2729f8603809e81b30b516c33e6c2597573844449978606aad6eb38 +"globals@npm:^16.4.0": + version: 16.4.0 + resolution: "globals@npm:16.4.0" + checksum: 10c0/a14b447a78b664b42f6d324e8675fcae6fe5e57924fecc1f6328dce08af9b2ca3a3138501e1b1f244a49814a732dc60cfc1aa24e714e0b64ac8bd18910bfac90 languageName: node linkType: hard @@ -1911,25 +1922,25 @@ __metadata: version: 0.0.0-use.local resolution: "hk100planner@workspace:." dependencies: - "@eslint/js": "npm:^9.34.0" - "@types/react": "npm:^19.1.12" + "@eslint/js": "npm:^9.35.0" + "@types/react": "npm:^19.1.13" "@types/react-dom": "npm:^19.1.9" "@vitejs/plugin-react": "npm:^5.0.2" - eslint: "npm:^9.34.0" + eslint: "npm:^9.35.0" eslint-plugin-react-hooks: "npm:^5.2.0" eslint-plugin-react-refresh: "npm:^0.4.20" - globals: "npm:^16.3.0" + globals: "npm:^16.4.0" hollow-knight-save-parser: ./pkg - immer: "npm:^10.1.1" - object-deep-merge: "npm:^1.0.4" + immer: "npm:^10.1.3" + object-deep-merge: "npm:^1.0.5" react: "npm:^19.1.1" react-dom: "npm:^19.1.1" styled-components: "npm:^6.1.19" - type-fest: "npm:^4.41.0" + type-fest: "npm:^5.0.0" typescript: "npm:^5.9.2" - typescript-eslint: "npm:^8.41.0" + typescript-eslint: "npm:^8.43.0" usehooks-ts: "npm:^3.1.1" - vite: "npm:^7.1.4" + vite: "npm:^7.1.5" vite-plugin-checker: "npm:^0.10.3" wasm-pack: "npm:^0.13.1" zundo: "npm:^2.3.0" @@ -1939,8 +1950,8 @@ __metadata: "hollow-knight-save-parser@file:./pkg::locator=hk100planner%40workspace%3A.": version: 0.0.0 - resolution: "hollow-knight-save-parser@file:./pkg#./pkg::hash=dde824&locator=hk100planner%40workspace%3A." - checksum: 10c0/cf695e494396b8098e3913469a0a4a1b777c4556d2101a682d0b1ff228a70233768435bdfd81f9d7dd3a9d5160871e89cf2b9f9bc3b3942b7f09b246f02843bc + resolution: "hollow-knight-save-parser@file:./pkg#./pkg::hash=bb4e7e&locator=hk100planner%40workspace%3A." + checksum: 10c0/2d186a33156a7970b50a021e5b182f612f1cc9aebe513cfec34f6956beb39dfbc2c5666f78bf3b544230c831a2fb7668020f2b3c8f1f928f47f10239d2c3efec languageName: node linkType: hard @@ -1994,10 +2005,10 @@ __metadata: languageName: node linkType: hard -"immer@npm:^10.1.1": - version: 10.1.1 - resolution: "immer@npm:10.1.1" - checksum: 10c0/b749e10d137ccae91788f41bd57e9387f32ea6d6ea8fd7eb47b23fd7766681575efc7f86ceef7fe24c3bc9d61e38ff5d2f49c2663b2b0c056e280a4510923653 +"immer@npm:^10.1.3": + version: 10.1.3 + resolution: "immer@npm:10.1.3" + checksum: 10c0/b3929022c1999935c9c5e9491fce20d883c15a04072628056f3b8c51a63ac0876d1c1f25cec146e325c30c906bc7f15a636c29ed53156f0a3049150f152df4c8 languageName: node linkType: hard @@ -2457,12 +2468,12 @@ __metadata: languageName: node linkType: hard -"object-deep-merge@npm:^1.0.4": - version: 1.0.4 - resolution: "object-deep-merge@npm:1.0.4" +"object-deep-merge@npm:^1.0.5": + version: 1.0.5 + resolution: "object-deep-merge@npm:1.0.5" dependencies: type-fest: "npm:4.2.0" - checksum: 10c0/2f11644d05217e1f784335a1230a7f05dbc265c4c3a765c160adb29b234bbb8842a5b8e1489cf7fab6ba21abd12f90c94194a9314825c6f952cf5888b42d48e1 + checksum: 10c0/6664ecb43a2519c9b101f1c3b130dfc73e108d86ec06fbe7261505e1522cf8b69b10dd53b8cbb4cde35cca9d44d349667e2404f06fff85cf9f50b825bb6d1839 languageName: node linkType: hard @@ -2996,6 +3007,13 @@ __metadata: languageName: node linkType: hard +"tagged-tag@npm:^1.0.0": + version: 1.0.0 + resolution: "tagged-tag@npm:1.0.0" + checksum: 10c0/91d25c9ffb86a91f20522cefb2cbec9b64caa1febe27ad0df52f08993ff60888022d771e868e6416cf2e72dab68449d2139e8709ba009b74c6c7ecd4000048d1 + languageName: node + linkType: hard + "tar@npm:^6.1.11": version: 6.2.1 resolution: "tar@npm:6.2.1" @@ -3041,6 +3059,16 @@ __metadata: languageName: node linkType: hard +"tinyglobby@npm:^0.2.15": + version: 0.2.15 + resolution: "tinyglobby@npm:0.2.15" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.3" + checksum: 10c0/869c31490d0d88eedb8305d178d4c75e7463e820df5a9b9d388291daf93e8b1eb5de1dad1c1e139767e4269fe75f3b10d5009b2cc14db96ff98986920a186844 + languageName: node + linkType: hard + "to-regex-range@npm:^5.0.1": version: 5.0.1 resolution: "to-regex-range@npm:5.0.1" @@ -3082,25 +3110,27 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^4.41.0": - version: 4.41.0 - resolution: "type-fest@npm:4.41.0" - checksum: 10c0/f5ca697797ed5e88d33ac8f1fec21921839871f808dc59345c9cf67345bfb958ce41bd821165dbf3ae591cedec2bf6fe8882098dfdd8dc54320b859711a2c1e4 +"type-fest@npm:^5.0.0": + version: 5.0.0 + resolution: "type-fest@npm:5.0.0" + dependencies: + tagged-tag: "npm:^1.0.0" + checksum: 10c0/e088f97f556056d654920153cc046c74e9f838f6b498e38cb7aaf7dfb309b9ee64c2864e4a38b027e87060b8ffdc2dc0462bdf9df047ecaee2cca56d1c1beb49 languageName: node linkType: hard -"typescript-eslint@npm:^8.41.0": - version: 8.41.0 - resolution: "typescript-eslint@npm:8.41.0" +"typescript-eslint@npm:^8.43.0": + version: 8.43.0 + resolution: "typescript-eslint@npm:8.43.0" dependencies: - "@typescript-eslint/eslint-plugin": "npm:8.41.0" - "@typescript-eslint/parser": "npm:8.41.0" - "@typescript-eslint/typescript-estree": "npm:8.41.0" - "@typescript-eslint/utils": "npm:8.41.0" + "@typescript-eslint/eslint-plugin": "npm:8.43.0" + "@typescript-eslint/parser": "npm:8.43.0" + "@typescript-eslint/typescript-estree": "npm:8.43.0" + "@typescript-eslint/utils": "npm:8.43.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/e141ffaf0332053483526a31e68755fe1438f6367571f39e67e32c0e15d509e8678adab2020597720b0307360493724d8dcf60d0620611d7e1e209d7f952cfe9 + checksum: 10c0/ee8429b16a5b7678136b8b2688bec03d11b5f1590895523ba9b8c6920c7a0876c9bf3bf0ff415df79e57c10ed48955cf183b727394b1c228ca75b5168fb466a1 languageName: node linkType: hard @@ -3239,9 +3269,9 @@ __metadata: languageName: node linkType: hard -"vite@npm:^7.1.4": - version: 7.1.4 - resolution: "vite@npm:7.1.4" +"vite@npm:^7.1.5": + version: 7.1.5 + resolution: "vite@npm:7.1.5" dependencies: esbuild: "npm:^0.25.0" fdir: "npm:^6.5.0" @@ -3249,7 +3279,7 @@ __metadata: picomatch: "npm:^4.0.3" postcss: "npm:^8.5.6" rollup: "npm:^4.43.0" - tinyglobby: "npm:^0.2.14" + tinyglobby: "npm:^0.2.15" peerDependencies: "@types/node": ^20.19.0 || >=22.12.0 jiti: ">=1.21.0" @@ -3290,7 +3320,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10c0/dbe2ba29926ffe8985c93d1b3718dcc9040080b7fa10a74c82a52aad7449136a391ba17b265288ff03b864e6f1033b9b537247521a96d5491a9d4af90ac04702 + checksum: 10c0/782d2f20c25541b26d1fb39bef5f194149caff39dc25b7836e25f049ca919f2e2ce186bddb21f3f20f6195354b3579ec637a8ca08d65b117f8b6f81e3e730a9c languageName: node linkType: hard