From 9c2bccc1607ff84139e7783582a28d41753edc23 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Mon, 13 Apr 2026 18:15:03 -0500 Subject: [PATCH 01/58] add ignore file --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 8f9ed6df14fe..68f1a89039a5 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,8 @@ *.apsave *.BIN *.puml +*.nds +*.apcvpor setups build From c5fd07d103f863b1e4a3e36a986b58e4202404a1 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Mon, 13 Apr 2026 18:52:46 -0500 Subject: [PATCH 02/58] Set up init file --- worlds/cv_por/Items.py | 0 worlds/cv_por/__init__.py | 60 +++++++++++++++++++++++++++++++++ worlds/cv_por/generator_main.py | 0 3 files changed, 60 insertions(+) create mode 100644 worlds/cv_por/Items.py create mode 100644 worlds/cv_por/__init__.py create mode 100644 worlds/cv_por/generator_main.py diff --git a/worlds/cv_por/Items.py b/worlds/cv_por/Items.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py new file mode 100644 index 000000000000..88eb8fe03784 --- /dev/null +++ b/worlds/cv_por/__init__.py @@ -0,0 +1,60 @@ +from .Items import item_table +from .Options import PoROptions, por_option_groups +from .generator_main import generate_game + +class PoRWeb(WebWorld): + theme = "ocean" + + setup_en = Tutorial( + "Multiworld Setup Guide", + "A guide to setting up the Portrait of Ruin randomizer" + "and connecting to an Archipelago server.", + "English", + "setup_en.md", + "setup/en", + ["Pink Switch"] + ) + + option_groups = por_option_groups + tutorials = [setup_en] + +class CVPoRItem(Item): + game: str = "Castlevania: Portrait of Ruin" + +class PoRSettings(settings.Group): + class RomFile(settings.UserFilePath): + """File name of the Castlevania: Portrait of Ruin US ROM file.""" + description = "Portrait of Ruin ROM File" + copy_to = "CASTLEVANIA2_ACBEA4_00.nds" + md5 = "2edd57540cae45842fbd19c45a4214f9" + +class PoRWorld(World): + """Placeholder text""" + game = "Castlevania: Portrait of Ruin" + option_definitions = PoROptions + data_version = 1 + origin_region_name = "Dracula's Castle: Entrance" + + item_name_to_id = {item: item_table[item].code for item in item_table} + location_name_to_id = location_ids + item_name_groups = get_item_names_per_category() + + web = PoRWeb + settings: typing.ClassVar[PoRSettings] + # topology_present = True + ut_can_gen_without_yaml = True + + options_dataclass = PoROptions + options: PoROptions + + #locked_locations: List[str] + #ocation_cache: List[Location] + + def __init__(self, multiworld: MultiWorld, player: int): + self.rom_name_available_event = threading.Event() + super().__init__(multiworld, player) + + self.locked_locations = [] + self.location_cache = [] + self.extra_item_count = 0 + generate_game(self) \ No newline at end of file diff --git a/worlds/cv_por/generator_main.py b/worlds/cv_por/generator_main.py new file mode 100644 index 000000000000..e69de29bb2d1 From c4cc3c34969285f2a4b75e85f7b3d34957ef288d Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Mon, 13 Apr 2026 19:38:58 -0500 Subject: [PATCH 03/58] Start on items --- worlds/cv_por/Items.py | 192 ++++++++++++++++++++++++++++++++++++++ worlds/cv_por/__init__.py | 4 +- 2 files changed, 195 insertions(+), 1 deletion(-) diff --git a/worlds/cv_por/Items.py b/worlds/cv_por/Items.py index e69de29bb2d1..63a02704f251 100644 --- a/worlds/cv_por/Items.py +++ b/worlds/cv_por/Items.py @@ -0,0 +1,192 @@ +from typing import Dict, Set, NamedTuple, Optional +from BaseClasses import ItemClassification + +class ItemData(NamedTuple): + category: str + code: Optional[int] + classification: ItemClassification + amount: Optional[int] = 1 + +item_table: Dict[str, ItemData] = { + "$1": ItemData("Money", 0x100, ItemClassification.filler, 0), + "$10": ItemData("Money", 0x101, ItemClassification.filler, 0), + "$50": ItemData("Money", 0x102, ItemClassification.filler, 0), + "$100": ItemData("Money", 0x103, ItemClassification.filler, 0), + "$500": ItemData("Money", 0x104, ItemClassification.filler, 0), + "$1000": ItemData("Money", 0x105, ItemClassification.filler, 0), + "$2000": ItemData("Money", 0x106, ItemClassification.filler, 0), + + "Potion": ItemData("Consumables", 0x0200, ItemClassification.filler, 0), + "High Potion": ItemData("Consumables", 0x0201, ItemClassification.filler, 0), + "Super Potion": ItemData("Consumables", 0x0202, ItemClassification.filler, 0), + "Tonic": ItemData("Consumables", 0x0203, ItemClassification.filler, 0), + "High Tonic": ItemData("Consumables", 0x0204, ItemClassification.filler, 0), + "Super Tonic": ItemData("Consumables", 0x0205, ItemClassification.filler, 0), + "Anti-Venom": ItemData("Consumables", 0x0206, ItemClassification.filler, 0), + "Uncurse Potion": ItemData("Consumables", 0x0207, ItemClassification.filler, 0), + "HP Max up": ItemData("Upgrades", 0x0208, ItemClassification.useful, ???), + "MP Max up": ItemData("Upgrades", 0x0209, ItemClassification.useful, ???), + "Beef Jerky": ItemData("Consumables", 0x020A, ItemClassification.filler, 0), + "Tasty Meat": ItemData("Consumables", 0x020B, ItemClassification.filler, 0), + "New York Steak": ItemData("Consumables", 0x020C, ItemClassification.filler, 0), + "Rice Ball": ItemData("Consumables", 0x020D, ItemClassification.filler, 0), + "Mushroom": ItemData("Consumables", 0x020E, ItemClassification.filler, 0), + "Corn Soup": ItemData("Consumables", 0x020F, ItemClassification.filler, 0), + "Curry": ItemData("Consumables", 0x0210, ItemClassification.filler, 0), + "Ramen Noodles": ItemData("Consumables", 0x0211, ItemClassification.filler, 0), + "Mint Sundae": ItemData("Consumables", 0x0212, ItemClassification.filler, 0), + "Cream Puff": ItemData("Consumables", 0x0213, ItemClassification.filler, 0), + "Milk": ItemData("Consumables", 0x0214, ItemClassification.filler, 0), + "Coffee": ItemData("Consumables", 0x0215, ItemClassification.filler, 0), + "Tea": ItemData("Consumables", 0x0216, ItemClassification.filler, 0), + "Pudding": ItemData("Consumables", 0x0217, ItemClassification.filler, 0), + "Strawberry": ItemData("Consumables", 0x0218, ItemClassification.filler, 0), + "Melon": ItemData("Consumables", 0x0219, ItemClassification.filler, 0), + "Grapes": ItemData("Consumables", 0x021A, ItemClassification.filler, 0), + "Hamburger": ItemData("Consumables", 0x021B, ItemClassification.filler, 0), + "Fried Chicken": ItemData("Consumables", 0x021C, ItemClassification.filler, 0), + "Hot Dog": ItemData("Consumables", 0x021D, ItemClassification.filler, 0), + "Pumpkin Pie": ItemData("Consumables", 0x021E, ItemClassification.filler, 0), + "Pancake": ItemData("Consumables", 0x021F, ItemClassification.filler, 0), + "Tinned Spinach": ItemData("Consumables", 0x0220, ItemClassification.filler, 0), + "Foie Gras": ItemData("Consumables", 0x0221, ItemClassification.filler, 0), + "Caviar": ItemData("Consumables", 0x0222, ItemClassification.filler, 0), + "Truffle": ItemData("Consumables", 0x0223, ItemClassification.filler, 0), + "Paella": ItemData("Consumables", 0x0224, ItemClassification.filler, 0), + "Spaghetti": ItemData("Consumables", 0x0225, ItemClassification.filler, 0), + "Ice Cream": ItemData("Consumables", 0x0226, ItemClassification.filler, 0), + "Meuniere": ItemData("Consumables", 0x0227, ItemClassification.filler, 0), + "Boiled Starfish": ItemData("Consumables", 0x0228, ItemClassification.filler, 0), + "Wheat Roll": ItemData("Consumables", 0x0229, ItemClassification.filler, 0), + "Sachertorte": ItemData("Consumables", 0x022A, ItemClassification.filler, 0), + "NY Cheesecake": ItemData("Consumables", 0x022B, ItemClassification.filler, 0), + "Mille-feuille": ItemData("Consumables", 0x022C, ItemClassification.filler, 0), + "Tarte au Poire": ItemData("Consumables", 0x022D, ItemClassification.filler, 0), + "Gateau Fraise": ItemData("Consumables", 0x022E, ItemClassification.filler, 0), + "Kugelhopf": ItemData("Consumables", 0x022F, ItemClassification.filler, 0), + "Green Tea Cake": ItemData("Consumables", 0x0230, ItemClassification.filler, 0), + "Gateau Marron": ItemData("Consumables", 0x0231, ItemClassification.filler, 0), + "Langues de Chat": ItemData("Consumables", 0x0232, ItemClassification.filler, 0), + "Financier": ItemData("Consumables", 0x0233, ItemClassification.filler, 0), + "Tropical Sundae": ItemData("Consumables", 0x0234, ItemClassification.filler, 0), + "Pudding Bucket": ItemData("Consumables", 0x0235, ItemClassification.filler, 0), + "Piyoko": ItemData("Consumables", 0x0236, ItemClassification.filler, 0), + "1000 Year Egg": ItemData("Consumables", 0x0237, ItemClassification.filler, 0), + "Jamon Iberico": ItemData("Consumables", 0x0238, ItemClassification.filler, 0), + "Spicy Shrimp": ItemData("Consumables", 0x0239, ItemClassification.filler, 0), + "Peking Duck": ItemData("Consumables", 0x023A, ItemClassification.filler, 0), + "Pasta Carbonara": ItemData("Consumables", 0x023B, ItemClassification.filler, 0), + "Penne Arrabiata": ItemData("Consumables", 0x023C, ItemClassification.filler, 0), + "Powdered Milk": ItemData("Consumables", 0x023D, ItemClassification.filler, 0), + "Rotten Durian": ItemData("Consumables", 0x023E, ItemClassification.trap, 0), + "Ground Meat": ItemData("Consumables", 0x023F, ItemClassification.filler, 0), + "Noodle Bun": ItemData("Consumables", 0x0240, ItemClassification.filler, 0), + "Touch & Pop": ItemData("Consumables", 0x0241, ItemClassification.filler, 0), + "Moldy Bread": ItemData("Consumables", 0x0242, ItemClassification.trap, 0), + "Penters Natural": ItemData("Consumables", 0x0243, ItemClassification.filler, 0), + "Birthday Cake": ItemData("Consumables", 0x0244, ItemClassification.filler, 0), + # "Magical Ticket": ItemData("Consumables", 0x0245, ItemClassification.filler, 0), We have infinite of these, AP doesn't need them + "Beehive": ItemData("Consumables", 0x0246, ItemClassification.filler, 0), + "Amanita": ItemData("Consumables", 0x0247, ItemClassification.trap, 0), + "Rotten Meat": ItemData("Consumables", 0x0248, ItemClassification.trap, 0), + "Spoiled Milk": ItemData("Consumables", 0x0249, ItemClassification.trap, 0), + "Rusty Food Tin": ItemData("Consumables", 0x024A, ItemClassification.trap, 0), + "CASTLE MAP 1": ItemData("Key Items", 0x024B, ItemClassification.filler, 0), + "CASTLE MAP 2": ItemData("Key Items", 0x024C, ItemClassification.filler, 0), + "CASTLE MAP 3": ItemData("Key Items", 0x024D, ItemClassification.filler, 0), + "Cog": ItemData("Key Items", 0x024E, ItemClassification.progression), + # "AP Item": ItemData("Consumables", 0x024F, ItemClassification.filler, 0), + # "AP Item": ItemData("Consumables", 0x0250, ItemClassification.filler, 0), These are reserved for offworld items + # "Vic Viper": ItemData("Consumables", 0x0251, ItemClassification.filler, 0), + "Tome of Arms p1": ItemData("Key Items", 0x0252, ItemClassification.useful), + "Tome of Arms p2": ItemData("Key Items", 0x0253, ItemClassification.useful), + "Spade": ItemData("Key Items", 0x0254, ItemClassification.filler, 0), + "Diamond": ItemData("Key Items", 0x0255, ItemClassification.filler, 0), + "Heart": ItemData("Key Items", 0x0256, ItemClassification.filler, 0), + "Club": ItemData("Key Items", 0x0257, ItemClassification.filler, 0), + "Joker": ItemData("Key Items", 0x0258, ItemClassification.filler, 0), + "Statue's Tear": ItemData("Key Items", 0x0259, ItemClassification.useful, 0), + "Record Player": ItemData("Key Items", 0x025A, ItemClassification.useful, 0), + "Record 1": ItemData("Key Items", 0x025B, ItemClassification.filler, 0), + "Record 2": ItemData("Key Items", 0x025C, ItemClassification.filler, 0), + "Record 3": ItemData("Key Items", 0x025D, ItemClassification.filler, 0), + "Record 4": ItemData("Key Items", 0x025E, ItemClassification.filler, 0), + "Record 5": ItemData("Key Items", 0x025F, ItemClassification.filler, 0), + + "Vampire Killer": ItemData("Whips", 0x0301, ItemClassification.useful, 0), # The starter copy + "Leather Whip": ItemData("Whips", 0x0302, ItemClassification.filler, 0), + "Steel Whip": ItemData("Whips", 0x0303, ItemClassification.filler, 0), + "Rose Stem Whip": ItemData("Whips", 0x0304, ItemClassification.filler, 0), + "Medusa Whip": ItemData("Whips", 0x0305, ItemClassification.filler, 0), + "Nebula": ItemData("Whips", 0x0306, ItemClassification.filler, 0), + "Flame Whip": ItemData("Whips", 0x0307, ItemClassification.filler, 0), + "Jet Black Whip": ItemData("Whips", 0x0308, ItemClassification.filler, 0), + "Undead Killer": ItemData("Whips", 0x0309, ItemClassification.filler, 0), + "True Vampire Killer": ItemData("Whips", 0x030A, ItemClassification.useful, 0), + "Richter Vampire Killer": ItemData("Whips", 0x030B, ItemClassification.filler, 0), # Used for richter mode + "Encyclopedia": ItemData("Books", 0x030C, ItemClassification.filler, 0), + "Blank Book": ItemData("Books", 0x030D, ItemClassification.filler, 0), + "Tristan&Isolde": ItemData("Books", 0x030E, ItemClassification.filler, 0), + "Don Quixote": ItemData("Books", 0x030F, ItemClassification.filler, 0), + "Book of Death": ItemData("Books", 0x0310, ItemClassification.filler, 0), + "Tome of Arms 1": ItemData("Books", 0x0311, ItemClassification.filler, 0), + "Tome of Arms 2": ItemData("Books", 0x0312, ItemClassification.filler, 0), + "Tome of Arms 3": ItemData("Books", 0x0313, ItemClassification.filler, 0), + "Tome of Arms X": ItemData("Books", 0x0314, ItemClassification.filler, 0), + "Knife": ItemData("Knives", 0x0315, ItemClassification.filler, 0), + "Combat Knife": ItemData("Knives", 0x0316, ItemClassification.filler, 0), + "Baselard": ItemData("Knives", 0x0317, ItemClassification.filler, 0), + "Katar": ItemData("Knives", 0x0318, ItemClassification.filler, 0), + "Cinquedea": ItemData("Knives", 0x319, ItemClassification.filler, 0), + "Short Sword": ItemData("Swords", 0x031A, ItemClassification.filler, 0), + "Cutlass": ItemData("Swords", 0x031B, ItemClassification.filler, 0), + "Long Sword": ItemData("Swords", 0x031C, ItemClassification.filler, 0), + "Gladius": ItemData("Swords", 0x031D, ItemClassification.filler, 0), + "Milican's Sword": ItemData("Swords", 0x031E, ItemClassification.filler, 0), + "Rahab's Frost": ItemData("Swords", 0x031F, ItemClassification.filler, 0), + "Agni's Flame": ItemData("Swords", 0x0320, ItemClassification.filler, 0), + "Assassin Blade": ItemData("Swords", 0x0321, ItemClassification.filler, 0), + "Heaven's Sword": ItemData("Swords", 0x0322, ItemClassification.filler, 0), + "Bakatwa": ItemData("Swords", 0x0323, ItemClassification.filler, 0), + "Jagdplaute": ItemData("Swords", 0x0324, ItemClassification.filler, 0), + "Stellar Sword": ItemData("Swords", 0x0325, ItemClassification.filler, 0), + "Damascus Sword": ItemData("Swords", 0x0326, ItemClassification.filler, 0), + "Claymore": ItemData("Greatswords", 0x0327, ItemClassification.filler, 0), + "Falchion": ItemData("Greatswords", 0x0328, ItemClassification.filler, 0), + "Great Sword": ItemData("Greatswords", 0x0329, ItemClassification.filler, 0), + "Zweihander": ItemData("Greatswords", 0x032A, ItemClassification.filler, 0), + "Royal Sword": ItemData("Greatswords", 0x032B, ItemClassification.filler, 0), + "Dragon Slayer": ItemData("Greatswords", 0x032C, ItemClassification.filler, 0), + "Final Sword": ItemData("Greatswords", 0x032D, ItemClassification.filler, 0), + "Holy Claymore": ItemData("Greatswords", 0x032E, ItemClassification.filler, 0), + "Spear": ItemData("Spears", 0x032F, ItemClassification.filler, 0), + "Partisan": ItemData("Spears", 0x0330, ItemClassification.filler, 0), + "Halberd": ItemData("Spears", 0x0331, ItemClassification.filler, 0), + "Lance": ItemData("Spears", 0x0332, ItemClassification.filler, 0), + "Trident": ItemData("Spears", 0x0333, ItemClassification.filler, 0), + "Long Spear": ItemData("Spears", 0x0334, ItemClassification.filler, 0), + "Couse": ItemData("Spears", 0x0335, ItemClassification.filler, 0), + "Sarissa": ItemData("Spears", 0x0336, ItemClassification.filler, 0), + "Alucard's Spear": ItemData("Spears", 0x0337, ItemClassification.filler, 0), + "Axe": ItemData("Axes", 0x0338, ItemClassification.filler, 0), + "Mace": ItemData("Axes", 0x0339, ItemClassification.filler, 0), + "Battle Axe": ItemData("Axes", 0x033A, ItemClassification.filler, 0), + "Morning Star": ItemData("Axes", 0x033B, ItemClassification.filler, 0), + "Bhuj": ItemData("Axes", 0x033C, ItemClassification.filler, 0), + "Great Axe": ItemData("Axes", 0x033D, ItemClassification.filler, 0), + "Voulge": ItemData("Axes", 0x033E, ItemClassification.filler, 0), + "Bullova": ItemData("Axes", 0x033F, ItemClassification.filler, 0), + "Golden Axe": ItemData("Axes", 0x0340, ItemClassification.filler, 0), + "Brass Knuckles": ItemData("Fists", 0x0341, ItemClassification.filler, 0), + "Cestus": ItemData("Fists", 0x0342, ItemClassification.filler, 0), + "Whip Knuckles": ItemData("Fists", 0x0343, ItemClassification.filler, 0), + "Bahg Nahk": ItemData("Fists", 0x0344, ItemClassification.filler, 0), + "Bullet Punch": ItemData("Fists", 0x0345, ItemClassification.filler, 0), + "Illusion Fist": ItemData("Fists", 0x0346, ItemClassification.filler, 0), + "Kaiser Knuckles": ItemData("Fists", 0x0347, ItemClassification.filler, 0), + "Tori": ItemData("Other Weapons", 0x0348, ItemClassification.filler, 0), + + + + + "Dummy": ItemData("Dummy", 0x, ItemClassification.filler, 0), diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py index 88eb8fe03784..6cb9be0a9587 100644 --- a/worlds/cv_por/__init__.py +++ b/worlds/cv_por/__init__.py @@ -29,7 +29,9 @@ class RomFile(settings.UserFilePath): md5 = "2edd57540cae45842fbd19c45a4214f9" class PoRWorld(World): - """Placeholder text""" + """The year is 1944. Dracula's castle has reappeared, and it's been taken over + by the mad vampire artist, Brauner, and his two twin daughters. Explore the + castle and Brauner's magical portraits with two characters and put a stop to him!""" game = "Castlevania: Portrait of Ruin" option_definitions = PoROptions data_version = 1 From f03d4dee9aa6a9dcdc6b2b8491909e5544269a41 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Mon, 13 Apr 2026 22:08:33 -0500 Subject: [PATCH 04/58] Finish adding items --- worlds/cv_por/Items.py | 284 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 283 insertions(+), 1 deletion(-) diff --git a/worlds/cv_por/Items.py b/worlds/cv_por/Items.py index 63a02704f251..a09659c34c6e 100644 --- a/worlds/cv_por/Items.py +++ b/worlds/cv_por/Items.py @@ -186,7 +186,289 @@ class ItemData(NamedTuple): "Kaiser Knuckles": ItemData("Fists", 0x0347, ItemClassification.filler, 0), "Tori": ItemData("Other Weapons", 0x0348, ItemClassification.filler, 0), + "Casual Clothes": ItemData("Body Armor", 0x0401, ItemClassification.filler, 0), + "Hobo's Clothes": ItemData("Body Armor", 0x0402, ItemClassification.filler, 0), + "Houppelande": ItemData("Body Armor", 0x0403, ItemClassification.filler, 0), + "Poncho": ItemData("Body Armor", 0x0404, ItemClassification.filler, 0), + "Combat Fatigues": ItemData("Body Armor", 0x0405, ItemClassification.filler, 0), + "Adrenaline Gear": ItemData("Body Armor", 0x0406, ItemClassification.filler, 0), + "Kalasiris": ItemData("Body Armor", 0x0407, ItemClassification.filler, 0), + "Feather Gear": ItemData("Body Armor", 0x0408, ItemClassification.filler, 0), + "Tailcoat": ItemData("Jonathan Armor", 0x0409, ItemClassification.filler, 0), + "Biker's Jacket": ItemData("Jonathan Armor", 0x040A, ItemClassification.filler, 0), + "Justaucorps": ItemData("Jonathan Armor", 0x040B, ItemClassification.filler, 0), + "Tuxedo Coat": ItemData("Jonathan Armor", 0x040C, ItemClassification.filler, 0), + "Battle Jacket": ItemData("Jonathan Armor", 0x040D, ItemClassification.filler, 0), + "Surcoat": ItemData("Jonathan Armor", 0x040E, ItemClassification.filler, 0), + "Leather Cuirass": ItemData("Jonathan Armor", 0x040F, ItemClassification.filler, 0), + "Copper Plate": ItemData("Jonathan Armor", 0x0410, ItemClassification.filler, 0), + "Iron Plate": ItemData("Jonathan Armor", 0x0411, ItemClassification.filler, 0), + "Silver Plate": ItemData("Jonathan Armor", 0x0412, ItemClassification.filler, 0), + "Gold Plate": ItemData("Jonathan Armor", 0x0413, ItemClassification.filler, 0), + "Lorica": ItemData("Jonathan Armor", 0x0414, ItemClassification.filler, 0), + "Platinum Plate": ItemData("Jonathan Armor", 0x0415, ItemClassification.filler, 0), + "Samurai Plate": ItemData("Jonathan Armor", 0x0416, ItemClassification.filler, 0), + "Holy Mail": ItemData("Jonathan Armor", 0x0417, ItemClassification.filler, 0), + "Berserker Mail": ItemData("Jonathan Armor", 0x0418, ItemClassification.filler, 0), + "Ancient Armor": ItemData("Jonathan Armor", 0x0419, ItemClassification.filler, 0), + "Healing Mail": ItemData("Jonathan Armor", 0x041A, ItemClassification.filler, 0), + "Mirror Cuirass": ItemData("Jonathan Armor", 0x041B, ItemClassification.filler, 0), + "Spiked Mail": ItemData("Jonathan Armor", 0x041C, ItemClassification.filler, 0), + "Scout Armor": ItemData("Jonathan Armor", 0x041D, ItemClassification.filler, 0), + "Heavy Armor": ItemData("Jonathan Armor", 0x041E, ItemClassification.filler, 0), + "Impervious Mail": ItemData("Jonathan Armor", 0x041F, ItemClassification.filler, 0), + "Leather Corset": ItemData("Charlotte Armor", 0x0420, ItemClassification.filler, 0), + "Jade Corset": ItemData("Charlotte Armor", 0x0421, ItemClassification.filler, 0), + "Amethyst Corset": ItemData("Charlotte Armor", 0x0422, ItemClassification.filler, 0), + "Emerald Corset": ItemData("Charlotte Armor", 0x0423, ItemClassification.filler, 0), + "Ruby Corset": ItemData("Charlotte Armor", 0x0424, ItemClassification.filler, 0), + "Sapphire Corset": ItemData("Charlotte Armor", 0x0425, ItemClassification.filler, 0), + "Lilith Corset": ItemData("Charlotte Armor", 0x0426, ItemClassification.filler, 0), + "Platinum Corset": ItemData("Charlotte Armor", 0x0427, ItemClassification.filler, 0), + "Diamond Corset": ItemData("Charlotte Armor", 0x0428, ItemClassification.filler, 0), + "Kirtle": ItemData("Charlotte Armor", 0x0429, ItemClassification.filler, 0), + "Miko Dress": ItemData("Charlotte Armor", 0x042A, ItemClassification.filler, 0), + "Cotton Apron": ItemData("Charlotte Armor", 0x042B, ItemClassification.filler, 0), + "Silk Negligee": ItemData("Charlotte Armor", 0x042C, ItemClassification.filler, 0), + "Bathrobe": ItemData("Charlotte Armor", 0x042D, ItemClassification.filler, 0), + "Frilly Camisole": ItemData("Charlotte Armor", 0x042E, ItemClassification.filler, 0), + "Sequined Dress": ItemData("Charlotte Armor", 0x042F, ItemClassification.filler, 0), + "Clown Shirt": ItemData("Charlotte Armor", 0x0430, ItemClassification.filler, 0), + "Nun's Robes": ItemData("Charlotte Armor", 0x0431, ItemClassification.filler, 0), + "Cocktail Dress": ItemData("Charlotte Armor", 0x0432, ItemClassification.filler, 0), + "Dancer's Blouse": ItemData("Charlotte Armor", 0x0433, ItemClassification.filler, 0), + "Cotehardie": ItemData("Charlotte Armor", 0x0434, ItemClassification.filler, 0), + "Wedding Dress": ItemData("Charlotte Armor", 0x0435, ItemClassification.filler, 0), + "Europa's Dress": ItemData("Charlotte Armor", 0x0436, ItemClassification.filler, 0), + "Dalmatica": ItemData("Charlotte Armor", 0x0437, ItemClassification.filler, 0), + "Princess Coat": ItemData("Charlotte Armor", 0x0438, ItemClassification.filler, 0), + "Robe Decollete": ItemData("Charlotte Armor", 0x0439, ItemClassification.filler, 0), + "Eye for Decay": ItemData("Headgear", 0x0501, ItemClassification.filler, 0), + "Gambler Glasses": ItemData("Headgear", 0x0502, ItemClassification.filler, 0), + "Sunglasses": ItemData("Headgear", 0x0503, ItemClassification.filler, 0), + "Thick Glasses": ItemData("Headgear", 0x0504, ItemClassification.filler, 0), + "Glasses": ItemData("Headgear", 0x0505, ItemClassification.filler, 0), + "Monocole": ItemData("Headgear", 0x0506, ItemClassification.filler, 0), + "Goggles": ItemData("Headgear", 0x0507, ItemClassification.filler, 0), + "Combat Beret": ItemData("Headgear", 0x0508, ItemClassification.filler, 0), + "Uraeus": ItemData("Headgear", 0x0509, ItemClassification.filler, 0), + "Royal Crown": ItemData("Headgear", 0x050A, ItemClassification.filler, 0), + "Headguard": ItemData("Jonathan Headgear", 0x050B, ItemClassification.filler, 0), + "Bandana": ItemData("Jonathan Headgear", 0x050C, ItemClassification.filler, 0), + "Iron Helmet": ItemData("Jonathan Headgear", 0x050D, ItemClassification.filler, 0), + "Stone Mask": ItemData("Jonathan Headgear", 0x050E, ItemClassification.filler, 0), + "Viking Helmt": ItemData("Jonathan Headgear", 0x050F, ItemClassification.filler, 0), + "Ballroom Masque": ItemData("Jonathan Headgear", 0x0510, ItemClassification.filler, 0), + "Fedora": ItemData("Jonathan Headgear", 0x0511, ItemClassification.filler, 0), + "Porkpie Hat": ItemData("Jonathan Headgear", 0x0512, ItemClassification.filler, 0), + "Silk Hat": ItemData("Jonathan Headgear", 0x0513, ItemClassification.filler, 0), + "Traveler's Hat": ItemData("Headgear", 0x0514, ItemClassification.filler, 0), + "Attica Helmet": ItemData("Jonathan Headgear", 0x0515, ItemClassification.filler, 0), + "Samurai Helmet": ItemData("Jonathan Headgear", 0x0516, ItemClassification.filler, 0), + "Shogun Helmet": ItemData("Jonathan Headgear", 0x0517, ItemClassification.filler, 0), + "Skull Mask": ItemData("Jonathan Headgear", 0x0518, ItemClassification.filler, 0), + "Velvet Ribbon": ItemData("Charlotte Headgear", 0x0519, ItemClassification.filler, 0), + "Witch's Hat": ItemData("Charlotte Headgear", 0x051A, ItemClassification.filler, 0), + "Mourning Veil": ItemData("Charlotte Headgear", 0x051B, ItemClassification.filler, 0), + "Nun's Habit": ItemData("Charlotte Headgear", 0x051C, ItemClassification.filler, 0), + "Open Veil": ItemData("Charlotte Headgear", 0x051D, ItemClassification.filler, 0), + "Holy Veil": ItemData("Charlotte Headgear", 0x051E, ItemClassification.filler, 0), + "Clover Tiara": ItemData("Charlotte Headgear", 0x051F, ItemClassification.filler, 0), + "Pearl Tiara": ItemData("Charlotte Headgear", 0x0520, ItemClassification.filler, 0), + "Diamond Tiara": ItemData("Charlotte Headgear", 0x0521, ItemClassification.filler, 0), + "Princess Tiara": ItemData("Charlotte Headgear", 0x0522, ItemClassification.filler, 0), + "Muse's Tiara": ItemData("Charlotte Headgear", 0x0523, ItemClassification.filler, 0), + "Midnight Tiara": ItemData("Charlotte Headgear", 0x0524, ItemClassification.filler, 0), + "Arachne Hennin": ItemData("Charlotte Headgear", 0x0525, ItemClassification.filler, 0), + "Sandals": ItemData("Boots", 0x0601, ItemClassification.filler, 0), + "Engineer Boots": ItemData("Boots", 0x0602, ItemClassification.filler, 0), + "Royal Sandals": ItemData("Boots", 0x0603, ItemClassification.filler, 0), + "Combat Boots": ItemData("Boots", 0x0604, ItemClassification.filler, 0), + "Moonwalkers": ItemData("Boots", 0x0605, ItemClassification.filler, 0), + "Slick Boots": ItemData("Boots", 0x0606, ItemClassification.filler, 0), + "Spiked Boots": ItemData("Boots", 0x0607, ItemClassification.filler, 0), + "Mercury Boots": ItemData("Boots", 0x0608, ItemClassification.filler, 0), + "Winged Boots": ItemData("Boots", 0x0609, ItemClassification.filler, 0), + "Hiking Boots": ItemData("Jonathan Boots", 0x060A, ItemClassification.filler, 0), + "Oxfords": ItemData("Jonathan Boots", 0x060B, ItemClassification.filler, 0), + "Wingtips": ItemData("Jonathan Boots", 0x060C, ItemClassification.filler, 0), + "Iron Leggings": ItemData("Jonathan Boots", 0x060D, ItemClassification.filler, 0), + "Silver Leggings": ItemData("Jonathan Boots", 0x060E, ItemClassification.filler, 0), + "Gold Leggings": ItemData("Jonathan Boots", 0x060F, ItemClassification.filler, 0), + "Steel Greaves": ItemData("Jonathan Boots", 0x0610, ItemClassification.filler, 0), + "Platinum Lggngs": ItemData("Jonathan Boots", 0x0611, ItemClassification.filler, 0), + "Samurai Leggngs": ItemData("Jonathan Boots", 0x0612, ItemClassification.filler, 0), + "Enamel Pinheels": ItemData("Charlotte Boots", 0x0613, ItemClassification.filler, 0), + "Inuit Boots": ItemData("Charlotte Boots", 0x0614, ItemClassification.filler, 0), + "Succubus Boots": ItemData("Charlotte Boots", 0x0615, ItemClassification.filler, 0), + "Glamorous Pumps": ItemData("Charlotte Boots", 0x0616, ItemClassification.filler, 0), + "Silent Sandals": ItemData("Charlotte Boots", 0x0617, ItemClassification.filler, 0), + "Artemis Shoes": ItemData("Charlotte Boots", 0x0618, ItemClassification.filler, 0), + "Nun's Shoes": ItemData("Charlotte Boots", 0x0619, ItemClassification.filler, 0), + "Glass Shoes": ItemData("Charlotte Boots", 0x061A, ItemClassification.filler, 0), + "Prima Shoes": ItemData("Charlotte Boots", 0x061B, ItemClassification.filler, 0), + "Diamond Shoes": ItemData("Charlotte Boots", 0x061C, ItemClassification.filler, 0), - "Dummy": ItemData("Dummy", 0x, ItemClassification.filler, 0), + "Studded Choker": ItemData("Accessories", 0x0701, ItemClassification.filler, 0), + "Charm Necklace": ItemData("Accessories", 0x0702, ItemClassification.filler, 0), + "Stella's Locket": ItemData("Key Items", 0x0703, ItemClassification.progression), + "Astral Ring": ItemData("Accessories", 0x0704, ItemClassification.filler, 0), + "Engagement Ring": ItemData("Accessories", 0x0705, ItemClassification.filler, 0), + "Magus Ring": ItemData("Accessories", 0x0706, ItemClassification.filler, 0), + "Gold Ring": ItemData("Accessories", 0x0707, ItemClassification.filler, 0), + "Shaman Ring": ItemData("Accessories", 0x0708, ItemClassification.filler, 0), + "Miser Ring": ItemData("Accessories", 0x0709, ItemClassification.filler, 0), + "Sage Ring": ItemData("Accessories", 0x070A, ItemClassification.filler, 0), + "Thief Ring": ItemData("Accessories", 0x070B, ItemClassification.filler, 0), + "Pearl Ring": ItemData("Accessories", 0x070C, ItemClassification.filler, 0), + "Tough Ring": ItemData("Accessories", 0x070D, ItemClassification.filler, 0), + "Master Ring": ItemData("Accessories", 0x070E, ItemClassification.filler, 0), + "Skull Ring": ItemData("Accessories", 0x070F, ItemClassification.filler, 0), + "Immunity Ring": ItemData("Accessories", 0x0710, ItemClassification.filler, 0), + "Blessed Ring": ItemData("Accessories", 0x0711, ItemClassification.filler, 0), + "Hercules Ring": ItemData("Accessories", 0x0712, ItemClassification.filler, 0), + "Cape": ItemData("Accessories", 0x0713, ItemClassification.filler, 0), + "Hero's Cape": ItemData("Accessories", 0x0714, ItemClassification.filler, 0), + "Blue Cape": ItemData("Accessories", 0x0715, ItemClassification.filler, 0), + "Elven Cape": ItemData("Accessories", 0x0716, ItemClassification.filler, 0), + "Assassin's Cape": ItemData("Accessories", 0x0717, ItemClassification.filler, 0), + "Invisible Cape": ItemData("Accessories", 0x0718, ItemClassification.filler, 0), + "Holy Mantle": ItemData("Accessories", 0x0719, ItemClassification.filler, 0), + "Paludamentum": ItemData("Accessories", 0x071A, ItemClassification.filler, 0), + "Blessed Ankh": ItemData("Accessories", 0x071B, ItemClassification.filler, 0), + "White Corsage": ItemData("Accessories", 0x071C, ItemClassification.filler, 0), + "Orchid Corsage": ItemData("Accessories", 0x071D, ItemClassification.filler, 0), + "Rose Corsage": ItemData("Accessories", 0x071E, ItemClassification.filler, 0), + "Sorceress Crest": ItemData("Accessories", 0x071F, ItemClassification.filler, 0), + "Forget-Me-Not": ItemData("Accessories", 0x0720, ItemClassification.filler, 0), + "Crystal Necklet": ItemData("Accessories", 0x0721, ItemClassification.filler, 0), + "Platinum Chain": ItemData("Accessories", 0x0722, ItemClassification.filler, 0), + "Heart Brooch": ItemData("Accessories", 0x0723, ItemClassification.filler, 0), + "Astral Brooch": ItemData("Accessories", 0x0724, ItemClassification.filler, 0), + "Laurel Brooch": ItemData("Accessories", 0x0725, ItemClassification.filler, 0), + "Abalone Brooch": ItemData("Accessories", 0x0726, ItemClassification.filler, 0), + "Diamond Brooch": ItemData("Accessories", 0x0727, ItemClassification.filler, 0), + "Moon Brooch": ItemData("Accessories", 0x0728, ItemClassification.filler, 0), + "Venus Brooch": ItemData("Accessories", 0x0729, ItemClassification.filler, 0), + + "Puppet Master": ItemData("Subweapons", 0x0801, ItemClassification.progression, 0), + "R. Axe": ItemData("Subweapons", 0x0802, ItemClassification.filler, 0), + "R. Cross": ItemData("Subweapons", 0x0803, ItemClassification.filler, 0), + "R. Holy Water": ItemData("Subweapons", 0x0804, ItemClassification.filler, 0), + "Julius Cross": ItemData("Subweapons", 0x0805, ItemClassification.filler, 0), + "Seiryu": ItemData("Subweapons", 0x0806, ItemClassification.filler, 0), + "Suzaku": ItemData("Subweapons", 0x0807, ItemClassification.filler, 0), + "Byakko": ItemData("Subweapons", 0x0808, ItemClassification.filler, 0), + "Gnebu": ItemData("Subweapons", 0x0809, ItemClassification.filler, 0), + "Knife": ItemData("Subweapons", 0x080A, ItemClassification.filler, 0), + "Axe": ItemData("Subweapons", 0x080B, ItemClassification.filler, 0), + "Cross": ItemData("Subweapons", 0x080C, ItemClassification.filler, 0), + "Holy Water": ItemData("Subweapons", 0x080D, ItemClassification.filler, 0), + "Bible": ItemData("Subweapons", 0x080E, ItemClassification.filler, 0), + "Javelin": ItemData("Subweapons", 0x080F, ItemClassification.filler, 0), + "Ricochet Rock": ItemData("Subweapons", 0x0810, ItemClassification.filler, 0), + "Boomerang": ItemData("Subweapons", 0x0811, ItemClassification.filler, 0), + "Bwaka Knife": ItemData("Subweapons", 0x0812, ItemClassification.filler, 0), + "Shuriken": ItemData("Subweapons", 0x0813, ItemClassification.filler, 0), + "Yagyu Shuriken": ItemData("Subweapons", 0x0814, ItemClassification.filler, 0), + "Discus": ItemData("Subweapons", 0x0815, ItemClassification.filler, 0), + "Kunimitsu": ItemData("Subweapons", 0x0816, ItemClassification.filler, 0), + "Kunai": ItemData("Subweapons", 0x0817, ItemClassification.filler, 0), + "Paper Airplane": ItemData("Subweapons", 0x0818, ItemClassification.filler, 0), + "Cream Pie": ItemData("Subweapons", 0x0819, ItemClassification.filler, 0), + "Crossbow": ItemData("Subweapons", 0x081A, ItemClassification.filler, 0), + "Dart": ItemData("Subweapons", 0x081B, ItemClassification.filler, 0), + "Grenade": ItemData("Subweapons", 0x081C, ItemClassification.filler, 0), + "Steel Ball": ItemData("Subweapons", 0x081D, ItemClassification.filler, 0), + "Stonewall": ItemData("Subweapons", 0x081E, ItemClassification.filler, 0), + "Offensive Form": ItemData("Subweapons", 0x081F, ItemClassification.filler, 0), + "Defensive Form": ItemData("Subweapons", 0x0820, ItemClassification.filler, 0), + "Taunt": ItemData("Subweapons", 0x0821, ItemClassification.filler, 0), + "Wrecking Ball": ItemData("Subweapons", 0x0822, ItemClassification.filler, 0), + "Rampage": ItemData("Subweapons", 0x0823, ItemClassification.filler, 0), + "Knee Strike": ItemData("Subweapons", 0x0824, ItemClassification.filler, 0), + "Aura Blast": ItemData("Subweapons", 0x0825, ItemClassification.filler, 0), + "Rocket Slash": ItemData("Subweapons", 0x0826, ItemClassification.filler, 0), + + "Toad Morph": ItemData("Spells", 0x0827, ItemClassification.progression), + "Owl Morph": ItemData("Spells", 0x0828, ItemClassification.progression), + "Sanctuary": ItemData("Spells", 0x0829, ItemClassification.progression), + "Speed Up": ItemData("Spells", 0x082A, ItemClassification.filler, 0), + "Berserker": ItemData("Spells", 0x082B, ItemClassification.filler, 0), + "Eye for an Eye": ItemData("Spells", 0x082C, ItemClassification.filler, 0), + "Clear Skies": ItemData("Spells", 0x082D, ItemClassification.filler, 0), + "Time Stop": ItemData("Spells", 0x082E, ItemClassification.filler, 0), + "Heal": ItemData("Spells", 0x082F, ItemClassification.filler, 0), + "Cure Poison": ItemData("Spells", 0x0830, ItemClassification.filler, 0), + "Cure Curse": ItemData("Spells", 0x0831, ItemClassification.filler, 0), + "STR Boost": ItemData("Spells", 0x0832, ItemClassification.filler, 0), + "CON Boost": ItemData("Spells", 0x0833, ItemClassification.filler, 0), + "INT Boost": ItemData("Spells", 0x0834, ItemClassification.filler, 0), + "MIND Boost": ItemData("Spells", 0x0835, ItemClassification.filler, 0), + "LUCK Boost": ItemData("Spells", 0x0836, ItemClassification.filler, 0), + "ALL Boost": ItemData("Spells", 0x0837, ItemClassification.filler, 0), + "Gale Force": ItemData("Spells", 0x0838, ItemClassification.filler, 0), + "Rock Riot": ItemData("Spells", 0x0839, ItemClassification.filler, 0), + "Raging Fire": ItemData("Spells", 0x083A, ItemClassification.filler, 0), + "Ice Fang": ItemData("Spells", 0x083B, ItemClassification.filler, 0), + "Thunderbolt": ItemData("Spells", 0x083C, ItemClassification.filler, 0), + "Spirit of Light": ItemData("Spells", 0x083D, ItemClassification.filler, 0), + "Dark Rift": ItemData("Spells", 0x083E, ItemClassification.filler, 0), + "Tempest": ItemData("Spells", 0x083F, ItemClassification.filler, 0), + "Stone Circle": ItemData("Spells", 0x0840, ItemClassification.filler, 0), + "Ice Needle": ItemData("Spells", 0x0841, ItemClassification.filler, 0), + "Explosion": ItemData("Spells", 0x0842, ItemClassification.filler, 0), + "Chain Lightning": ItemData("Spells", 0x0843, ItemClassification.filler, 0), + "Piercing Beam": ItemData("Spells", 0x0844, ItemClassification.filler, 0), + "Nightmare": ItemData("Spells", 0x0845, ItemClassification.filler, 0), + "Summon Medusa": ItemData("Spells", 0x0846, ItemClassification.filler, 0), + "Acidic Bubbles": ItemData("Spells", 0x0847, ItemClassification.filler, 0), + "Hex": ItemData("Spells", 0x0848, ItemClassification.filler, 0), + "Salamander": ItemData("Spells", 0x0849, ItemClassification.filler, 0), + "Cocytus": ItemData("Spells", 0x084A, ItemClassification.filler, 0), + "Thor's Bellow": ItemData("Spells", 0x084B, ItemClassification.filler, 0), + "Summon Crow": ItemData("Spells", 0x084C, ItemClassification.filler, 0), + "Summon Skeleton": ItemData("Spells", 0x084D, ItemClassification.filler, 0), + "Summon Ghost": ItemData("Spells", 0x084E, ItemClassification.filler, 0), + "Summon Gunman": ItemData("Spells", 0x084F, ItemClassification.filler, 0), + "Summon Frog": ItemData("Spells", 0x0850, ItemClassification.filler, 0), + + "Rush": ItemData("Dual Crashes", 0x0852, ItemClassification.filler, 0), + "Holy Lightning": ItemData("Dual Crashes", 0x0853, ItemClassification.filler, 0), + "Axe Bomber": ItemData("Dual Crashes", 0x0854, ItemClassification.filler, 0), + "1,000 Blades": ItemData("Dual Crashes", 0x0855, ItemClassification.filler, 0), + "Volcano": ItemData("Dual Crashes", 0x0856, ItemClassification.filler, 0), + "Meteor": ItemData("Dual Crashes", 0x0857, ItemClassification.filler, 0), + "Grand Cruz": ItemData("Dual Crashes", 0x0858, ItemClassification.filler, 0), + "Divine Storm": ItemData("Dual Crashes", 0x0859, ItemClassification.filler, 0), + "Dark Gate": ItemData("Dual Crashes", 0x085A, ItemClassification.filler, 0), + "Greatest Five": ItemData("Dual Crashes", 0x085B, ItemClassification.filler, 0), + + "Change Cube": ItemData("Relics", 0x085C, ItemClassification.progression), + "Call Cube": ItemData("Relics", 0x085D, ItemClassification.progression), + "Skill Cube": ItemData("Relics", 0x085E, ItemClassification.useful), + "Wait Cube": ItemData("Relics", 0x085F, ItemClassification.progression), + "Acrobat Cube": ItemData("Relics", 0x0860, ItemClassification.progression), + "Push Cube": ItemData("Relics", 0x0861, ItemClassification.progression), + "Lizard Tail": ItemData("Relics", 0x0862, ItemClassification.progression), + "Stone of Flight": ItemData("Relics", 0x0863, ItemClassification.progression), + "Griffon Wing": ItemData("Relics", 0x0864, ItemClassification.progression), + "Strength Glove": ItemData("Relics", 0x0865, ItemClassification.progression), + "Spinning Art": ItemData("Relics", 0x0866, ItemClassification.useful, 0), + "Martial Art": ItemData("Relics", 0x0867, ItemClassification.useful, 0), + "Critical Art": ItemData("Relics", 0x0868, ItemClassification.useful), + "Whip Skill 1": ItemData("Relics", 0x0869, ItemClassification.useful), + "Whip Skill 2": ItemData("Relics", 0x086A, ItemClassification.useful), + "Book of Spirits": ItemData("Relics", 0x086B, ItemClassification.useful) +} + +def get_item_names_per_category() -> Dict[str, Set[str]]: + categories: Dict[str, Set[str]] = {} + + for name, data in item_table.items(): + if data.category != "Events": + categories.setdefault(data.category, set()).add(name) + + return categories From cbbb2cd1b4ee39560d8416e3a3340820b8c6c922 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Tue, 14 Apr 2026 22:27:50 -0500 Subject: [PATCH 05/58] Adjust amounts and classes --- worlds/cv_por/Items.py | 254 ++++++++++++++++++++--------------------- 1 file changed, 127 insertions(+), 127 deletions(-) diff --git a/worlds/cv_por/Items.py b/worlds/cv_por/Items.py index a09659c34c6e..8f7efb56717e 100644 --- a/worlds/cv_por/Items.py +++ b/worlds/cv_por/Items.py @@ -24,8 +24,8 @@ class ItemData(NamedTuple): "Super Tonic": ItemData("Consumables", 0x0205, ItemClassification.filler, 0), "Anti-Venom": ItemData("Consumables", 0x0206, ItemClassification.filler, 0), "Uncurse Potion": ItemData("Consumables", 0x0207, ItemClassification.filler, 0), - "HP Max up": ItemData("Upgrades", 0x0208, ItemClassification.useful, ???), - "MP Max up": ItemData("Upgrades", 0x0209, ItemClassification.useful, ???), + "HP Max up": ItemData("Upgrades", 0x0208, ItemClassification.useful, 16), + "MP Max up": ItemData("Upgrades", 0x0209, ItemClassification.useful, 16), "Beef Jerky": ItemData("Consumables", 0x020A, ItemClassification.filler, 0), "Tasty Meat": ItemData("Consumables", 0x020B, ItemClassification.filler, 0), "New York Steak": ItemData("Consumables", 0x020C, ItemClassification.filler, 0), @@ -118,21 +118,21 @@ class ItemData(NamedTuple): "Steel Whip": ItemData("Whips", 0x0303, ItemClassification.filler, 0), "Rose Stem Whip": ItemData("Whips", 0x0304, ItemClassification.filler, 0), "Medusa Whip": ItemData("Whips", 0x0305, ItemClassification.filler, 0), - "Nebula": ItemData("Whips", 0x0306, ItemClassification.filler, 0), - "Flame Whip": ItemData("Whips", 0x0307, ItemClassification.filler, 0), - "Jet Black Whip": ItemData("Whips", 0x0308, ItemClassification.filler, 0), - "Undead Killer": ItemData("Whips", 0x0309, ItemClassification.filler, 0), + "Nebula": ItemData("Whips", 0x0306, ItemClassification.useful), + "Flame Whip": ItemData("Whips", 0x0307, ItemClassification.useful), + "Jet Black Whip": ItemData("Whips", 0x0308, ItemClassification.useful), + "Undead Killer": ItemData("Whips", 0x0309, ItemClassification.useful, 0), "True Vampire Killer": ItemData("Whips", 0x030A, ItemClassification.useful, 0), "Richter Vampire Killer": ItemData("Whips", 0x030B, ItemClassification.filler, 0), # Used for richter mode "Encyclopedia": ItemData("Books", 0x030C, ItemClassification.filler, 0), "Blank Book": ItemData("Books", 0x030D, ItemClassification.filler, 0), - "Tristan&Isolde": ItemData("Books", 0x030E, ItemClassification.filler, 0), - "Don Quixote": ItemData("Books", 0x030F, ItemClassification.filler, 0), - "Book of Death": ItemData("Books", 0x0310, ItemClassification.filler, 0), - "Tome of Arms 1": ItemData("Books", 0x0311, ItemClassification.filler, 0), - "Tome of Arms 2": ItemData("Books", 0x0312, ItemClassification.filler, 0), - "Tome of Arms 3": ItemData("Books", 0x0313, ItemClassification.filler, 0), - "Tome of Arms X": ItemData("Books", 0x0314, ItemClassification.filler, 0), + "Tristan&Isolde": ItemData("Books", 0x030E, ItemClassification.useful), + "Don Quixote": ItemData("Books", 0x030F, ItemClassification.useful), + "Book of Death": ItemData("Books", 0x0310, ItemClassification.useful), + "Tome of Arms 1": ItemData("Books", 0x0311, ItemClassification.useful), + "Tome of Arms 2": ItemData("Books", 0x0312, ItemClassification.useful), + "Tome of Arms 3": ItemData("Books", 0x0313, ItemClassification.useful), + "Tome of Arms X": ItemData("Books", 0x0314, ItemClassification.useful, 0), "Knife": ItemData("Knives", 0x0315, ItemClassification.filler, 0), "Combat Knife": ItemData("Knives", 0x0316, ItemClassification.filler, 0), "Baselard": ItemData("Knives", 0x0317, ItemClassification.filler, 0), @@ -140,55 +140,55 @@ class ItemData(NamedTuple): "Cinquedea": ItemData("Knives", 0x319, ItemClassification.filler, 0), "Short Sword": ItemData("Swords", 0x031A, ItemClassification.filler, 0), "Cutlass": ItemData("Swords", 0x031B, ItemClassification.filler, 0), - "Long Sword": ItemData("Swords", 0x031C, ItemClassification.filler, 0), + "Long Sword": ItemData("Swords", 0x031C, ItemClassification.useful), "Gladius": ItemData("Swords", 0x031D, ItemClassification.filler, 0), - "Milican's Sword": ItemData("Swords", 0x031E, ItemClassification.filler, 0), + "Milican's Sword": ItemData("Swords", 0x031E, ItemClassification.useful), "Rahab's Frost": ItemData("Swords", 0x031F, ItemClassification.filler, 0), "Agni's Flame": ItemData("Swords", 0x0320, ItemClassification.filler, 0), - "Assassin Blade": ItemData("Swords", 0x0321, ItemClassification.filler, 0), + "Assassin Blade": ItemData("Swords", 0x0321, ItemClassification.useful, 0), "Heaven's Sword": ItemData("Swords", 0x0322, ItemClassification.filler, 0), - "Bakatwa": ItemData("Swords", 0x0323, ItemClassification.filler, 0), + "Bakatwa": ItemData("Swords", 0x0323, ItemClassification.useful), "Jagdplaute": ItemData("Swords", 0x0324, ItemClassification.filler, 0), - "Stellar Sword": ItemData("Swords", 0x0325, ItemClassification.filler, 0), + "Stellar Sword": ItemData("Swords", 0x0325, ItemClassification.useful, 0), "Damascus Sword": ItemData("Swords", 0x0326, ItemClassification.filler, 0), - "Claymore": ItemData("Greatswords", 0x0327, ItemClassification.filler, 0), + "Claymore": ItemData("Greatswords", 0x0327, ItemClassification.useful), "Falchion": ItemData("Greatswords", 0x0328, ItemClassification.filler, 0), "Great Sword": ItemData("Greatswords", 0x0329, ItemClassification.filler, 0), "Zweihander": ItemData("Greatswords", 0x032A, ItemClassification.filler, 0), - "Royal Sword": ItemData("Greatswords", 0x032B, ItemClassification.filler, 0), + "Royal Sword": ItemData("Greatswords", 0x032B, ItemClassification.useful, 0), "Dragon Slayer": ItemData("Greatswords", 0x032C, ItemClassification.filler, 0), "Final Sword": ItemData("Greatswords", 0x032D, ItemClassification.filler, 0), "Holy Claymore": ItemData("Greatswords", 0x032E, ItemClassification.filler, 0), "Spear": ItemData("Spears", 0x032F, ItemClassification.filler, 0), "Partisan": ItemData("Spears", 0x0330, ItemClassification.filler, 0), - "Halberd": ItemData("Spears", 0x0331, ItemClassification.filler, 0), + "Halberd": ItemData("Spears", 0x0331, ItemClassification.useful), "Lance": ItemData("Spears", 0x0332, ItemClassification.filler, 0), "Trident": ItemData("Spears", 0x0333, ItemClassification.filler, 0), "Long Spear": ItemData("Spears", 0x0334, ItemClassification.filler, 0), "Couse": ItemData("Spears", 0x0335, ItemClassification.filler, 0), "Sarissa": ItemData("Spears", 0x0336, ItemClassification.filler, 0), - "Alucard's Spear": ItemData("Spears", 0x0337, ItemClassification.filler, 0), + "Alucard's Spear": ItemData("Spears", 0x0337, ItemClassification.useful, 0), "Axe": ItemData("Axes", 0x0338, ItemClassification.filler, 0), "Mace": ItemData("Axes", 0x0339, ItemClassification.filler, 0), "Battle Axe": ItemData("Axes", 0x033A, ItemClassification.filler, 0), "Morning Star": ItemData("Axes", 0x033B, ItemClassification.filler, 0), "Bhuj": ItemData("Axes", 0x033C, ItemClassification.filler, 0), - "Great Axe": ItemData("Axes", 0x033D, ItemClassification.filler, 0), + "Great Axe": ItemData("Axes", 0x033D, ItemClassification.useful), "Voulge": ItemData("Axes", 0x033E, ItemClassification.filler, 0), "Bullova": ItemData("Axes", 0x033F, ItemClassification.filler, 0), "Golden Axe": ItemData("Axes", 0x0340, ItemClassification.filler, 0), "Brass Knuckles": ItemData("Fists", 0x0341, ItemClassification.filler, 0), "Cestus": ItemData("Fists", 0x0342, ItemClassification.filler, 0), - "Whip Knuckles": ItemData("Fists", 0x0343, ItemClassification.filler, 0), - "Bahg Nahk": ItemData("Fists", 0x0344, ItemClassification.filler, 0), - "Bullet Punch": ItemData("Fists", 0x0345, ItemClassification.filler, 0), + "Whip Knuckles": ItemData("Fists", 0x0343, ItemClassification.useful), + "Bahg Nahk": ItemData("Fists", 0x0344, ItemClassification.useful), + "Bullet Punch": ItemData("Fists", 0x0345, ItemClassification.useful, 0), "Illusion Fist": ItemData("Fists", 0x0346, ItemClassification.filler, 0), - "Kaiser Knuckles": ItemData("Fists", 0x0347, ItemClassification.filler, 0), + "Kaiser Knuckles": ItemData("Fists", 0x0347, ItemClassification.useful), "Tori": ItemData("Other Weapons", 0x0348, ItemClassification.filler, 0), "Casual Clothes": ItemData("Body Armor", 0x0401, ItemClassification.filler, 0), "Hobo's Clothes": ItemData("Body Armor", 0x0402, ItemClassification.filler, 0), - "Houppelande": ItemData("Body Armor", 0x0403, ItemClassification.filler, 0), + "Houppelande": ItemData("Body Armor", 0x0403, ItemClassification.useful), "Poncho": ItemData("Body Armor", 0x0404, ItemClassification.filler, 0), "Combat Fatigues": ItemData("Body Armor", 0x0405, ItemClassification.filler, 0), "Adrenaline Gear": ItemData("Body Armor", 0x0406, ItemClassification.filler, 0), @@ -197,21 +197,21 @@ class ItemData(NamedTuple): "Tailcoat": ItemData("Jonathan Armor", 0x0409, ItemClassification.filler, 0), "Biker's Jacket": ItemData("Jonathan Armor", 0x040A, ItemClassification.filler, 0), "Justaucorps": ItemData("Jonathan Armor", 0x040B, ItemClassification.filler, 0), - "Tuxedo Coat": ItemData("Jonathan Armor", 0x040C, ItemClassification.filler, 0), - "Battle Jacket": ItemData("Jonathan Armor", 0x040D, ItemClassification.filler, 0), + "Tuxedo Coat": ItemData("Jonathan Armor", 0x040C, ItemClassification.useful), + "Battle Jacket": ItemData("Jonathan Armor", 0x040D, ItemClassification.useful), "Surcoat": ItemData("Jonathan Armor", 0x040E, ItemClassification.filler, 0), "Leather Cuirass": ItemData("Jonathan Armor", 0x040F, ItemClassification.filler, 0), "Copper Plate": ItemData("Jonathan Armor", 0x0410, ItemClassification.filler, 0), "Iron Plate": ItemData("Jonathan Armor", 0x0411, ItemClassification.filler, 0), "Silver Plate": ItemData("Jonathan Armor", 0x0412, ItemClassification.filler, 0), "Gold Plate": ItemData("Jonathan Armor", 0x0413, ItemClassification.filler, 0), - "Lorica": ItemData("Jonathan Armor", 0x0414, ItemClassification.filler, 0), + "Lorica": ItemData("Jonathan Armor", 0x0414, ItemClassification.useful), "Platinum Plate": ItemData("Jonathan Armor", 0x0415, ItemClassification.filler, 0), "Samurai Plate": ItemData("Jonathan Armor", 0x0416, ItemClassification.filler, 0), - "Holy Mail": ItemData("Jonathan Armor", 0x0417, ItemClassification.filler, 0), + "Holy Mail": ItemData("Jonathan Armor", 0x0417, ItemClassification.useful), "Berserker Mail": ItemData("Jonathan Armor", 0x0418, ItemClassification.filler, 0), "Ancient Armor": ItemData("Jonathan Armor", 0x0419, ItemClassification.filler, 0), - "Healing Mail": ItemData("Jonathan Armor", 0x041A, ItemClassification.filler, 0), + "Healing Mail": ItemData("Jonathan Armor", 0x041A, ItemClassification.useful), "Mirror Cuirass": ItemData("Jonathan Armor", 0x041B, ItemClassification.filler, 0), "Spiked Mail": ItemData("Jonathan Armor", 0x041C, ItemClassification.filler, 0), "Scout Armor": ItemData("Jonathan Armor", 0x041D, ItemClassification.filler, 0), @@ -224,60 +224,60 @@ class ItemData(NamedTuple): "Ruby Corset": ItemData("Charlotte Armor", 0x0424, ItemClassification.filler, 0), "Sapphire Corset": ItemData("Charlotte Armor", 0x0425, ItemClassification.filler, 0), "Lilith Corset": ItemData("Charlotte Armor", 0x0426, ItemClassification.filler, 0), - "Platinum Corset": ItemData("Charlotte Armor", 0x0427, ItemClassification.filler, 0), + "Platinum Corset": ItemData("Charlotte Armor", 0x0427, ItemClassification.useful), "Diamond Corset": ItemData("Charlotte Armor", 0x0428, ItemClassification.filler, 0), - "Kirtle": ItemData("Charlotte Armor", 0x0429, ItemClassification.filler, 0), + "Kirtle": ItemData("Charlotte Armor", 0x0429, ItemClassification.useful), "Miko Dress": ItemData("Charlotte Armor", 0x042A, ItemClassification.filler, 0), "Cotton Apron": ItemData("Charlotte Armor", 0x042B, ItemClassification.filler, 0), "Silk Negligee": ItemData("Charlotte Armor", 0x042C, ItemClassification.filler, 0), - "Bathrobe": ItemData("Charlotte Armor", 0x042D, ItemClassification.filler, 0), + "Bathrobe": ItemData("Charlotte Armor", 0x042D, ItemClassification.useful), "Frilly Camisole": ItemData("Charlotte Armor", 0x042E, ItemClassification.filler, 0), - "Sequined Dress": ItemData("Charlotte Armor", 0x042F, ItemClassification.filler, 0), + "Sequined Dress": ItemData("Charlotte Armor", 0x042F, ItemClassification.useful), "Clown Shirt": ItemData("Charlotte Armor", 0x0430, ItemClassification.filler, 0), - "Nun's Robes": ItemData("Charlotte Armor", 0x0431, ItemClassification.filler, 0), + "Nun's Robes": ItemData("Charlotte Armor", 0x0431, ItemClassification.useful), "Cocktail Dress": ItemData("Charlotte Armor", 0x0432, ItemClassification.filler, 0), - "Dancer's Blouse": ItemData("Charlotte Armor", 0x0433, ItemClassification.filler, 0), + "Dancer's Blouse": ItemData("Charlotte Armor", 0x0433, ItemClassification.useful), "Cotehardie": ItemData("Charlotte Armor", 0x0434, ItemClassification.filler, 0), - "Wedding Dress": ItemData("Charlotte Armor", 0x0435, ItemClassification.filler, 0), + "Wedding Dress": ItemData("Charlotte Armor", 0x0435, ItemClassification.useful), "Europa's Dress": ItemData("Charlotte Armor", 0x0436, ItemClassification.filler, 0), "Dalmatica": ItemData("Charlotte Armor", 0x0437, ItemClassification.filler, 0), "Princess Coat": ItemData("Charlotte Armor", 0x0438, ItemClassification.filler, 0), - "Robe Decollete": ItemData("Charlotte Armor", 0x0439, ItemClassification.filler, 0), + "Robe Decollete": ItemData("Charlotte Armor", 0x0439, ItemClassification.useful), "Eye for Decay": ItemData("Headgear", 0x0501, ItemClassification.filler, 0), - "Gambler Glasses": ItemData("Headgear", 0x0502, ItemClassification.filler, 0), + "Gambler Glasses": ItemData("Headgear", 0x0502, ItemClassification.useful, 0), "Sunglasses": ItemData("Headgear", 0x0503, ItemClassification.filler, 0), - "Thick Glasses": ItemData("Headgear", 0x0504, ItemClassification.filler, 0), + "Thick Glasses": ItemData("Headgear", 0x0504, ItemClassification.useful), "Glasses": ItemData("Headgear", 0x0505, ItemClassification.filler, 0), - "Monocole": ItemData("Headgear", 0x0506, ItemClassification.filler, 0), + "Monocle": ItemData("Headgear", 0x0506, ItemClassification.filler, 0), "Goggles": ItemData("Headgear", 0x0507, ItemClassification.filler, 0), - "Combat Beret": ItemData("Headgear", 0x0508, ItemClassification.filler, 0), + "Combat Beret": ItemData("Headgear", 0x0508, ItemClassification.useful), "Uraeus": ItemData("Headgear", 0x0509, ItemClassification.filler, 0), "Royal Crown": ItemData("Headgear", 0x050A, ItemClassification.filler, 0), "Headguard": ItemData("Jonathan Headgear", 0x050B, ItemClassification.filler, 0), - "Bandana": ItemData("Jonathan Headgear", 0x050C, ItemClassification.filler, 0), + "Bandana": ItemData("Jonathan Headgear", 0x050C, ItemClassification.useful), "Iron Helmet": ItemData("Jonathan Headgear", 0x050D, ItemClassification.filler, 0), "Stone Mask": ItemData("Jonathan Headgear", 0x050E, ItemClassification.filler, 0), - "Viking Helmt": ItemData("Jonathan Headgear", 0x050F, ItemClassification.filler, 0), + "Viking Helmet": ItemData("Jonathan Headgear", 0x050F, ItemClassification.filler, 0), "Ballroom Masque": ItemData("Jonathan Headgear", 0x0510, ItemClassification.filler, 0), "Fedora": ItemData("Jonathan Headgear", 0x0511, ItemClassification.filler, 0), "Porkpie Hat": ItemData("Jonathan Headgear", 0x0512, ItemClassification.filler, 0), "Silk Hat": ItemData("Jonathan Headgear", 0x0513, ItemClassification.filler, 0), "Traveler's Hat": ItemData("Headgear", 0x0514, ItemClassification.filler, 0), - "Attica Helmet": ItemData("Jonathan Headgear", 0x0515, ItemClassification.filler, 0), + "Attica Helmet": ItemData("Jonathan Headgear", 0x0515, ItemClassification.useful), "Samurai Helmet": ItemData("Jonathan Headgear", 0x0516, ItemClassification.filler, 0), - "Shogun Helmet": ItemData("Jonathan Headgear", 0x0517, ItemClassification.filler, 0), + "Shogun Helmet": ItemData("Jonathan Headgear", 0x0517, ItemClassification.useful), "Skull Mask": ItemData("Jonathan Headgear", 0x0518, ItemClassification.filler, 0), - "Velvet Ribbon": ItemData("Charlotte Headgear", 0x0519, ItemClassification.filler, 0), + "Velvet Ribbon": ItemData("Charlotte Headgear", 0x0519, ItemClassification.useful), "Witch's Hat": ItemData("Charlotte Headgear", 0x051A, ItemClassification.filler, 0), "Mourning Veil": ItemData("Charlotte Headgear", 0x051B, ItemClassification.filler, 0), - "Nun's Habit": ItemData("Charlotte Headgear", 0x051C, ItemClassification.filler, 0), - "Open Veil": ItemData("Charlotte Headgear", 0x051D, ItemClassification.filler, 0), - "Holy Veil": ItemData("Charlotte Headgear", 0x051E, ItemClassification.filler, 0), - "Clover Tiara": ItemData("Charlotte Headgear", 0x051F, ItemClassification.filler, 0), + "Nun's Habit": ItemData("Charlotte Headgear", 0x051C, ItemClassification.useful), + "Open Veil": ItemData("Charlotte Headgear", 0x051D, ItemClassification.useful), + "Holy Veil": ItemData("Charlotte Headgear", 0x051E, ItemClassification.useful), + "Clover Tiara": ItemData("Charlotte Headgear", 0x051F, ItemClassification.useful), "Pearl Tiara": ItemData("Charlotte Headgear", 0x0520, ItemClassification.filler, 0), "Diamond Tiara": ItemData("Charlotte Headgear", 0x0521, ItemClassification.filler, 0), - "Princess Tiara": ItemData("Charlotte Headgear", 0x0522, ItemClassification.filler, 0), + "Princess Tiara": ItemData("Charlotte Headgear", 0x0522, ItemClassification.useful), "Muse's Tiara": ItemData("Charlotte Headgear", 0x0523, ItemClassification.filler, 0), "Midnight Tiara": ItemData("Charlotte Headgear", 0x0524, ItemClassification.filler, 0), "Arachne Hennin": ItemData("Charlotte Headgear", 0x0525, ItemClassification.filler, 0), @@ -286,72 +286,72 @@ class ItemData(NamedTuple): "Engineer Boots": ItemData("Boots", 0x0602, ItemClassification.filler, 0), "Royal Sandals": ItemData("Boots", 0x0603, ItemClassification.filler, 0), "Combat Boots": ItemData("Boots", 0x0604, ItemClassification.filler, 0), - "Moonwalkers": ItemData("Boots", 0x0605, ItemClassification.filler, 0), + "Moonwalkers": ItemData("Boots", 0x0605, ItemClassification.useful), "Slick Boots": ItemData("Boots", 0x0606, ItemClassification.filler, 0), - "Spiked Boots": ItemData("Boots", 0x0607, ItemClassification.filler, 0), - "Mercury Boots": ItemData("Boots", 0x0608, ItemClassification.filler, 0), - "Winged Boots": ItemData("Boots", 0x0609, ItemClassification.filler, 0), + "Spiked Boots": ItemData("Boots", 0x0607, ItemClassification.useful), + "Mercury Boots": ItemData("Boots", 0x0608, ItemClassification.useful), + "Winged Boots": ItemData("Boots", 0x0609, ItemClassification.useful), "Hiking Boots": ItemData("Jonathan Boots", 0x060A, ItemClassification.filler, 0), "Oxfords": ItemData("Jonathan Boots", 0x060B, ItemClassification.filler, 0), "Wingtips": ItemData("Jonathan Boots", 0x060C, ItemClassification.filler, 0), "Iron Leggings": ItemData("Jonathan Boots", 0x060D, ItemClassification.filler, 0), - "Silver Leggings": ItemData("Jonathan Boots", 0x060E, ItemClassification.filler, 0), - "Gold Leggings": ItemData("Jonathan Boots", 0x060F, ItemClassification.filler, 0), - "Steel Greaves": ItemData("Jonathan Boots", 0x0610, ItemClassification.filler, 0), + "Silver Leggings": ItemData("Jonathan Boots", 0x060E, ItemClassification.useful), + "Gold Leggings": ItemData("Jonathan Boots", 0x060F, ItemClassification.useful), + "Steel Greaves": ItemData("Jonathan Boots", 0x0610, ItemClassification.useful), "Platinum Lggngs": ItemData("Jonathan Boots", 0x0611, ItemClassification.filler, 0), - "Samurai Leggngs": ItemData("Jonathan Boots", 0x0612, ItemClassification.filler, 0), - "Enamel Pinheels": ItemData("Charlotte Boots", 0x0613, ItemClassification.filler, 0), + "Samurai Leggngs": ItemData("Jonathan Boots", 0x0612, ItemClassification.useful), + "Enamel Pinheels": ItemData("Charlotte Boots", 0x0613, ItemClassification.useful, 0), "Inuit Boots": ItemData("Charlotte Boots", 0x0614, ItemClassification.filler, 0), "Succubus Boots": ItemData("Charlotte Boots", 0x0615, ItemClassification.filler, 0), - "Glamorous Pumps": ItemData("Charlotte Boots", 0x0616, ItemClassification.filler, 0), + "Glamorous Pumps": ItemData("Charlotte Boots", 0x0616, ItemClassification.useful), "Silent Sandals": ItemData("Charlotte Boots", 0x0617, ItemClassification.filler, 0), - "Artemis Shoes": ItemData("Charlotte Boots", 0x0618, ItemClassification.filler, 0), - "Nun's Shoes": ItemData("Charlotte Boots", 0x0619, ItemClassification.filler, 0), + "Artemis Shoes": ItemData("Charlotte Boots", 0x0618, ItemClassification.useful), + "Nun's Shoes": ItemData("Charlotte Boots", 0x0619, ItemClassification.useful), "Glass Shoes": ItemData("Charlotte Boots", 0x061A, ItemClassification.filler, 0), "Prima Shoes": ItemData("Charlotte Boots", 0x061B, ItemClassification.filler, 0), "Diamond Shoes": ItemData("Charlotte Boots", 0x061C, ItemClassification.filler, 0), "Studded Choker": ItemData("Accessories", 0x0701, ItemClassification.filler, 0), - "Charm Necklace": ItemData("Accessories", 0x0702, ItemClassification.filler, 0), + "Charm Necklace": ItemData("Accessories", 0x0702, ItemClassification.useful), "Stella's Locket": ItemData("Key Items", 0x0703, ItemClassification.progression), "Astral Ring": ItemData("Accessories", 0x0704, ItemClassification.filler, 0), - "Engagement Ring": ItemData("Accessories", 0x0705, ItemClassification.filler, 0), + "Engagement Ring": ItemData("Accessories", 0x0705, ItemClassification.useful), "Magus Ring": ItemData("Accessories", 0x0706, ItemClassification.filler, 0), "Gold Ring": ItemData("Accessories", 0x0707, ItemClassification.filler, 0), - "Shaman Ring": ItemData("Accessories", 0x0708, ItemClassification.filler, 0), - "Miser Ring": ItemData("Accessories", 0x0709, ItemClassification.filler, 0), - "Sage Ring": ItemData("Accessories", 0x070A, ItemClassification.filler, 0), - "Thief Ring": ItemData("Accessories", 0x070B, ItemClassification.filler, 0), + "Shaman Ring": ItemData("Accessories", 0x0708, ItemClassification.useful), + "Miser Ring": ItemData("Accessories", 0x0709, ItemClassification.useful, 0), + "Sage Ring": ItemData("Accessories", 0x070A, ItemClassification.useful, 0), + "Thief Ring": ItemData("Accessories", 0x070B, ItemClassification.useful, 0), "Pearl Ring": ItemData("Accessories", 0x070C, ItemClassification.filler, 0), "Tough Ring": ItemData("Accessories", 0x070D, ItemClassification.filler, 0), "Master Ring": ItemData("Accessories", 0x070E, ItemClassification.filler, 0), "Skull Ring": ItemData("Accessories", 0x070F, ItemClassification.filler, 0), - "Immunity Ring": ItemData("Accessories", 0x0710, ItemClassification.filler, 0), - "Blessed Ring": ItemData("Accessories", 0x0711, ItemClassification.filler, 0), + "Immunity Ring": ItemData("Accessories", 0x0710, ItemClassification.useful, 0), + "Blessed Ring": ItemData("Accessories", 0x0711, ItemClassification.useful, 0), "Hercules Ring": ItemData("Accessories", 0x0712, ItemClassification.filler, 0), - "Cape": ItemData("Accessories", 0x0713, ItemClassification.filler, 0), + "Cape": ItemData("Accessories", 0x0713, ItemClassification.useful), "Hero's Cape": ItemData("Accessories", 0x0714, ItemClassification.filler, 0), - "Blue Cape": ItemData("Accessories", 0x0715, ItemClassification.filler, 0), + "Blue Cape": ItemData("Accessories", 0x0715, ItemClassification.useful), "Elven Cape": ItemData("Accessories", 0x0716, ItemClassification.filler, 0), - "Assassin's Cape": ItemData("Accessories", 0x0717, ItemClassification.filler, 0), + "Assassin's Cape": ItemData("Accessories", 0x0717, ItemClassification.useful), "Invisible Cape": ItemData("Accessories", 0x0718, ItemClassification.filler, 0), "Holy Mantle": ItemData("Accessories", 0x0719, ItemClassification.filler, 0), "Paludamentum": ItemData("Accessories", 0x071A, ItemClassification.filler, 0), "Blessed Ankh": ItemData("Accessories", 0x071B, ItemClassification.filler, 0), - "White Corsage": ItemData("Accessories", 0x071C, ItemClassification.filler, 0), + "White Corsage": ItemData("Accessories", 0x071C, ItemClassification.useful), "Orchid Corsage": ItemData("Accessories", 0x071D, ItemClassification.filler, 0), "Rose Corsage": ItemData("Accessories", 0x071E, ItemClassification.filler, 0), - "Sorceress Crest": ItemData("Accessories", 0x071F, ItemClassification.filler, 0), + "Sorceress Crest": ItemData("Accessories", 0x071F, ItemClassification.useful, 0), "Forget-Me-Not": ItemData("Accessories", 0x0720, ItemClassification.filler, 0), - "Crystal Necklet": ItemData("Accessories", 0x0721, ItemClassification.filler, 0), + "Crystal Necklet": ItemData("Accessories", 0x0721, ItemClassification.useful), "Platinum Chain": ItemData("Accessories", 0x0722, ItemClassification.filler, 0), "Heart Brooch": ItemData("Accessories", 0x0723, ItemClassification.filler, 0), "Astral Brooch": ItemData("Accessories", 0x0724, ItemClassification.filler, 0), - "Laurel Brooch": ItemData("Accessories", 0x0725, ItemClassification.filler, 0), + "Laurel Brooch": ItemData("Accessories", 0x0725, ItemClassification.useful), "Abalone Brooch": ItemData("Accessories", 0x0726, ItemClassification.filler, 0), - "Diamond Brooch": ItemData("Accessories", 0x0727, ItemClassification.filler, 0), + "Diamond Brooch": ItemData("Accessories", 0x0727, ItemClassification.useful), "Moon Brooch": ItemData("Accessories", 0x0728, ItemClassification.filler, 0), - "Venus Brooch": ItemData("Accessories", 0x0729, ItemClassification.filler, 0), + "Venus Brooch": ItemData("Accessories", 0x0729, ItemClassification.useful), "Puppet Master": ItemData("Subweapons", 0x0801, ItemClassification.progression, 0), "R. Axe": ItemData("Subweapons", 0x0802, ItemClassification.filler, 0), @@ -362,72 +362,72 @@ class ItemData(NamedTuple): "Suzaku": ItemData("Subweapons", 0x0807, ItemClassification.filler, 0), "Byakko": ItemData("Subweapons", 0x0808, ItemClassification.filler, 0), "Gnebu": ItemData("Subweapons", 0x0809, ItemClassification.filler, 0), - "Knife": ItemData("Subweapons", 0x080A, ItemClassification.filler, 0), + "Knife": ItemData("Subweapons", 0x080A, ItemClassification.useful), "Axe": ItemData("Subweapons", 0x080B, ItemClassification.filler, 0), - "Cross": ItemData("Subweapons", 0x080C, ItemClassification.filler, 0), - "Holy Water": ItemData("Subweapons", 0x080D, ItemClassification.filler, 0), - "Bible": ItemData("Subweapons", 0x080E, ItemClassification.filler, 0), + "Cross": ItemData("Subweapons", 0x080C, ItemClassification.useful, 0), + "Holy Water": ItemData("Subweapons", 0x080D, ItemClassification.useful, 0), + "Bible": ItemData("Subweapons", 0x080E, ItemClassification.useful, 0), "Javelin": ItemData("Subweapons", 0x080F, ItemClassification.filler, 0), "Ricochet Rock": ItemData("Subweapons", 0x0810, ItemClassification.filler, 0), "Boomerang": ItemData("Subweapons", 0x0811, ItemClassification.filler, 0), - "Bwaka Knife": ItemData("Subweapons", 0x0812, ItemClassification.filler, 0), + "Bwaka Knife": ItemData("Subweapons", 0x0812, ItemClassification.useful), "Shuriken": ItemData("Subweapons", 0x0813, ItemClassification.filler, 0), "Yagyu Shuriken": ItemData("Subweapons", 0x0814, ItemClassification.filler, 0), "Discus": ItemData("Subweapons", 0x0815, ItemClassification.filler, 0), "Kunimitsu": ItemData("Subweapons", 0x0816, ItemClassification.filler, 0), "Kunai": ItemData("Subweapons", 0x0817, ItemClassification.filler, 0), - "Paper Airplane": ItemData("Subweapons", 0x0818, ItemClassification.filler, 0), - "Cream Pie": ItemData("Subweapons", 0x0819, ItemClassification.filler, 0), + "Paper Airplane": ItemData("Subweapons", 0x0818, ItemClassification.useful), + "Cream Pie": ItemData("Subweapons", 0x0819, ItemClassification.useful), "Crossbow": ItemData("Subweapons", 0x081A, ItemClassification.filler, 0), "Dart": ItemData("Subweapons", 0x081B, ItemClassification.filler, 0), - "Grenade": ItemData("Subweapons", 0x081C, ItemClassification.filler, 0), - "Steel Ball": ItemData("Subweapons", 0x081D, ItemClassification.filler, 0), - "Stonewall": ItemData("Subweapons", 0x081E, ItemClassification.filler, 0), - "Offensive Form": ItemData("Subweapons", 0x081F, ItemClassification.filler, 0), + "Grenade": ItemData("Subweapons", 0x081C, ItemClassification.useful), + "Steel Ball": ItemData("Subweapons", 0x081D, ItemClassification.useful), + "Stonewall": ItemData("Subweapons", 0x081E, ItemClassification.useful), + "Offensive Form": ItemData("Subweapons", 0x081F, ItemClassification.useful), "Defensive Form": ItemData("Subweapons", 0x0820, ItemClassification.filler, 0), - "Taunt": ItemData("Subweapons", 0x0821, ItemClassification.filler, 0), + "Taunt": ItemData("Subweapons", 0x0821, ItemClassification.useful), "Wrecking Ball": ItemData("Subweapons", 0x0822, ItemClassification.filler, 0), "Rampage": ItemData("Subweapons", 0x0823, ItemClassification.filler, 0), - "Knee Strike": ItemData("Subweapons", 0x0824, ItemClassification.filler, 0), - "Aura Blast": ItemData("Subweapons", 0x0825, ItemClassification.filler, 0), - "Rocket Slash": ItemData("Subweapons", 0x0826, ItemClassification.filler, 0), + "Knee Strike": ItemData("Subweapons", 0x0824, ItemClassification.useful), + "Aura Blast": ItemData("Subweapons", 0x0825, ItemClassification.useful), + "Rocket Slash": ItemData("Subweapons", 0x0826, ItemClassification.useful, 0), "Toad Morph": ItemData("Spells", 0x0827, ItemClassification.progression), "Owl Morph": ItemData("Spells", 0x0828, ItemClassification.progression), "Sanctuary": ItemData("Spells", 0x0829, ItemClassification.progression), - "Speed Up": ItemData("Spells", 0x082A, ItemClassification.filler, 0), + "Speed Up": ItemData("Spells", 0x082A, ItemClassification.useful), "Berserker": ItemData("Spells", 0x082B, ItemClassification.filler, 0), - "Eye for an Eye": ItemData("Spells", 0x082C, ItemClassification.filler, 0), - "Clear Skies": ItemData("Spells", 0x082D, ItemClassification.filler, 0), - "Time Stop": ItemData("Spells", 0x082E, ItemClassification.filler, 0), - "Heal": ItemData("Spells", 0x082F, ItemClassification.filler, 0), - "Cure Poison": ItemData("Spells", 0x0830, ItemClassification.filler, 0), + "Eye for an Eye": ItemData("Spells", 0x082C, ItemClassification.useful), + "Clear Skies": ItemData("Spells", 0x082D, ItemClassification.useful), + "Time Stop": ItemData("Spells", 0x082E, ItemClassification.useful, 0), + "Heal": ItemData("Spells", 0x082F, ItemClassification.useful, 0), + "Cure Poison": ItemData("Spells", 0x0830, ItemClassification.useful), "Cure Curse": ItemData("Spells", 0x0831, ItemClassification.filler, 0), - "STR Boost": ItemData("Spells", 0x0832, ItemClassification.filler, 0), - "CON Boost": ItemData("Spells", 0x0833, ItemClassification.filler, 0), - "INT Boost": ItemData("Spells", 0x0834, ItemClassification.filler, 0), - "MIND Boost": ItemData("Spells", 0x0835, ItemClassification.filler, 0), + "STR Boost": ItemData("Spells", 0x0832, ItemClassification.useful), + "CON Boost": ItemData("Spells", 0x0833, ItemClassification.useful), + "INT Boost": ItemData("Spells", 0x0834, ItemClassification.useful), + "MIND Boost": ItemData("Spells", 0x0835, ItemClassification.useful), "LUCK Boost": ItemData("Spells", 0x0836, ItemClassification.filler, 0), - "ALL Boost": ItemData("Spells", 0x0837, ItemClassification.filler, 0), - "Gale Force": ItemData("Spells", 0x0838, ItemClassification.filler, 0), + "ALL Boost": ItemData("Spells", 0x0837, ItemClassification.useful), + "Gale Force": ItemData("Spells", 0x0838, ItemClassification.useful), "Rock Riot": ItemData("Spells", 0x0839, ItemClassification.filler, 0), - "Raging Fire": ItemData("Spells", 0x083A, ItemClassification.filler, 0), - "Ice Fang": ItemData("Spells", 0x083B, ItemClassification.filler, 0), - "Thunderbolt": ItemData("Spells", 0x083C, ItemClassification.filler, 0), + "Raging Fire": ItemData("Spells", 0x083A, ItemClassification.useful), + "Ice Fang": ItemData("Spells", 0x083B, ItemClassification.useful), + "Thunderbolt": ItemData("Spells", 0x083C, ItemClassification.useful), "Spirit of Light": ItemData("Spells", 0x083D, ItemClassification.filler, 0), "Dark Rift": ItemData("Spells", 0x083E, ItemClassification.filler, 0), - "Tempest": ItemData("Spells", 0x083F, ItemClassification.filler, 0), + "Tempest": ItemData("Spells", 0x083F, ItemClassification.useful), "Stone Circle": ItemData("Spells", 0x0840, ItemClassification.filler, 0), "Ice Needle": ItemData("Spells", 0x0841, ItemClassification.filler, 0), "Explosion": ItemData("Spells", 0x0842, ItemClassification.filler, 0), "Chain Lightning": ItemData("Spells", 0x0843, ItemClassification.filler, 0), - "Piercing Beam": ItemData("Spells", 0x0844, ItemClassification.filler, 0), + "Piercing Beam": ItemData("Spells", 0x0844, ItemClassification.useful), "Nightmare": ItemData("Spells", 0x0845, ItemClassification.filler, 0), "Summon Medusa": ItemData("Spells", 0x0846, ItemClassification.filler, 0), "Acidic Bubbles": ItemData("Spells", 0x0847, ItemClassification.filler, 0), "Hex": ItemData("Spells", 0x0848, ItemClassification.filler, 0), "Salamander": ItemData("Spells", 0x0849, ItemClassification.filler, 0), - "Cocytus": ItemData("Spells", 0x084A, ItemClassification.filler, 0), + "Cocytus": ItemData("Spells", 0x084A, ItemClassification.useful, 0), "Thor's Bellow": ItemData("Spells", 0x084B, ItemClassification.filler, 0), "Summon Crow": ItemData("Spells", 0x084C, ItemClassification.filler, 0), "Summon Skeleton": ItemData("Spells", 0x084D, ItemClassification.filler, 0), @@ -435,16 +435,16 @@ class ItemData(NamedTuple): "Summon Gunman": ItemData("Spells", 0x084F, ItemClassification.filler, 0), "Summon Frog": ItemData("Spells", 0x0850, ItemClassification.filler, 0), - "Rush": ItemData("Dual Crashes", 0x0852, ItemClassification.filler, 0), - "Holy Lightning": ItemData("Dual Crashes", 0x0853, ItemClassification.filler, 0), - "Axe Bomber": ItemData("Dual Crashes", 0x0854, ItemClassification.filler, 0), - "1,000 Blades": ItemData("Dual Crashes", 0x0855, ItemClassification.filler, 0), - "Volcano": ItemData("Dual Crashes", 0x0856, ItemClassification.filler, 0), - "Meteor": ItemData("Dual Crashes", 0x0857, ItemClassification.filler, 0), - "Grand Cruz": ItemData("Dual Crashes", 0x0858, ItemClassification.filler, 0), - "Divine Storm": ItemData("Dual Crashes", 0x0859, ItemClassification.filler, 0), - "Dark Gate": ItemData("Dual Crashes", 0x085A, ItemClassification.filler, 0), - "Greatest Five": ItemData("Dual Crashes", 0x085B, ItemClassification.filler, 0), + "Rush": ItemData("Dual Crashes", 0x0852, ItemClassification.useful), + "Holy Lightning": ItemData("Dual Crashes", 0x0853, ItemClassification.useful), + "Axe Bomber": ItemData("Dual Crashes", 0x0854, ItemClassification.useful), + "1,000 Blades": ItemData("Dual Crashes", 0x0855, ItemClassification.useful), + "Volcano": ItemData("Dual Crashes", 0x0856, ItemClassification.useful), + "Meteor": ItemData("Dual Crashes", 0x0857, ItemClassification.useful), + "Grand Cruz": ItemData("Dual Crashes", 0x0858, ItemClassification.useful, 0), + "Divine Storm": ItemData("Dual Crashes", 0x0859, ItemClassification.useful), + "Dark Gate": ItemData("Dual Crashes", 0x085A, ItemClassification.useful), + "Greatest Five": ItemData("Dual Crashes", 0x085B, ItemClassification.useful, 0), "Change Cube": ItemData("Relics", 0x085C, ItemClassification.progression), "Call Cube": ItemData("Relics", 0x085D, ItemClassification.progression), From a57f9c7f1fe441c7b4bf08c23a45088efa07a485 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Tue, 14 Apr 2026 23:09:39 -0500 Subject: [PATCH 06/58] Add the options file --- worlds/cv_por/Options.py | 118 ++++++++++++++++++++++++++++++++++++++ worlds/cv_por/__init__.py | 1 + 2 files changed, 119 insertions(+) create mode 100644 worlds/cv_por/Options.py diff --git a/worlds/cv_por/Options.py b/worlds/cv_por/Options.py new file mode 100644 index 000000000000..14e355572481 --- /dev/null +++ b/worlds/cv_por/Options.py @@ -0,0 +1,118 @@ +from dataclasses import dataclass +from .Options import Choice, DefaultOnToggle, Range, NamedRange, Toggle + +class Goal(Choice): # TODO!!!! IMPLEMENT + """The goal for your game. + Brauner: Defeat Brauner in the studio portrait. + Dracula: Defeat Dracula in the throne room.""" + display_name = "Goal" + option_brauner = 0 + option_dracula = 1 + default = 0 + +class RevealMap(DefaultOnToggle): # TODO!!! IMPLEMENT + """Start with the entire map visible.""" + display_name = "Reveal Map" + +class RevealBreakableWalls(Toggle): # TODO!!! IMPLEMENT + """Automatically reveals all breakable walls/objects.""" + display_name = "Reveal Hidden Walls" + +class NestofEvil(Choice): # TODO!!! IMPLEMENT + """The state of the Nest of Evil. + Vanilla: Nest of Evil has its original rewards, the two tome pages and Greatest Five. + Randomized: The three items in the Nest of Evil are random, and added to the item pool. + Required: You are required to clear the Nest of Evil before you can access your final boss. + Removed: The three items are added to the item pool, and the Nest of Evil is inaccessible.""" + display_name = "Nest of Evil State" + +class BraunerRequired(DefaultOnToggle): # TODO!!! IMPLEMENT + """If enabled, you will be required to fight Brauner before you can access the Throne Room, + Of course, this won't do anything if the goal is set to Brauner.""" + display_name = "Brauner Required" + +class NestPortraits(Range): # TODO!!! Implement + """How many Portraits are required to be cleared to access the underground passage + at the castle entrance.""" + display_name = "Passage Portraits" + range_start = 0 + range_end = 8 + default = 8 + +class BraunerPortraits(Range): #TODO!!! IMPLEMENT + """How many of ANY portraits are required to be cleared to access Brauner's portrait.""" + display_name = "Brauner Portraits" + range_start = 0 + range_end = 8 + default = 4 + +class DraculaPortraits(Range): # TODO!!! IMPLEMENT + """How many portraits are required to be cleared to access the Throne Room.""" + display_name = "Throne Portraits" + range_start = 0 + range_end = 8 + default = 0 + +class ExperiencePercent(NamedRange): # TODO!!! IMPLEMENT + """What percentage of EXP enemies give you. This is a percent of their original EXP amount.""" + display_name = "Experience Percentage" + range_start = 0 + range_end = 500 + default = 100 + special_range_names = { + "none": 0, + "half": 50, + "normal": 100, + "double": 200, + "quadruple": 400 + } + +class ShuffleWhip(Toggle): # TODO!!! Implement + """If enabled, the True Vampire Killer will be added to the item pool, + and the Whip's Memory fight will grant you a random item. + Otherwise, it will be at its normal location.""" + display_name = "Shuffle True Vampire Killer" + +@dataclass +class PoROptions(PerGameCommonOptions): + goal: Goal + brauner_portraits: BraunerPortraits + dracula_portraits: DraculaPortraits + nest_portraits: NestPortraits + brauner_required: BraunerRequired + nest_of_evil_state: NestofEvil + shuffle_whip: ShuffleWhip + reveal_map: RevealMap + reveal_hidden_walls: RevealBreakableWalls + experience_percentage: ExperiencePercent + +por_option_groups = [ + OptionGroup("Goal Options", [ + Goal, + BraunerPortraits, + DraculaPortraits, + BraunerRequired, + + ]), + + OptionGroup("Nest of Evil Options", [ + NestofEvil, + NestPortraits + + ]), + + OptionGroup("Item Options", [ + ShuffleWhip + + ]), + + OptionGroup("Enemy Settings", [ + ExperiencePercent + + ]), + + OptionGroup("Quality of Life", [ + RevealMap, + RevealBreakableWalls + ]) +] \ No newline at end of file diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py index 6cb9be0a9587..0d71d5c918b9 100644 --- a/worlds/cv_por/__init__.py +++ b/worlds/cv_por/__init__.py @@ -59,4 +59,5 @@ def __init__(self, multiworld: MultiWorld, player: int): self.locked_locations = [] self.location_cache = [] self.extra_item_count = 0 + generate_game(self) \ No newline at end of file From b6e6aa884e62e19cf99c0e33b6a861dedeea8068 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Tue, 14 Apr 2026 23:20:34 -0500 Subject: [PATCH 07/58] More init --- worlds/cv_por/Options.py | 7 ++++++- worlds/cv_por/__init__.py | 8 +++++++- worlds/cv_por/generator_main.py | 2 ++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/worlds/cv_por/Options.py b/worlds/cv_por/Options.py index 14e355572481..994de7ff32c4 100644 --- a/worlds/cv_por/Options.py +++ b/worlds/cv_por/Options.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from .Options import Choice, DefaultOnToggle, Range, NamedRange, Toggle +from Options import PerGameCommonOptions, Choice, DefaultOnToggle, Range, NamedRange, Toggle, ExcludeLocations, OptionGroup class Goal(Choice): # TODO!!!! IMPLEMENT """The goal for your game. @@ -73,6 +73,10 @@ class ShuffleWhip(Toggle): # TODO!!! Implement Otherwise, it will be at its normal location.""" display_name = "Shuffle True Vampire Killer" +class PoRExcludeLocations(ExcludeLocations): + """Prevent these locations from having an important item.""" + default = frozenset({"Dark Chasm of Old"}) + @dataclass class PoROptions(PerGameCommonOptions): goal: Goal @@ -85,6 +89,7 @@ class PoROptions(PerGameCommonOptions): reveal_map: RevealMap reveal_hidden_walls: RevealBreakableWalls experience_percentage: ExperiencePercent + exclude_locations: PoRExcludeLocations por_option_groups = [ OptionGroup("Goal Options", [ diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py index 0d71d5c918b9..63a6a1f789dc 100644 --- a/worlds/cv_por/__init__.py +++ b/worlds/cv_por/__init__.py @@ -1,6 +1,12 @@ -from .Items import item_table +import settings + +from BaseClasses import Item, Tutorial +from worlds.AutoWorld import World, WebWorld + +from .Items import item_table, get_item_names_per_category from .Options import PoROptions, por_option_groups from .generator_main import generate_game +from .static_location_data import location_ids class PoRWeb(WebWorld): theme = "ocean" diff --git a/worlds/cv_por/generator_main.py b/worlds/cv_por/generator_main.py index e69de29bb2d1..7eee07b2f025 100644 --- a/worlds/cv_por/generator_main.py +++ b/worlds/cv_por/generator_main.py @@ -0,0 +1,2 @@ +def generate_game(world): + print("hi") \ No newline at end of file From 5bdaeb13870b4059ad0876f40c0e342bfc943443 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 15 Apr 2026 01:00:46 -0500 Subject: [PATCH 08/58] Add some things, get generator stable --- worlds/cv_por/Options.py | 5 ++ worlds/cv_por/__init__.py | 6 +- worlds/cv_por/archipelago.json | 4 ++ .../docs/en_Castlevania Portrait of Ruin.md | 29 ++++++++++ worlds/cv_por/docs/setup_en.md | 56 +++++++++++++++++++ worlds/cv_por/static_location_data.py | 39 +++++++++++++ 6 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 worlds/cv_por/archipelago.json create mode 100644 worlds/cv_por/docs/en_Castlevania Portrait of Ruin.md create mode 100644 worlds/cv_por/docs/setup_en.md create mode 100644 worlds/cv_por/static_location_data.py diff --git a/worlds/cv_por/Options.py b/worlds/cv_por/Options.py index 994de7ff32c4..32b1a93df6b8 100644 --- a/worlds/cv_por/Options.py +++ b/worlds/cv_por/Options.py @@ -25,6 +25,11 @@ class NestofEvil(Choice): # TODO!!! IMPLEMENT Required: You are required to clear the Nest of Evil before you can access your final boss. Removed: The three items are added to the item pool, and the Nest of Evil is inaccessible.""" display_name = "Nest of Evil State" + option_vanilla = 0 + option_randomized = 1 + option_required = 2 + option_removed = 3 + default = 0 class BraunerRequired(DefaultOnToggle): # TODO!!! IMPLEMENT """If enabled, you will be required to fight Brauner before you can access the Throne Room, diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py index 63a6a1f789dc..0ea22432e850 100644 --- a/worlds/cv_por/__init__.py +++ b/worlds/cv_por/__init__.py @@ -1,6 +1,8 @@ import settings +import typing +import threading -from BaseClasses import Item, Tutorial +from BaseClasses import Item, Tutorial, MultiWorld from worlds.AutoWorld import World, WebWorld from .Items import item_table, get_item_names_per_category @@ -47,7 +49,7 @@ class PoRWorld(World): location_name_to_id = location_ids item_name_groups = get_item_names_per_category() - web = PoRWeb + web = PoRWeb() settings: typing.ClassVar[PoRSettings] # topology_present = True ut_can_gen_without_yaml = True diff --git a/worlds/cv_por/archipelago.json b/worlds/cv_por/archipelago.json new file mode 100644 index 000000000000..0664d43828fb --- /dev/null +++ b/worlds/cv_por/archipelago.json @@ -0,0 +1,4 @@ +{"game": "Castlevania: Portrait of Ruin", +"authors": ["Pink Switch"], +"minimum_ap_version": "0.6.7", +"world_version": "1.0.0"} \ No newline at end of file diff --git a/worlds/cv_por/docs/en_Castlevania Portrait of Ruin.md b/worlds/cv_por/docs/en_Castlevania Portrait of Ruin.md new file mode 100644 index 000000000000..56a8fcda0b79 --- /dev/null +++ b/worlds/cv_por/docs/en_Castlevania Portrait of Ruin.md @@ -0,0 +1,29 @@ +# Castlevania: Dawn of Sorrow + +## Where is the options page? + +The [player options page for this game](../player-options) contains all the options you need to configure and export a config file. + +## What does randomization do to this game? + +As of right now, Dawn of Sorrow randomizes most items. + +## What is the goal when randomized? +There are a few selectable goals. +Throne Room: Get to the Throne Room. Dario is replaced with Menace. +Abyss: Defeat Menace in the Abyss. The Mine of Judgment is opened when the power of darkness is unleashed at the center of the castle. +Abyss Plus: The same as Abyss, however you will be unable to trigger the power of darkness until you defeat Aguni in the throne room. + +## What items and locations get shuffled? + +You can find any consumable, armor, weapon, money, or Souls. +The Locations that are shuffled include all static item pickups, as well as all Boss Souls. +The event items you get from Yoko and Arikado are placed on the ground instead. +The 3 "easter egg" items are randomized, but their appearance does not change. + +## What happens when I receive an item? +When you receive an item, it should automatically be added into your game. + +## Changes from the Base Game ## +You will always have an item in your inventory called the Return Gem. +If you find yourself stuck, you can use it to warp to the starting room. \ No newline at end of file diff --git a/worlds/cv_por/docs/setup_en.md b/worlds/cv_por/docs/setup_en.md new file mode 100644 index 000000000000..4ecebad41992 --- /dev/null +++ b/worlds/cv_por/docs/setup_en.md @@ -0,0 +1,56 @@ +# Castlevania: Dawn of Sorrow Archipelago Randomizer Setup Guide + +## Required Software + +- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases). + + +- BizHawk version 2.10 or newer +- A legally obtained US ROM of Castlevania: Dawn of Sorrow. The MD5 hash should be cc0f25b8783fb83cb4588d1c111bdc18. + +## Installation Procedures + +### Windows Setup + +1. Download and install Archipelago from the link above, making sure to install the most recent version. + +## Create a Config (.yaml) File + +### What is a config file and why do I need one? + +See the guide on setting up a basic YAML at the Archipelago setup +guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en) + +### Where do I get an options file? + +The Player Options page on the website allows you to configure your personal options and export a config file from +them. You can also create one with your local Archipelago install. + +### Verifying your config file + +If you would like to validate your config file to make sure it works, you may do so on the YAML Validator page. YAML +validator page: [YAML Validation page](/mysterycheck) + +## Joining a MultiWorld Game + +### Generating and Patching a Game +1. Follow the general Archipelago instructions for generating a game. This will generate an output file for you. Your patch file will have the .apcvdos file extension. + Open ArchipelagoLauncher.exe +2. Select "Open Patch" on the left side and select your patch file. Alternatively, you can open the ArchipelagoBizhawkClient with your patch file. +3. If this is your first time patching, you will be prompted to locate your vanilla ROM. +4. A patched .nds file will be created in the same place as the patch file. +5. On your first time opening a patch with BizHawk Client, you will also be asked to locate EmuHawk.exe in your BizHawk install. + +If you are playing offline, you can simply run your ROM at this point. For Multiworld play, see the following section. + +### Connecting to a Server +By default, opening a patch file will do steps 1-5 below for you automatically. Even so, keep them in your memory just in case you have to close and reopen a window mid-game for some reason. + +1. This game uses Archipelago's BizHawk Client. If the client isn't still open from when you patched your game, you can re-open it from the launcher. +2. Ensure EmuHawk is running the patched ROM. +3. In EmuHawk, go to Tools > Lua Console. This window must stay open while playing. +4. In the Lua Console window, go to Script > Open Script…. +5. Navigate to your Archipelago install folder and open data/lua/connector_bizhawk_generic.lua. +6. The emulator may freeze every few seconds until it manages to connect to the client. This is expected. The BizHawk Client window should indicate that it connected and recognized the game. +7. To connect the client to the server, enter your room's address and port (e.g. archipelago.gg:38281) into the top text field of the client and click Connect. +8. You should now be able to receive and send items. You'll need to do these steps every time you want to reconnect. It is perfectly safe to make progress offline; everything will re-sync when you reconnect. diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py new file mode 100644 index 000000000000..ea59319d9a91 --- /dev/null +++ b/worlds/cv_por/static_location_data.py @@ -0,0 +1,39 @@ +location_ids = { + "Entrance: Drawbridge Pit Item": 0x01, + "Entrance: Drawbridge Switch Item": 0x02, + "Entrance: Statue Room Above Statue": 0x04, + "Entrance: Double Room Lower": 0x0A, + "Entrance: Above Hub": 0x0E, + "Entrance: Double Room Upper": 0x0F, + "Entrance: Center Hidden Room": 0x35, + "Entrance: Beyond Great Armor Hallway": 0x4B, + "Entrance: Drawbridge Upper Item": 0x57, + "Entrance: Statue Room Upper": 0x58, + "Entrance: Above Metal Block Room": 0x71, + "Entrance: First Passage Upper Item": 0x78, + "Entrance: Right Hidden Room": 0x7B, + "Entrance: First Passage Lower Item": 0x7C, + "Entrance: Pre-Behemoth Boss Room": 0x88, + "Entrance: Hub Room": 0xCD, + "Entrance: Lilith Room": 0x101, + "Entrance: Behemoth Chase Room": 0x12C, + "Entrance: First Passage Hidden Wall": 0x12D, + + "Buried Chamber: Westmost Room": 0x41, + "Buried Chamber: West Shaft": 0x124, + "Buried Chamber: Central Hall": 0x92, + "Buried Chamber: Central Secret": 0x20, + "Buried Chamber: Right Secret": 0x30, + "Buried Chamber: Hidden Wall Item": 0x014C, + "Buried Chamber: Right Shade Hall": 0x61, + "Buried Chamber: Right Shaft Top": 0x110, + "Buried Chamber: Eastmost Room": 0xCE, + + + + + + + + + } \ No newline at end of file From 75ac7ea3888e991a6d6e8647e4ad1a6917b72ed7 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 15 Apr 2026 01:23:18 -0500 Subject: [PATCH 09/58] Loc logging --- worlds/cv_por/static_location_data.py | 42 +++++++++++++++++++++------ 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index ea59319d9a91..2fb16bf8af8e 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -1,33 +1,57 @@ +# IDs are matched to which internal Flag number the location uses +# Because of this, they are asynchronous and out of order from what is expected +# They are listed here in numerical order for clarity + location_ids = { "Entrance: Drawbridge Pit Item": 0x01, "Entrance: Drawbridge Switch Item": 0x02, "Entrance: Statue Room Above Statue": 0x04, + "Great Stairway: After Keremet Lower": 0x09, "Entrance: Double Room Lower": 0x0A, "Entrance: Above Hub": 0x0E, "Entrance: Double Room Upper": 0x0F, + "Buried Chamber: Central Secret": 0x20, + "Great Stairway: Lower Grand Staircase Corner Nook": 0x23, + "Buried Chamber: Right Secret": 0x30, "Entrance: Center Hidden Room": 0x35, + "Buried Chamber: Westmost Room": 0x41, "Entrance: Beyond Great Armor Hallway": 0x4B, + "Great Stairway: Upper Grand Staircase Top Left Item": 0x53, + "Great Stairway: Upper Underground Room": 0x56, "Entrance: Drawbridge Upper Item": 0x57, "Entrance: Statue Room Upper": 0x58, + "Buried Chamber: Right Shade Hall": 0x61, + "Great Stairway: Underground Hidden Room": 0x66, "Entrance: Above Metal Block Room": 0x71, + "Great Stairway: Before Keremet Room": 0x76, "Entrance: First Passage Upper Item": 0x78, + "Great Stairway: Upper Grand Staircase Corner Nook": 0x79, "Entrance: Right Hidden Room": 0x7B, "Entrance: First Passage Lower Item": 0x7C, "Entrance: Pre-Behemoth Boss Room": 0x88, + "Great Stairway: After Keremet Upper": 0x89, + "Buried Chamber: Central Hall": 0x92, + "Great Stairway: Upper Grand Staircase Upper Alcove": 0xA1, + "Buried Chamber: Final Shaft Item": 0xAA, + "Great Stairway: Lower Grand Staircase Lower Alcove": 0xB4, + "Great Stairway: Big Underground Room Hidden Item": 0xBF, + "Great Stairway: Lower Grand Staircase Upper Alcove": 0xCA, + "Great Stairway: Underground East Room": 0xCB, "Entrance: Hub Room": 0xCD, + "Buried Chamber: Eastmost Room": 0xCE, + "Great Stairway: Big Underground Room Lower": 0xDA, + "Great Stairway: Big Underground Room Upper": 0xE7, "Entrance: Lilith Room": 0x101, + "Great Stairway: Upper Grand Staircase Middle Alcove": 0x10F, + "Buried Chamber: Right Shaft Top": 0x110, + "Great Stairway: Upper Grand Staircase Lower Alcove": 0x11B, + "Buried Chamber: West Shaft": 0x124, + "Great Stairway: Lower Grand Staircase Middle Alcove": 0x127, "Entrance: Behemoth Chase Room": 0x12C, "Entrance: First Passage Hidden Wall": 0x12D, - - "Buried Chamber: Westmost Room": 0x41, - "Buried Chamber: West Shaft": 0x124, - "Buried Chamber: Central Hall": 0x92, - "Buried Chamber: Central Secret": 0x20, - "Buried Chamber: Right Secret": 0x30, "Buried Chamber: Hidden Wall Item": 0x014C, - "Buried Chamber: Right Shade Hall": 0x61, - "Buried Chamber: Right Shaft Top": 0x110, - "Buried Chamber: Eastmost Room": 0xCE, + + From a1c6b278657cd2c3b79654df4a18ab6fbe380847 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 15 Apr 2026 08:20:23 -0500 Subject: [PATCH 10/58] Tower of death p1 --- worlds/cv_por/static_location_data.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index 2fb16bf8af8e..75322d0e8e17 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -11,17 +11,26 @@ "Entrance: Above Hub": 0x0E, "Entrance: Double Room Upper": 0x0F, "Buried Chamber: Central Secret": 0x20, + "Tower of Death: Second Gears Room Left": 0x22, "Great Stairway: Lower Grand Staircase Corner Nook": 0x23, + "Tower of Death: Secret Room": 0x2A, "Buried Chamber: Right Secret": 0x30, + "Tower of Death: Passage Hidden Item": 0x32, + "Great Stairway: Left Loft Lower": 0x33, "Entrance: Center Hidden Room": 0x35, "Buried Chamber: Westmost Room": 0x41, "Entrance: Beyond Great Armor Hallway": 0x4B, + "Tower of Death: Elevator Room Lower": 0x4C, "Great Stairway: Upper Grand Staircase Top Left Item": 0x53, + "Tower of Death: Top of the Tower": 0x55, "Great Stairway: Upper Underground Room": 0x56, "Entrance: Drawbridge Upper Item": 0x57, "Entrance: Statue Room Upper": 0x58, + "Tower of Death: Second Gears Room Right": 0x59, + "Tower of Death: Lower Gears Room Spike Shaft": 0x5A, "Buried Chamber: Right Shade Hall": 0x61, "Great Stairway: Underground Hidden Room": 0x66, + "Tower of Death: Third Gears Room": 0x70, "Entrance: Above Metal Block Room": 0x71, "Great Stairway: Before Keremet Room": 0x76, "Entrance: First Passage Upper Item": 0x78, @@ -31,16 +40,23 @@ "Entrance: Pre-Behemoth Boss Room": 0x88, "Great Stairway: After Keremet Upper": 0x89, "Buried Chamber: Central Hall": 0x92, + "Great Stairway: Central Nook": 0x93, "Great Stairway: Upper Grand Staircase Upper Alcove": 0xA1, "Buried Chamber: Final Shaft Item": 0xAA, + "Great Stairway: Central Upper": 0xAB, "Great Stairway: Lower Grand Staircase Lower Alcove": 0xB4, "Great Stairway: Big Underground Room Hidden Item": 0xBF, + "Great Stairway: Right Loft": 0xC0, + "Great Stairway: Left Loft": 0xC1, "Great Stairway: Lower Grand Staircase Upper Alcove": 0xCA, "Great Stairway: Underground East Room": 0xCB, + "Tower of Death: Elevator Room Top": 0xCC, "Entrance: Hub Room": 0xCD, "Buried Chamber: Eastmost Room": 0xCE, "Great Stairway: Big Underground Room Lower": 0xDA, + "Tower of Death: Elevator Room Middle": 0xDB, "Great Stairway: Big Underground Room Upper": 0xE7, + "Tower of Death: Top Gears Room": 0xF9, "Entrance: Lilith Room": 0x101, "Great Stairway: Upper Grand Staircase Middle Alcove": 0x10F, "Buried Chamber: Right Shaft Top": 0x110, @@ -49,15 +65,12 @@ "Great Stairway: Lower Grand Staircase Middle Alcove": 0x127, "Entrance: Behemoth Chase Room": 0x12C, "Entrance: First Passage Hidden Wall": 0x12D, + "Tower of Death: Lower Passage Hidden Item": 0x131, "Buried Chamber: Hidden Wall Item": 0x014C, + "Great Stairway: Connector Pipe Left": 0x180, + "Great Stairway: Connector Pipe Right": 0x181, - - - - - - } \ No newline at end of file From 3857098c7d9b003e890fd6749dfb56f740754ce1 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 15 Apr 2026 08:52:37 -0500 Subject: [PATCH 11/58] Throne room --- worlds/cv_por/Options.py | 5 +++-- worlds/cv_por/static_location_data.py | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/worlds/cv_por/Options.py b/worlds/cv_por/Options.py index 32b1a93df6b8..6bd1154e0050 100644 --- a/worlds/cv_por/Options.py +++ b/worlds/cv_por/Options.py @@ -80,7 +80,8 @@ class ShuffleWhip(Toggle): # TODO!!! Implement class PoRExcludeLocations(ExcludeLocations): """Prevent these locations from having an important item.""" - default = frozenset({"Dark Chasm of Old"}) + default = frozenset({"The Throne Room: Above Throne Left", "The Throne Room: Above Throne Right", "The Throne Room: Great Stairs Left", + "The Throne Room: Great Stairs Center", "The Throne Room: Great Stairs Under Stairs", "The Throne Room: Great Stairs Hidden"}) @dataclass class PoROptions(PerGameCommonOptions): @@ -94,7 +95,7 @@ class PoROptions(PerGameCommonOptions): reveal_map: RevealMap reveal_hidden_walls: RevealBreakableWalls experience_percentage: ExperiencePercent - exclude_locations: PoRExcludeLocations + #exclude_locations: PoRExcludeLocations por_option_groups = [ OptionGroup("Goal Options", [ diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index 75322d0e8e17..1e9c7256975d 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -10,15 +10,19 @@ "Entrance: Double Room Lower": 0x0A, "Entrance: Above Hub": 0x0E, "Entrance: Double Room Upper": 0x0F, + "The Throne Room: Above Throne Left": 0x13, "Buried Chamber: Central Secret": 0x20, "Tower of Death: Second Gears Room Left": 0x22, "Great Stairway: Lower Grand Staircase Corner Nook": 0x23, "Tower of Death: Secret Room": 0x2A, + "The Throne Room: Great Stairs Under Stairs": 0x2E, "Buried Chamber: Right Secret": 0x30, "Tower of Death: Passage Hidden Item": 0x32, "Great Stairway: Left Loft Lower": 0x33, "Entrance: Center Hidden Room": 0x35, + "The Throne Room: Great Stairs Center": 0x3E, "Buried Chamber: Westmost Room": 0x41, + "Tower of Death: Rampart Room": 0x4A, "Entrance: Beyond Great Armor Hallway": 0x4B, "Tower of Death: Elevator Room Lower": 0x4C, "Great Stairway: Upper Grand Staircase Top Left Item": 0x53, @@ -39,6 +43,7 @@ "Entrance: First Passage Lower Item": 0x7C, "Entrance: Pre-Behemoth Boss Room": 0x88, "Great Stairway: After Keremet Upper": 0x89, + "Tower of Death: Gear Belt Room": 0x8A, "Buried Chamber: Central Hall": 0x92, "Great Stairway: Central Nook": 0x93, "Great Stairway: Upper Grand Staircase Upper Alcove": 0xA1, @@ -48,13 +53,16 @@ "Great Stairway: Big Underground Room Hidden Item": 0xBF, "Great Stairway: Right Loft": 0xC0, "Great Stairway: Left Loft": 0xC1, + "The Throne Room: Above Throne Right": 0xC5, "Great Stairway: Lower Grand Staircase Upper Alcove": 0xCA, "Great Stairway: Underground East Room": 0xCB, "Tower of Death: Elevator Room Top": 0xCC, "Entrance: Hub Room": 0xCD, "Buried Chamber: Eastmost Room": 0xCE, + "Tower of Death: Hidden Room": 0xD1, "Great Stairway: Big Underground Room Lower": 0xDA, "Tower of Death: Elevator Room Middle": 0xDB, + "Tower of Death: Above Motorcycles": 0xE3, "Great Stairway: Big Underground Room Upper": 0xE7, "Tower of Death: Top Gears Room": 0xF9, "Entrance: Lilith Room": 0x101, @@ -66,11 +74,20 @@ "Entrance: Behemoth Chase Room": 0x12C, "Entrance: First Passage Hidden Wall": 0x12D, "Tower of Death: Lower Passage Hidden Item": 0x131, + "The Throne Room: Great Stairs Hidden": 0x145, "Buried Chamber: Hidden Wall Item": 0x014C, "Great Stairway: Connector Pipe Left": 0x180, "Great Stairway: Connector Pipe Right": 0x181, + "The Throne Room: Great Stairs Left": 0x182, + #"Tower of Death: Stella Item": ????, + + } + + game_events = { + "Tower of Death: Stella Item", + "Master's Keep: Whip Trial" } \ No newline at end of file From 8ddc1d6ee762bb65393c5f1520bd2a73deb9858d Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 15 Apr 2026 09:27:02 -0500 Subject: [PATCH 12/58] City of haze --- worlds/cv_por/Options.py | 2 +- worlds/cv_por/src/por_ap_main.asm | 891 ++++++++++++++++++++++++++ worlds/cv_por/static_location_data.py | 52 +- 3 files changed, 940 insertions(+), 5 deletions(-) create mode 100644 worlds/cv_por/src/por_ap_main.asm diff --git a/worlds/cv_por/Options.py b/worlds/cv_por/Options.py index 6bd1154e0050..8874b1b0f2b3 100644 --- a/worlds/cv_por/Options.py +++ b/worlds/cv_por/Options.py @@ -95,7 +95,7 @@ class PoROptions(PerGameCommonOptions): reveal_map: RevealMap reveal_hidden_walls: RevealBreakableWalls experience_percentage: ExperiencePercent - #exclude_locations: PoRExcludeLocations + exclude_locations: PoRExcludeLocations por_option_groups = [ OptionGroup("Goal Options", [ diff --git a/worlds/cv_por/src/por_ap_main.asm b/worlds/cv_por/src/por_ap_main.asm new file mode 100644 index 000000000000..71dcbf472604 --- /dev/null +++ b/worlds/cv_por/src/por_ap_main.asm @@ -0,0 +1,891 @@ +.nds +.relativeinclude on +.erroronwarning on + +@Overlay119Start equ 0x02308EC0 +@FreeSpace equ @Overlay119Start + 0x60 + +@ServerItemType equ 0x02308ED0 ; 2 bytes +@TotalItemsReceived equ 0x02308ED2 ; 2 bytes + +@OptionFlag_BraunerRequired equ 0x1 +@OptionFlag_NestRequired equ 0x2 + +;;;;;;;;;;;;;;;;; +.open "ftc/arm9.bin", 02000000h + .org 0x020523B0 + bl @GetRemoteItem + + .org 0x02041DB4 + bl @SaveAPData + + .org 0x0203A30C + nop ; Infinite-use Magical Tickets + + .org 0x0203A24C + ands r0, r0, 0x80000007 ; Allow tickets before having saved the game + + .org 0x0203A26C + b 0203A27Ch ; Allow ticket use before visiting the shop + + .org 0x0204FEC4 + bl @AddCyanPaletteToItemNames + + .org 0x0204FE94 + bl @UseOverrideColor + + .org 0x02042388 + bl @LoadAPData ; The game loads file X when copying data. Load in the relevant AP data so it gets copied as well. + + .org 0x020423A4 + bl @SaveAPData ; The game saves file Y when copying data. Save the AP data we JUST copied out of the old file into the new one. + + .org 0x02079294 + bl @CheckPresenceOfLocket + + .org 0x0203B65C + b @DontEquipOnGlitchMenu + + .org 0x02079874 + bl @PostBraunerCheck + + .org 0x02076B84 + b @CheckBraunerRequirements + + .org 0x020E537C + .dw @PostBehemothRoom + + ; Fix the map explore/suspend bug + .org 0x0202E5F8 + push r4-r10,r14 + + ldr r4, =020FCAB0h + ldr r5, =020FCC10h + ldr r6, =02111778h + ldrh r7, [r6] ; Read room's x pos from 02111778 + ldrh r8, [r6, 2h] ; Read room's y pos from 0211177A + + ldr r0, [r4] ; Read player 1's x pos from 020FCAB0 + bl @ClampXPosToRoomWidth + add r0, r0, r7 ; Add player 1's x to room's x + strh r0, [r6, 4h] ; Store player 1's x on map to 0211177C + + ldr r0, [r5] ; Read player 2's x pos from 020FCC10 + bl @ClampXPosToRoomWidth + add r0, r0, r7 ; Add player 2's x to room's x + strh r0, [r6, 6h] ; Store player 2's x on map to 0211177E + + ldr r0, [r4, 4h] ; Read player 1's y pos from 020FCAB4 + bl @ClampYPosToRoomHeight + add r0, r0, r8 ; Add player 1's y to room's y + strh r0, [r6, 8h] ; Store player 1's y on map to 02111780 + + ldr r0, [r5, 4h] ; Read player 2's y pos from 020FCC14 + bl @ClampYPosToRoomHeight + add r0, r0, r8 ; Add player 2's y to room's y + strh r0, [r6, 0Ah] ; Store player 2's y on map to 02111782 + + + ; The rewritten first half of the function now correctly updates the players' positions on the map. + ; But the second half of the function which updates the tile being explored needs certain values to be in certain registers to work correctly. + ; So we initialize those registers here. + ldrb r4, [r6, 0Dh] ; r4 has current area index read from 02111785 + mov r0, r4 + bl 02030570h ; MapGetExploredTileListForArea + mov r5, r0 ; r5 has current area's explored tile list pointer + mov r0, r4 ; ; r0 has current area index + ldr r7, =020CA580h ; PointerToGameObject + ldr r14, =1B50Ch + ldr r6, =1B510h + b 0202E6CCh + .pool + +.close +;;;;;;;;;;;;;;;;;;;;;; +.open "ftc/overlay9_0", 021CDF60h + ; If we own 9 of an item, pretend we have 8 + ; this allows the item to still be properly picked up even at cap + .org 0x021E4400 ; In GiveItem + movge r9, 8 + nop + nop + + .org 0x021E5A30 + nop ; Prevent the game from not letting you pick up 9 of a skill. This is autocapped anyways... + + .org 0x021E4418 + bl @SpecialItemHandler + + .org 0x021FCA80 + bl @InitializeNewGameData + + .org 0x021E5414 + bl @LoadAPItemColor + +;;;;;;;;;;;;;;;; + .org 0x0221BBBC ; Repoint the Konami Man and Twin Bee's item names + .dw 0x0222253C ; This so that they can both use the name 'AP Item' + .dw 0x0222253C +;;;;;;;;;;;;;;;;;;;;; +;overlay 9 0 +.close + +.open "ftc/overlay9_1", 0x0221F680 + .org 0x0222253E + .db 0x21, 0x30, 0x00, 0x49, 0x54, 0x45, 0x4D, 0xEA ; AP Item - editor repoints text so im doing it here manually +.close +;;;;;;;;;;;;;;;;;;;;;; + +.open "ftc/overlay9_2", 0x0221F680 + .org 0x0222B076 + ; The Management says we shouldn't enter this portrait + ; without Stella's locket... + .db 0x34, 0x48, 0x45, 0x00, 0x2D, 0x41, 0x4E, 0x41, 0x47, 0x45, 0x4D, 0x45, 0x4E, 0x54, 0x00, 0x53 + .db 0x41, 0x59, 0x53, 0x00, 0x57, 0x45, 0x00, 0x53, 0x48, 0x4F, 0x55, 0x4C, 0x44, 0x4E, 0x07, 0x54 + .db 0x00, 0x45, 0x4E, 0x54, 0x45, 0x52, 0xE6, 0x54, 0x48, 0x49, 0x53, 0x00, 0x50, 0x4F, 0x52, 0x54 + .db 0x52, 0x41, 0x49, 0x54, 0x00, 0x57, 0x49, 0x54, 0x48, 0x4F, 0x55, 0x54, 0x00, 0x33, 0x54, 0x45 + .db 0x4C, 0x4C, 0x41, 0x07, 0x53, 0x00, 0x4C, 0x4F, 0x43, 0x4B, 0x45, 0x54, 0x0E, 0x0E, 0x0E, 0xE5 + .db 0xE4 + + ;...? + ;...Who the hell is "The Management?!" + .db 0xE7, 0x00, 0xE3, 0x09, 0x0E, 0x0E, 0x0E, 0x1F, 0xE5, 0xE9, 0xE3, 0x02, 0x0E, 0x0E, 0x0E, 0x37 + .db 0x48, 0x4F, 0x00, 0x54, 0x48, 0x45, 0x00, 0x48, 0x45, 0x4C, 0x4C, 0x00, 0x49, 0x53, 0x00, 0x02 + .db 0x34, 0x48, 0x45, 0x00, 0x2D, 0x41, 0x4E, 0x41, 0x47, 0x45, 0x4D, 0x45, 0x4E, 0x54, 0x02, 0x1F + .db 0x01, 0xE5, 0xE4 + + ; Me. + ; I'm The Management. + .db 0xE7, 0x01, 0xE3, 0x0B, 0x2D, 0x45, 0x0E, 0xE6, 0x29, 0x07, 0x4D, 0x00, 0x54, 0x48, 0x45, 0x00 + .db 0x4D, 0x41, 0x4E, 0x41, 0x47, 0x45, 0x4D, 0x45, 0x4E, 0x54, 0x0E, 0xE5, 0xE4 + + ; Well, I guess I can't really + ; argue with that... + .db 0xE7, 0x00, 0xE3, 0x05, 0x37, 0x45, 0x4C, 0x4C, 0x0C, 0x00, 0x29, 0x00, 0x47, 0x55, 0x45, 0x53 + .db 0x53, 0x00, 0x29, 0x00, 0x43, 0x41, 0x4E, 0x07, 0x54, 0x00, 0x52, 0x45, 0x41, 0x4C, 0x4C, 0x59 + .db 0xE6, 0x41, 0x52, 0x47, 0x55, 0x45, 0x00, 0x57, 0x49, 0x54, 0x48, 0x00, 0x54, 0x48, 0x41, 0x54 + .db 0x0E, 0x0E, 0x0E, 0xE6, 0xE5, 0xE4, 0xEA +.close +;;;;;;;;;;;; +.open "ftc/overlay9_4", 0x0222C840 + .org 0x02232864 + nop ; Prevent the game from deleting the old vampire killer + + .org 0x02232890 + nop ; Prevent the game from giving you the new Vampire Killer, we do this in the next room instead + + .org 0x02232880 + nop ; Prevent the game from auto-equipping new Vampire Killer. +.close +;;;;;;;;;;;;;;;;;;;;;;;;; +.open "ftc/overlay9_25", 0x022D7900 + .org 0x022D95CC + bl @LoadAPData + +.close + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.open "ftc/overlay9_27", 0x022E01A0 + .org 0x022E04BC ; Skip to the title screen instantly instead of 6 years of fucking logos + nop +.close +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.open "ftc/overlay9_60", 0x022D7900 + ; Fix the enemy version of the creature setting the boss kill flag + .org 0x022D7D14 + ldrh r1, [r1, 3Ch] + cmp r1, 0h + beq 0x022D7D30 + + ; Then set the boss death flag. + ldr r2, [r0, 76Ch] + orr r2, r2, 200h + str r2, [r0, 76Ch] +.close + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.open "ftc/overlay9_64", 022D7900h + .org 0x022D8B18 ; Prevents death's cutscene skip from setting his flag too early + nop + + .org 0x022E4F94 + nop ; Prevent dracula from setting the dracula flag mid fight +.close +;;;;;;;;;;;;;;;;;;;;; +.open "ftc/overlay9_78", 0x022E8820 + .org 0x022E8884 + cmp r1, 0xFF ; Always set the drawbridge to open + + .org 0x022E898C + bl @Undergroundpassage_Portraitcheck +.close + +.open "ftc/overlay9_79", 0x022E8820 + .org 0x022E9348 + beq 0x022E9364 ; Skips Wind's locket cutscene, removed for brevity +.close +;;;;;;;; +;Tile data for the tower room. adds platforms +.open "ftc/overlay9_85", 0x022E8820 + .org 0x022F3FA4 + .dh 0x0044 + .dh 0x0045 + .dh 0x0044 + .dh 0x0047 +.close +;;;;;;;;;;;;;;;;;;;;;;;;; +.open "ftc/overlay9_86", 0x022E8820 + .org 0x022EC2BC + nop ; Skip over giving the original locket + + .org 0x022EC304 + bl @GiveLocketItem + + .org 0x022EC8EC + b 0x022EC900 ; Forest of Doom portrait. Ignore the shortened dialogue so I can write my own thing here + + .org 0x022EC8A0 + b @ForestPortraitSceneCheck +.close +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.open "ftc/overlay9_88", 0x022E8820 + .org 0x022E8880 + b @CheckDraculaBarrierRequirements +.close + +.open "ftc/overlay9_89", 0x022E8820 + .org 0x022EA018 + bl @GetWhipMemItem +.close +;;;;;;;;;;;;;;;;;;;;;;;; +.open "ftc/overlay9_90", 0x022E8820 + .org 0x022E8BD8 + bl @EndDracula +.close +;;;;;;;;;;;;;;;;;;;;;;;; +.open "ftc/overlay9_119", @Overlay119Start +.org @FreeSpace + +@GenerationFlags: + @OptionFlag_NestPortraits: ;02308F20 + .db 0x08 + + @OptionFlag_BraunerPortraits: ;02308F21 + .db 0x04 + + @OptionFlag_DraculaPortraits: ;02308F22 + .db 0x00 + + @OptionFlag_DraculaRequirements: ;02308F23 + .db 0x01 + + @OptionFlag_DraculaGoal: ;02308F24 + .db 0x01 + + @StellaLocketItem: + .dh 0x0703 ; Type/ID for the locket 02308F25 + .db 0x00 ; Item color 02308F27 + + @WhipMemoryItem: + .dh 0x030A ;02308F28 + .db 0x00 ;02308F2A + + .align 4 +;;;;;;;;;;;;;;;;;;; +; Gets an arbitrary item and gives it to the player. Used for Archipelago server items +;0x02308ED0 - item type +;0x02308ED1 - item ID +@GetRemoteItem: + push r0-r3, lr + + ldr r0, =0x020F628C ; Does the player have control? + ldrb r0, [r0] + cmp r0, 0 + bne @@End ; We don't want to get items while playing text and stuff + + ldr r0, = 0x020FC498 + ldrb r0, [r0] + cmp r0, 0 + bne @@End ; This is for main player control. Also used for cutscenes/autowalks? + + ldr r0, = 0x0211174C + ldr r0, [r0] + ldr r1, = 0x80100041 ; We don't want to get items while dying, in a cutscene, room transition, or otherwise in a no pausing state + ands r0, r0, r1 + bne @@End + + ldr r0, = @ServerItemType + mov r1, r0 ; back up the address + + ldrh r0, [r0] ; Load the item we've received + ands r1, r0, 0xFF ; Move the ID to r1 + ands r0, r0, 0xFF00 ; separate the TYPE from the ID + beq @@End ; If this is 0, assume we haven't received anything + + lsr r0, r0, 0x8 + bl @GetItemArbitrary + ldr r0, = @ServerItemType + mov r1, 0 + strh r1, [r0] ; Zero out the item so we don't get anything again +@@End: + pop r0-r3,lr + b 0x0201E9C0 ; Return to normal code +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;Gives something to the player. +; r0 = Item TYPE +; r1 = Item ID + +@MoneyValues: +.dh 0x0001, 0x000A, 0x0032, 0x0064, 0x01F4, 0x03E8, 0x07D0 +.align 4 + +@GetItemArbitrary: + push lr + cmp r0, 1 ; Money + beq @@GetMoney + cmp r0, 8 + beq @@GetSkill ; Skills/abilities + ; normal items/armor + push r0,r1 + cmp r1, 0x08 + blt @@NormalItem + cmp r1, 0x0A + bge @@NormalItem + b @@GetMaxUp ; ID 8-A are used for max up boosts +@@NormalItem: + bl 0x021E43E4 ; Subroutine for granting regular items + pop r0,r1 + bl 0x021E476C ; Get the global ID for the item + mov r1, 0xF0 + bl 0x0204FE0C ; Display the got item popup + mov r0, 0x31 + bl 0x0204D6B0 ; Play the sound effect for the item + pop lr + bx lr +@@GetMaxUp: + pop r0,r1 + mov r0, r1 ; Put the ID in r0 + mov r1, 1 + mov r2, 0 + bl 0x021E3AA8 ; Useconsumable/Get Max Up + pop lr + bx lr +@@GetSkill: + mov r0, r1 + push r0, r1 + mov r1, 0xF0 + bl 0x0204FB24 ; Show skill popup + + pop r0, r1 + push r0 + mov r1, 1 + bl 0x02214F34 ; Give the skill + mov r0, 0x30 + bl 0x0204D6B0 ; Play the sound effect for the item + pop r0 + + cmp r0, 0x5C ; Is this a Relic? + blt @@EndSkill + sub r0, r0, 0x5C ; get the skill id num + mov r1, 0 + bl 0x02215308 ; Activate relic +@@EndSkill: + pop lr + bx lr +@@GetMoney: + mov r1, r1, lsl 1 + ldr r0, = @MoneyValues + ldrh r0, [r0, r1] ; Load the 2 bytes corresponding to the amount + push r0 + cmp r0, 1000 + bge @@BigMoneySound ; values over $1,000 play special sfx + mov r0, 0x2B + b @@SmallMoneySound +@@BigMoneySound: + mov r0, 0x2C +@@SmallMoneySound: + bl 0x0204D6B0 + pop r0 + push r0 + bl 0x021E3F24 ; Give this amount of money + pop r0 + bl 0x0204FD4C ; Show the text popup for it + pop lr + bx lr +;;;;;;;;;;;;;;;;;;;;;;;;; +; Commits some extra data used by AP into SRAM +@SaveAPData: + push r0-r3 + push lr + mov r2, 0x50 + mul r0, r0, r2 ; filenum * 0x50 + + ldr r1, =0x1F00 + add r0, r0, r1 + ldr r1, =@ServerItemType + ldr r3, =0x00 + bl 0x020118F4 + pop lr + pop r0-r3 + b 0x02042C44 + +; Loads data out of the save file into main ram +@LoadAPData: + push r0-r3 + push lr + mov r2, 0x50 + mul r0, r0, r2 ; (Offset is FileNum * 0x50) + ldr r1, =0x1F00 + add r0, r0, r1 ; Add base offset + file offset + ldr r1, = @ServerItemType + bl 0x02011998 ;LoadData + pop lr + pop r0-r3 + b 0x02042FA4 ; load the rest of the file +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; When we get AP items (4F or 50), dont add them to the inventory +; This can also be purposed for a custom items handler for consumables greater than 0x5F +@SpecialItemHandler: + cmp r0, 3 + beq @@GiveSpecialWeapon ; Use this to check for the Vampire Killer + cmp r0, 2 + bne @@End ; Items like this will only be normal pickups... + cmp r1, 0x4F + beq @@SkipItem + cmp r1, 0x50 + beq @@SkipItem; 0x4F and 0x50 are used for offworld items... + cmp r1, 0x5F + bge @@CustomItemHandler ; Use this for custom, new items. +@@End: + b 0x021E4428 +@@SkipItem: + bx lr ; Just don't do the give routine +@@CustomItemHandler: + bx lr +@@GiveSpecialWeapon: + cmp r1, 1 ; Standard vampire killer + beq @@StandardVK + cmp r1, 0x0A + beq @@TrueVK + b 0x021E4428 +@@StandardVK: + push r0-r2,lr + mov r0, 3 + mov r1, 0x0A + bl 0x021E45A4 ; Do we have the True VK? + cmp r0, 0 ; If not... + pop r0-r2, lr + movne r0, 3 + movne r1, 0x0A ; If we already have the true VK, convert this item to the normal one + movne r2, 1 + bne @SpecialItemHandler + push r0-r2,lr + mov r0, 3 + mov r1, 1 + bl 0x021E45A4 ; Do we already have a copy of the normal one? + cmp r0, 0 ; If we do already have the normal VK, upgrade it! + pop r0-r2, lr + movne r0, 3 + movne r1, 0x0A + movne r2, 1 + bne @SpecialItemHandler ; If we have the + b 0x021E4428 ; Give the regular VK +@@TrueVK: + push r0-r2,lr + mov r0, 3 + mov r1, 1 + mov r2, 0 + bl 0x021E4428 ; Wipe out the standard VK no matter what + ldr r0, =0x0211217C + ldrb r1, [r0] ; Check Jonathans' current weapon + cmp r1, 1 ; Is the normal VK equipped? + bne @@SkipEquip + mov r1, 0x0A + strb r1,[r0] ; Replace with old one with the new one +@@SkipEquip: + pop r0-r2, lr + b 0x021E4428 + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Clear out AP memory on a new file. +; Also, give the player a Magical Ticket to start with. +@InitializeNewGameData: + push lr + bl 0x021E4428 + mov r0, 2 + mov r1, 0x45 + mov r2, 1 + bl 0x021E4428 ; Give the player a magical ticket + mov r0, 0x00 ; Clear + ldr r1, =@ServerItemType + mov r2, 0x50 ; 50 bytes + bl 0x0209E570 ; Wipe the 0x50 bytes of AP memory when starting a new file + pop lr + bx lr +;;;;;;;;;;;;;;;;;;;;;;;;; +@RamFlag_ItemColorOverride: +.db 0x00 +.align 4 + + +; Add a special handler to the DrawPopupWindow function +; We repoint to a new palette so I can add a new one +@ItemPalette_Cyan: + .dh 0x0000 + .dh 0x0000 + .dh 0x014E + .dh 0x0000 + .dh 0x1A32 + .dh 0x32D8 + .dh 0x573C + .dh 0xFFFF + .dh 0xFFFF + .dh 0x7ECE + .dh 0x0000 + .dh 0x0000 + .dh 0x65E6 + .dh 0x0000 + .dh 0x0000 + .dh 0x0000 +@AddCyanPaletteToItemNames: + cmp r0, 0x0E ; clearly oob so i use this for my stuff + bne @@LoadNormalPalette + ldr r5, = @ItemPalette_Cyan + bx lr +@@LoadNormalPalette: + add r5, r3, r0, lsl 0x05 + bx lr + +; Use the Override color for item name displays, if one is present. +; This is used to change the color of AP items +@UseOverrideColor: + ldr r0, = @RamFlag_ItemColorOverride + ldrb r0, [r0] ; Is there an AP item primed? + cmp r0, 0 + beq @@NormalColor + push r0, r1 + ldr r0, =@RamFlag_ItemColorOverride + mov r1, 0 + strb r1, [r0] ; Zero out the flag here + pop r0, r1 + b 0x0204FEB4 ; We use our custom color instead +@@NormalColor: + mov r0, 0x0A + b 0x0204FEB4 + +; Use the item's flag as an index into the ItemColorTable +@LoadAPItemColor: + ldrh r12, [r0, 0x3C] ; Get the flag val of the item + push r0, r1 + ldr r0, = @ItemColorTable + ldrb r0, [r0, r12] + cmp r0, 0 + beq @@Skip + ldr r1, = @RamFlag_ItemColorOverride + strb r0, [r1] ; Store it for later + +@@Skip: + pop r0,r1 + bx lr +;;;;;;;;;;;;;;;;;;;;;;; +;Stella's Locket is given to you in the cutscene, we handle it here instead +@GiveLocketItem: + push r0-r2,lr + bl 0x021D13EC ; Run the original code, runs end-of-battle cleanup on bosses + ldr r2, = @RamFlag_ItemColorOverride + ldr r1, = @StellaLocketItem + ldrb r0, [r1, 2] ; Read the Item's Color + strb r0,[r2] ; Transfer the item color so that we can use it if it's an ap item + ldrb r0, [r1, 1] ; R0 = Item Type + ldrb r1,[r1] ; R1 = Item ID + cmp r0, 8 + beq @@LocketSkill +@@GiveLocketSkill: + bl @GetItemArbitrary ; Pass off to the actual item routine + pop r0-r2,lr + bx lr +@@LocketSkill: + push r0,r1 + bl 0x0202C064 ; Stella's cutscene unloaded the font, so if we get a skill we need to reload it + pop r0,r1 + b @@GiveLocketSkill +;;;;;;;;;;;;;;;;;;;;;; +; Normally this checks an event flag that Wind has been shown Stella's locket +; Instead, we only want to check that the item is owned +@CheckPresenceOfLocket: + push lr + mov r0, 7 + mov r1, 3 ; Load Stella's Locket + bl 0x021E45A4 ; Check the item count + cmp r0, 0 + pop lr + bx lr + +; This is the init for the cutscene when you haven't turned it in. +; Check if we have the locket, and cancel the cutscene if we do. +; This is to handle getting it while in the same room +@ForestPortraitSceneCheck: + bl @CheckPresenceOfLocket + bne @@EndForestScene + mov r0, r4 + b 0x022EC8A4 +@@EndForestScene: + push r1 + ldr r0, = 0x02111BB1 + ldrb r1, [r0] ; Get the flag + orr r1, r1, 0x0C ; Set the evnt flag that you've talked to wind about this + strb r1, [r0] + pop r1 + b 0x022EC87C ; Clean up and end the scene +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Prevent equipment from being applied on the glitchy character menus. +; This should patch out memory corruption +@DontEquipOnGlitchMenu: + ldrsh r2,[r0,0xE4] + cmp r2, 1 + bgt 0x0203B8DC + b 0x0203B660 +;;;;;;;;;;;;;;; +; Clamps position for the map, fixes suspend bug +@ClampXPosToRoomWidth: + mov r0, r0, asr 14h ; Divide X by 0x100000 subpixels to get X in screens. + cmp r0, 0h + movlt r0, 0h ; X = 0 if X < 0 + ldr r2, =02112458h + ldr r2, [r2] + ldrb r2, [r2] ; Load the room's width from the collision layer (the first layer) + sub r2, r2, 1h + cmp r0, r2 + movgt r0, r2 ; X = room_width-1 if X > room_width-1 + bx r14 +@ClampYPosToRoomHeight: + ldr r12,=2AAAAAABh + smull r12,r0,r12,r0 ; Divide Y in subpixels by 6 + mov r0, r0, asr 11h ; Further divide Y by 0x20000, divided by a total of 0xC0000 to get Y in screens + cmp r0, 0h + movlt r0, 0h ; Y = 0 if Y < 0 + ldr r2, =02112458h + ldr r2, [r2] + ldrb r2, [r2, 1h] ; Load the room's height from the collision layer (the first layer) + sub r2, r2, 1h + cmp r0, r2 + movgt r0, r2 ; Y = room_height-1 if Y > room_height-1 + bx r14 +;;;;;;;;;;;;;;;;;; +; Switches the underground passage/nest of evil to check portraits instead of a flag +@Undergroundpassage_Portraitcheck: + push lr + bl @CheckPortraitClearCount + ldr r1, = @OptionFlag_NestPortraits + ldrb r1,[r1] ; Check the set number + cmp r0, r1 ; If cleared portraits >= required + mov r0, 0 + bge @@ActivateCheck + cmp r0, 0 + pop lr + bx lr +@@ActivateCheck: + cmp r0, 1 + pop lr + bx lr +;;;;;;;;;;;;;;;;;; +; Counts how many portraits have been cleared +@PortraitBossFlags: + .db 0x01, 0x05, 0x06, 0x07, 0x8, 0x09, 0x0A, 0x0B +@CheckPortraitClearCount: + push r1-r6 + ldr r1, =0x021119DC + ldr r1,[r1] ; Load the bosses killed flags + mov r0, 0 + mov r2, 0 + mov r6, 1 + ldr r3, =@PortraitBossFlags +@@Loop: + ldrb r5, [r3,r2] ; Load the current flag + lsl r5, r6, r5 ; Shift to get a bit + ands r5, r5, r1 ; And this + addne r0, r0, 1 + cmp r2, 7 + addne r2, r2, 1 + bne @@Loop + pop r1-r6 + ;r0 = clear count + bx lr +;;;;;;;;;;;;;;;;;;;;;;;;;;; +; If Brauner Goal, teleport to ending. +; Else; teleport to portrait room +@PostBraunerCheck: + push r0 + ldr r0, = 0x02111785 + ldrb r0, [r0] ; Get the current area + cmp r0, 0x0B ; Are we in the Lost Galley? + bne @@NormalPortrait + pop r0 + ldr r0, = @OptionFlag_DraculaGoal + ldrb r0, [r0] + cmp r0, 1 ; Did the player choose Dracula goal? + beq @@DraculaTeleport + ldr r0, =0x021119DE + ldrb r1, [r0] + orr r1, r1, 0x02 ; Dracula + strb r1,[r0] ; Set Dracula's defeat flag so the ending works + mov r0, 0x0C + mov r1, 0x00 + mov r2, 0x00 + mov r3, 0x00 + b 0x02032D90 ; Warp to the epilogue room +@@DraculaTeleport: + mov r0, 0 + mov r1, 0x0B + mov r2, 0x00 + mov r3, 0x100 + b 0x02032D90 ; Warp to the Portrait room, outside the studio portrait +@@NormalPortrait: + pop r0 + b 0x02032D90 ; Do the normal warp instead of the special warp +;;;;;;;;;;;;;;;;;;;;;;;;;;; +@GetWhipMemItem: + push r0-r2,lr + ldr r2, = @RamFlag_ItemColorOverride + ldr r1, = @WhipMemoryItem + ldrb r0, [r1, 2] ; Read the Item's Color + strb r0,[r2] ; Transfer the item color so that we can use it if it's an ap item + ldrb r0, [r1, 1] ; R0 = Item Type + ldrb r1,[r1] ; R1 = Item ID + bl @GetItemArbitrary + pop r0-r2,lr + b 0x021CE664 +;;;;;;;;;;;;;;;;;;;;;;;;;;; +;Move the Dracula flag to HERE so it doesn't go off mid-fight +@EndDracula: + push r0,r1 + ldr r0, =0x021119DE + ldrb r1,[r0] + orr r1, r1, 0x02 + strb r1,[r0] + pop r0,r1 + b 0x02032D90 +;;;;;;;;;;;;;;;;;;;;;;;; +; Check the AP requirements for Dracula's barrier here +@CheckDraculaBarrierRequirements: + push r0 + ldr r0, =@OptionFlag_DraculaGoal + ldrb r0,[r0] + cmp r0, 0 + popeq r0 + beq 0x022E8894 ; If the goal is to fight Brauner, we don't want to access Dracula at all, so we bail + + push lr + bl @CheckPortraitClearCount ; Grab the cleared portraits + pop lr + ldr r1, = @OptionFlag_DraculaPortraits ; and the required portraits + ldrb r1, [r1] + cmp r0, r1 + poplt r0 + blt 0x022E8894 ; If Cleared portraits < required, bail + ldr r0, =@OptionFlag_DraculaRequirements + ldrb r0,[r0] + ands r0,r0, @OptionFlag_BraunerRequired + beq @@SkipBraunerCheck ; If Brauner is not set as required, we can just skip this + ldr r0, =0x021119DC + ldr r0,[r0] ; Get the boss death flags + ands r0, r0, 0x8000 ; Check Brauner's death flag + popeq r0 + beq 0x022E8894 ; Brauner is required but not defeated, so bail +@@SkipBraunerCheck: + ldr r0, =@OptionFlag_DraculaRequirements + ldrb r0,[r0] + ands r0,r0, @OptionFlag_NestRequired + beq @@SkipNestRequirement + ldr r0, = 0x02111BC5 + ldrb r0,[r0] + ands r0,r0,0x20 ; This is the pickup flag for the final item in Nest of Evil. All of its flags are otherwise temporary, so this is the closest we can get to a clear check + popeq r0 + beq 0x022E8894 +@@SkipNestRequirement: + pop r0 + b 0x022E8884 ; All checks pass so delete the barrier +;;;;;;;;;;;;;;;;;;;;;;;; +; Checks the requirements to enter Brauner's portrait +@CheckBraunerRequirements: + push lr + bl @CheckPortraitClearCount + pop lr + ldr r1, = @OptionFlag_BraunerPortraits + ldrb r1, [r1] + cmp r0, r1 ; If we have cleared more than are required + bge @@PortraitRequirementMet + sub r0, r1, r0 ; Get the difference in portraits + cmp r0, 4 + bge @@Draw4Chains + mov r1, 1 + mov r1, r1, lsl r0 + sub r1, r1, 1 + strb r1, [r4,0x13C] + b 0x02076BF4 + + +@@PortraitRequirementMet: + ldr r0, = @OptionFlag_DraculaGoal + ldrb r0,[r0] + cmp r0, 1 + beq 0x02076BF4 ; If Dracula is the goal, skip this and move on + + ldr r0, = @OptionFlag_NestRequired + ldrb r0,[r0] + cmp r0, 0 + beq 0x02076BF4 ; If Nest isn't required, don't bother + + ldr r0, = 0x02111BC5 + ldrb r0,[r0] + ands r0,r0,0x20 ; This is the pickup flag for the final item in Nest of Evil. All of its flags are otherwise temporary, so this is the closest we can get to a clear check + bne 0x022E8894 ; If we've done it, just end + mov r0, 1 + strb r0, [r4,0x13C] ; Store ONE chain as needing to be cleared + b 0x02076BF4 +@@Draw4Chains: + mov r0, 0x0F + strb r0, [r4,0x13C] + b 0x02076BF4 + +.pool + + +;;;;;;;;;;;;;;;;; +.org 0x02322AC0 ; We want this to not move ever so we put it at the back +@ItemColorTable: + .fill 0x200 ; This space is reserved for AP item tag colors +.align 4 + +@PostBehemothRoom: + .dh 0x00E8 ; Upper pickup + .dh 0x0068 + .db 0x00 + .db 0x04 + .db 0x0B + .db 0x00 + .dh 0x000F + .dh 0x0069 + + .dh 0x00E0 ; Lower pickup + .dh 0x0140 + .db 0x00 + .db 0x04 + .db 0x08 + .db 0x00 + .dh 0x0040 + .dh 0x0000 + + .dh 0x0010 ; Behemoth wall + .dh 0x0130 + .db 0x00 + .db 0x02 + .db 0x3A + .db 0x00 + .dh 0x000C + .dh 0x0000 + .db 0xFF, 0x7F, 0xFF, 0x7F + +.close \ No newline at end of file diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index 1e9c7256975d..214e9ede5060 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -5,35 +5,50 @@ location_ids = { "Entrance: Drawbridge Pit Item": 0x01, "Entrance: Drawbridge Switch Item": 0x02, + "City of Haze: Church Hallway": 0x03, "Entrance: Statue Room Above Statue": 0x04, + "City of Haze: Post-Boss Room": 0x05, + "City of Haze: Heavy Cart Item": 0x06, "Great Stairway: After Keremet Lower": 0x09, "Entrance: Double Room Lower": 0x0A, "Entrance: Above Hub": 0x0E, "Entrance: Double Room Upper": 0x0F, "The Throne Room: Above Throne Left": 0x13, + "City of Haze: Before Cart Room": 0x16, + "City of Haze: Underground Square Right": 0x17, "Buried Chamber: Central Secret": 0x20, "Tower of Death: Second Gears Room Left": 0x22, "Great Stairway: Lower Grand Staircase Corner Nook": 0x23, + "Master's Keep: Entrance": 0x24, "Tower of Death: Secret Room": 0x2A, "The Throne Room: Great Stairs Under Stairs": 0x2E, "Buried Chamber: Right Secret": 0x30, "Tower of Death: Passage Hidden Item": 0x32, "Great Stairway: Left Loft Lower": 0x33, "Entrance: Center Hidden Room": 0x35, + "Master's Keep: Hidden Room": 0x38, "The Throne Room: Great Stairs Center": 0x3E, "Buried Chamber: Westmost Room": 0x41, + "City of Haze: Register Room": 0x46, + "City of Haze: Bar Left": 0x47, "Tower of Death: Rampart Room": 0x4A, "Entrance: Beyond Great Armor Hallway": 0x4B, "Tower of Death: Elevator Room Lower": 0x4C, "Great Stairway: Upper Grand Staircase Top Left Item": 0x53, + "City of Haze: Axe Courtyard": 0x54, "Tower of Death: Top of the Tower": 0x55, "Great Stairway: Upper Underground Room": 0x56, "Entrance: Drawbridge Upper Item": 0x57, "Entrance: Statue Room Upper": 0x58, "Tower of Death: Second Gears Room Right": 0x59, "Tower of Death: Lower Gears Room Spike Shaft": 0x5A, + "Master's Keep: Right Square Room": 0x5D, + "City of Haze: Central Hallway Stairs": 0x60, "Buried Chamber: Right Shade Hall": 0x61, + "City of Haze: Right Corner Room": 0x64, + "City of Haze: Left Plaze": 0x65, "Great Stairway: Underground Hidden Room": 0x66, + "City of Haze: Center Room Lower": 0x6E, "Tower of Death: Third Gears Room": 0x70, "Entrance: Above Metal Block Room": 0x71, "Great Stairway: Before Keremet Room": 0x76, @@ -41,31 +56,51 @@ "Great Stairway: Upper Grand Staircase Corner Nook": 0x79, "Entrance: Right Hidden Room": 0x7B, "Entrance: First Passage Lower Item": 0x7C, + "City of Haze: Left Dance Hall Right Item": 0x7E, + "City of Haze: Tunnel Room Left": 0x7F, + "City of Haze: Chunky Room": 0x84, "Entrance: Pre-Behemoth Boss Room": 0x88, "Great Stairway: After Keremet Upper": 0x89, "Tower of Death: Gear Belt Room": 0x8A, + "City of Haze: Dance Halls Basement": 0x8B, + "City of Haze: Central Hallway Lower": 0x8C, "Buried Chamber: Central Hall": 0x92, "Great Stairway: Central Nook": 0x93, + "City of Haze: Underground Square Left": 0xA0, "Great Stairway: Upper Grand Staircase Upper Alcove": 0xA1, + "City of Haze: Right Dance Hall Secret Room": 0xA7, "Buried Chamber: Final Shaft Item": 0xAA, "Great Stairway: Central Upper": 0xAB, + "City of Haze: Cart Secret Left": 0xB0, + "City of Haze: Winding Rooms": 0xB3, "Great Stairway: Lower Grand Staircase Lower Alcove": 0xB4, + "City of Haze: Meat Room": 0xBB, "Great Stairway: Big Underground Room Hidden Item": 0xBF, "Great Stairway: Right Loft": 0xC0, "Great Stairway: Left Loft": 0xC1, "The Throne Room: Above Throne Right": 0xC5, + "City of Haze: Tunnel Room Right": 0xC8, "Great Stairway: Lower Grand Staircase Upper Alcove": 0xCA, "Great Stairway: Underground East Room": 0xCB, "Tower of Death: Elevator Room Top": 0xCC, "Entrance: Hub Room": 0xCD, "Buried Chamber: Eastmost Room": 0xCE, "Tower of Death: Hidden Room": 0xD1, + "City of Haze: Left Dance Hall Left Item": 0xD8, "Great Stairway: Big Underground Room Lower": 0xDA, "Tower of Death: Elevator Room Middle": 0xDB, + "City of Haze: First Passsage": 0xDD, + "City of Haze: Axe Armor Item": 0xE0, + "City of Haze: Dance Halls Vertical Hallway": 0xE2, "Tower of Death: Above Motorcycles": 0xE3, "Great Stairway: Big Underground Room Upper": 0xE7, + "City of Haze: Bread Shelf": 0xED, + "City of Haze: Underground Secret Room": 0xEF, + "City of Haze: Right Dance Hall Item": 0xF7, "Tower of Death: Top Gears Room": 0xF9, + "City of Haze: Corner Room": 0xFC, "Entrance: Lilith Room": 0x101, + "City of Haze: Center Room Top": 0x10E, "Great Stairway: Upper Grand Staircase Middle Alcove": 0x10F, "Buried Chamber: Right Shaft Top": 0x110, "Great Stairway: Upper Grand Staircase Lower Alcove": 0x11B, @@ -76,18 +111,27 @@ "Tower of Death: Lower Passage Hidden Item": 0x131, "The Throne Room: Great Stairs Hidden": 0x145, "Buried Chamber: Hidden Wall Item": 0x014C, + "City of Haze: Cart Secret Right": 0x14E, + "City of Haze: Bar Right": 0x154, "Great Stairway: Connector Pipe Left": 0x180, "Great Stairway: Connector Pipe Right": 0x181, "The Throne Room: Great Stairs Left": 0x182, + + + + + + + #"Master's Keep: Whip Trial": ????? #"Tower of Death: Stella Item": ????, } - game_events = { - "Tower of Death: Stella Item", - "Master's Keep: Whip Trial" - } \ No newline at end of file +game_events = { + "Tower of Death: Stella Item", + "Master's Keep: Whip Trial" +} \ No newline at end of file From add70db007204e3a7df8f7defcf6dbecf4514ba3 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 15 Apr 2026 18:50:39 -0500 Subject: [PATCH 13/58] Add 13th street --- worlds/cv_por/static_location_data.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index 214e9ede5060..d0341ac2b49d 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -20,13 +20,16 @@ "Tower of Death: Second Gears Room Left": 0x22, "Great Stairway: Lower Grand Staircase Corner Nook": 0x23, "Master's Keep: Entrance": 0x24, + "13th Street: Triple Moth Room": 0x29, "Tower of Death: Secret Room": 0x2A, "The Throne Room: Great Stairs Under Stairs": 0x2E, + "13th Street: Behind Final Guard": 0x2F, "Buried Chamber: Right Secret": 0x30, "Tower of Death: Passage Hidden Item": 0x32, "Great Stairway: Left Loft Lower": 0x33, "Entrance: Center Hidden Room": 0x35, "Master's Keep: Hidden Room": 0x38, + "13th Street: Three Amalaric Snipers": 0x39, "The Throne Room: Great Stairs Center": 0x3E, "Buried Chamber: Westmost Room": 0x41, "City of Haze: Register Room": 0x46, @@ -78,6 +81,7 @@ "Great Stairway: Big Underground Room Hidden Item": 0xBF, "Great Stairway: Right Loft": 0xC0, "Great Stairway: Left Loft": 0xC1, + "13th Street: Many Nyxes Room": 0xC4, "The Throne Room: Above Throne Right": 0xC5, "City of Haze: Tunnel Room Right": 0xC8, "Great Stairway: Lower Grand Staircase Upper Alcove": 0xCA, @@ -86,6 +90,7 @@ "Entrance: Hub Room": 0xCD, "Buried Chamber: Eastmost Room": 0xCE, "Tower of Death: Hidden Room": 0xD1, + "13th Street: Train Room Secret Right": 0xD6, "City of Haze: Left Dance Hall Left Item": 0xD8, "Great Stairway: Big Underground Room Lower": 0xDA, "Tower of Death: Elevator Room Middle": 0xDB, @@ -93,16 +98,22 @@ "City of Haze: Axe Armor Item": 0xE0, "City of Haze: Dance Halls Vertical Hallway": 0xE2, "Tower of Death: Above Motorcycles": 0xE3, + "13th Street: Underground Secret Room": 0xE6, "Great Stairway: Big Underground Room Upper": 0xE7, + "13th Street: Underground Demon Room": 0xEA, + "13th Street: Left Big Room": 0xEC, "City of Haze: Bread Shelf": 0xED, "City of Haze: Underground Secret Room": 0xEF, + "13th Street: Secret Floor Room": 0xF3, "City of Haze: Right Dance Hall Item": 0xF7, "Tower of Death: Top Gears Room": 0xF9, + "13th Street: East Courtyard": 0xFA, "City of Haze: Corner Room": 0xFC, "Entrance: Lilith Room": 0x101, "City of Haze: Center Room Top": 0x10E, "Great Stairway: Upper Grand Staircase Middle Alcove": 0x10F, "Buried Chamber: Right Shaft Top": 0x110, + "13th Street: Near Big Room": 0x118, "Great Stairway: Upper Grand Staircase Lower Alcove": 0x11B, "Buried Chamber: West Shaft": 0x124, "Great Stairway: Lower Grand Staircase Middle Alcove": 0x127, @@ -111,11 +122,20 @@ "Tower of Death: Lower Passage Hidden Item": 0x131, "The Throne Room: Great Stairs Hidden": 0x145, "Buried Chamber: Hidden Wall Item": 0x014C, + "13th Street: Bakery Middle": 0x14D, "City of Haze: Cart Secret Right": 0x14E, + "13th Street: Right Dance Hall Top Right": 0x150, + "13th Street: Bakery Left": 0x152, + "13th Street: Train Room Secret Left": 0x153, "City of Haze: Bar Right": 0x154, + "13th Street: Square Room": 0x155, + "13th Street: Bakery Right": 0x156, "Great Stairway: Connector Pipe Left": 0x180, "Great Stairway: Connector Pipe Right": 0x181, "The Throne Room: Great Stairs Left": 0x182, + "13th Street: Right Dance Hall Top Left": 0x183, + + From 17c396dfd9d503c3b28d445d805ff3a209e6d3cc Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 15 Apr 2026 20:16:53 -0500 Subject: [PATCH 14/58] Sandy grave --- worlds/cv_por/static_location_data.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index d0341ac2b49d..725235ea7232 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -9,14 +9,20 @@ "Entrance: Statue Room Above Statue": 0x04, "City of Haze: Post-Boss Room": 0x05, "City of Haze: Heavy Cart Item": 0x06, + "Sandy Grave: Pyramid 5F Nook": 0x07, "Great Stairway: After Keremet Lower": 0x09, "Entrance: Double Room Lower": 0x0A, "Entrance: Above Hub": 0x0E, "Entrance: Double Room Upper": 0x0F, "The Throne Room: Above Throne Left": 0x13, + "Sandy Grave: Boulder Room Tunnel": 0x14, "City of Haze: Before Cart Room": 0x16, "City of Haze: Underground Square Right": 0x17, + "Sandy Grave: Upper Big Underground Top Right": 0x18, + "Sandy Grave: Lower Big Underground Room Bottom": 0x1B, + "Sandy Grave: Left Trap": 0x1C, "Buried Chamber: Central Secret": 0x20, + "Sandy Grave: Sandworm Stomach": 0x21, "Tower of Death: Second Gears Room Left": 0x22, "Great Stairway: Lower Grand Staircase Corner Nook": 0x23, "Master's Keep: Entrance": 0x24, @@ -25,6 +31,7 @@ "The Throne Room: Great Stairs Under Stairs": 0x2E, "13th Street: Behind Final Guard": 0x2F, "Buried Chamber: Right Secret": 0x30, + "Sandy Grave: Bottom Right Room": 0x31, "Tower of Death: Passage Hidden Item": 0x32, "Great Stairway: Left Loft Lower": 0x33, "Entrance: Center Hidden Room": 0x35, @@ -32,11 +39,14 @@ "13th Street: Three Amalaric Snipers": 0x39, "The Throne Room: Great Stairs Center": 0x3E, "Buried Chamber: Westmost Room": 0x41, + "Sandy Grave: Pyramid East 1F": 0x42, "City of Haze: Register Room": 0x46, "City of Haze: Bar Left": 0x47, + "Sandy Grave: Pyramid 3F Lower": 0x49, "Tower of Death: Rampart Room": 0x4A, "Entrance: Beyond Great Armor Hallway": 0x4B, "Tower of Death: Elevator Room Lower": 0x4C, + "Sandy Grave: Upper Big Underground Top Left": 0x4F, "Great Stairway: Upper Grand Staircase Top Left Item": 0x53, "City of Haze: Axe Courtyard": 0x54, "Tower of Death: Top of the Tower": 0x55, @@ -51,7 +61,9 @@ "City of Haze: Right Corner Room": 0x64, "City of Haze: Left Plaze": 0x65, "Great Stairway: Underground Hidden Room": 0x66, + "Sandy Grave: Lower Big Underground Room Top": 0x67, "City of Haze: Center Room Lower": 0x6E, + "Sandy Grave: On Statue": 0x6F, "Tower of Death: Third Gears Room": 0x70, "Entrance: Above Metal Block Room": 0x71, "Great Stairway: Before Keremet Room": 0x76, @@ -69,11 +81,14 @@ "City of Haze: Central Hallway Lower": 0x8C, "Buried Chamber: Central Hall": 0x92, "Great Stairway: Central Nook": 0x93, + "Sandy Grave: Eastmost Item": 0x94, + "Sandy Grave: Behind Bricks": 0x95, "City of Haze: Underground Square Left": 0xA0, "Great Stairway: Upper Grand Staircase Upper Alcove": 0xA1, "City of Haze: Right Dance Hall Secret Room": 0xA7, "Buried Chamber: Final Shaft Item": 0xAA, "Great Stairway: Central Upper": 0xAB, + "Sandy Grave: Boulder Room Corner Alcove": 0xAE, "City of Haze: Cart Secret Left": 0xB0, "City of Haze: Winding Rooms": 0xB3, "Great Stairway: Lower Grand Staircase Lower Alcove": 0xB4, @@ -96,30 +111,40 @@ "Tower of Death: Elevator Room Middle": 0xDB, "City of Haze: First Passsage": 0xDD, "City of Haze: Axe Armor Item": 0xE0, + "Sandy Grave: Giant Shaft Upper Item": 0xE1, "City of Haze: Dance Halls Vertical Hallway": 0xE2, "Tower of Death: Above Motorcycles": 0xE3, "13th Street: Underground Secret Room": 0xE6, "Great Stairway: Big Underground Room Upper": 0xE7, "13th Street: Underground Demon Room": 0xEA, + "Sandy Grave: Mega Ghost Room Lower": 0xEB, "13th Street: Left Big Room": 0xEC, "City of Haze: Bread Shelf": 0xED, "City of Haze: Underground Secret Room": 0xEF, + "Sandy Grave: Mega Ghost Room Upper": 0xF0, "13th Street: Secret Floor Room": 0xF3, "City of Haze: Right Dance Hall Item": 0xF7, "Tower of Death: Top Gears Room": 0xF9, "13th Street: East Courtyard": 0xFA, "City of Haze: Corner Room": 0xFC, + "Sandy Grave: Giant Shaft Middle Item": 0xFD, "Entrance: Lilith Room": 0x101, + "Sandy Grave: Right Trap": 0x103, "City of Haze: Center Room Top": 0x10E, "Great Stairway: Upper Grand Staircase Middle Alcove": 0x10F, "Buried Chamber: Right Shaft Top": 0x110, + "Sandy Grave: Pyramid 3F Upper": 0x113, + "Sandy Grave: Lonely Mimic Alcove": 0x116, "13th Street: Near Big Room": 0x118, "Great Stairway: Upper Grand Staircase Lower Alcove": 0x11B, + "Sandy Grave: Fleaman Pit": 0x11E, "Buried Chamber: West Shaft": 0x124, "Great Stairway: Lower Grand Staircase Middle Alcove": 0x127, + "Sandy Grave: Pyramid East 5F": 0x173 "Entrance: Behemoth Chase Room": 0x12C, "Entrance: First Passage Hidden Wall": 0x12D, "Tower of Death: Lower Passage Hidden Item": 0x131, + "Sandy Grave: Giant Shaft Low Item": 0x144, "The Throne Room: Great Stairs Hidden": 0x145, "Buried Chamber: Hidden Wall Item": 0x014C, "13th Street: Bakery Middle": 0x14D, @@ -144,6 +169,8 @@ + + #"Master's Keep: Whip Trial": ????? #"Tower of Death: Stella Item": ????, From b62480f2c446f6f1907153242df4cf6adc7117c6 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 15 Apr 2026 21:39:49 -0500 Subject: [PATCH 15/58] Forgor city --- worlds/cv_por/static_location_data.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index 725235ea7232..639f920da16f 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -11,7 +11,7 @@ "City of Haze: Heavy Cart Item": 0x06, "Sandy Grave: Pyramid 5F Nook": 0x07, "Great Stairway: After Keremet Lower": 0x09, - "Entrance: Double Room Lower": 0x0A, + "Forgotten City: Pyramid 1F Bricks": 0x0A, "Entrance: Above Hub": 0x0E, "Entrance: Double Room Upper": 0x0F, "The Throne Room: Above Throne Left": 0x13, @@ -26,6 +26,7 @@ "Tower of Death: Second Gears Room Left": 0x22, "Great Stairway: Lower Grand Staircase Corner Nook": 0x23, "Master's Keep: Entrance": 0x24, + "Forgotten City: Big Shaft Room Left": 0x26, "13th Street: Triple Moth Room": 0x29, "Tower of Death: Secret Room": 0x2A, "The Throne Room: Great Stairs Under Stairs": 0x2E, @@ -35,9 +36,12 @@ "Tower of Death: Passage Hidden Item": 0x32, "Great Stairway: Left Loft Lower": 0x33, "Entrance: Center Hidden Room": 0x35, + "Forgotten City: Big Shaft Room Right": 0x36, "Master's Keep: Hidden Room": 0x38, "13th Street: Three Amalaric Snipers": 0x39, "The Throne Room: Great Stairs Center": 0x3E, + "Forgotten City: Upper Underground Square": 0x3F, + "Entrance: Double Room Lower": 0x40, "Buried Chamber: Westmost Room": 0x41, "Sandy Grave: Pyramid East 1F": 0x42, "City of Haze: Register Room": 0x46, @@ -46,6 +50,7 @@ "Tower of Death: Rampart Room": 0x4A, "Entrance: Beyond Great Armor Hallway": 0x4B, "Tower of Death: Elevator Room Lower": 0x4C, + "Forgotten City: Outside Left": 0x4E, "Sandy Grave: Upper Big Underground Top Left": 0x4F, "Great Stairway: Upper Grand Staircase Top Left Item": 0x53, "City of Haze: Axe Courtyard": 0x54, @@ -62,11 +67,13 @@ "City of Haze: Left Plaze": 0x65, "Great Stairway: Underground Hidden Room": 0x66, "Sandy Grave: Lower Big Underground Room Top": 0x67, + "Forgotten City: Lower Boulder Room Tunnel Alcove": 0x69, "City of Haze: Center Room Lower": 0x6E, "Sandy Grave: On Statue": 0x6F, "Tower of Death: Third Gears Room": 0x70, "Entrance: Above Metal Block Room": 0x71, "Great Stairway: Before Keremet Room": 0x76, + "Forgotten City: Lower Boulder Room Upper Alcove": 0x77, "Entrance: First Passage Upper Item": 0x78, "Great Stairway: Upper Grand Staircase Corner Nook": 0x79, "Entrance: Right Hidden Room": 0x7B, @@ -89,9 +96,11 @@ "Buried Chamber: Final Shaft Item": 0xAA, "Great Stairway: Central Upper": 0xAB, "Sandy Grave: Boulder Room Corner Alcove": 0xAE, + "Forgotten City: Post-Boss Item": 0xAF, "City of Haze: Cart Secret Left": 0xB0, "City of Haze: Winding Rooms": 0xB3, "Great Stairway: Lower Grand Staircase Lower Alcove": 0xB4, + "Forgotten City: Bottom Chamber": 0xB9, "City of Haze: Meat Room": 0xBB, "Great Stairway: Big Underground Room Hidden Item": 0xBF, "Great Stairway: Right Loft": 0xC0, @@ -105,11 +114,13 @@ "Entrance: Hub Room": 0xCD, "Buried Chamber: Eastmost Room": 0xCE, "Tower of Death: Hidden Room": 0xD1, + "Forgotten City: Pyramid 5F": 0xD5, "13th Street: Train Room Secret Right": 0xD6, "City of Haze: Left Dance Hall Left Item": 0xD8, "Great Stairway: Big Underground Room Lower": 0xDA, "Tower of Death: Elevator Room Middle": 0xDB, "City of Haze: First Passsage": 0xDD, + "Forgotten City: Gorgon Room": 0xDE, "City of Haze: Axe Armor Item": 0xE0, "Sandy Grave: Giant Shaft Upper Item": 0xE1, "City of Haze: Dance Halls Vertical Hallway": 0xE2, @@ -130,20 +141,23 @@ "Sandy Grave: Giant Shaft Middle Item": 0xFD, "Entrance: Lilith Room": 0x101, "Sandy Grave: Right Trap": 0x103, + "Forgotten City: Lower Underground Square Upper Item": 0x105, "City of Haze: Center Room Top": 0x10E, "Great Stairway: Upper Grand Staircase Middle Alcove": 0x10F, "Buried Chamber: Right Shaft Top": 0x110, "Sandy Grave: Pyramid 3F Upper": 0x113, "Sandy Grave: Lonely Mimic Alcove": 0x116, "13th Street: Near Big Room": 0x118, + "Forgotten City: Pyramid East 3F": 0x119, "Great Stairway: Upper Grand Staircase Lower Alcove": 0x11B, "Sandy Grave: Fleaman Pit": 0x11E, + "Forgotten City: Pyramid East 5F": 0x122, "Buried Chamber: West Shaft": 0x124, "Great Stairway: Lower Grand Staircase Middle Alcove": 0x127, - "Sandy Grave: Pyramid East 5F": 0x173 "Entrance: Behemoth Chase Room": 0x12C, "Entrance: First Passage Hidden Wall": 0x12D, "Tower of Death: Lower Passage Hidden Item": 0x131, + "Forgotten City: Armored Fleaman Pit": 0x137, "Sandy Grave: Giant Shaft Low Item": 0x144, "The Throne Room: Great Stairs Hidden": 0x145, "Buried Chamber: Hidden Wall Item": 0x014C, @@ -155,10 +169,13 @@ "City of Haze: Bar Right": 0x154, "13th Street: Square Room": 0x155, "13th Street: Bakery Right": 0x156, + "Sandy Grave: Pyramid East 5F": 0x173 "Great Stairway: Connector Pipe Left": 0x180, "Great Stairway: Connector Pipe Right": 0x181, "The Throne Room: Great Stairs Left": 0x182, "13th Street: Right Dance Hall Top Left": 0x183, + "Forgotten City: Pyramid East 1F": 0x185, + "Forgotten City: Lower Underground Square Lower Item": 0x186, From 25a55218131d0b78d2f13a1a74b75dbee932c544 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 15 Apr 2026 22:45:44 -0500 Subject: [PATCH 16/58] Nation of fools --- worlds/cv_por/static_location_data.py | 33 ++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index 639f920da16f..7e097aa4564f 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -12,13 +12,17 @@ "Sandy Grave: Pyramid 5F Nook": 0x07, "Great Stairway: After Keremet Lower": 0x09, "Forgotten City: Pyramid 1F Bricks": 0x0A, + "Nation of Fools: Central Top Shaft": 0x0D, "Entrance: Above Hub": 0x0E, "Entrance: Double Room Upper": 0x0F, + "Nation of Fools: Legion Arena": 0x10, "The Throne Room: Above Throne Left": 0x13, "Sandy Grave: Boulder Room Tunnel": 0x14, "City of Haze: Before Cart Room": 0x16, "City of Haze: Underground Square Right": 0x17, "Sandy Grave: Upper Big Underground Top Right": 0x18, + "Nation of Fools: Bottom Corner Room": 0x19, + "Nation of Fools: Mid-Right Corner Room": 0x1A, "Sandy Grave: Lower Big Underground Room Bottom": 0x1B, "Sandy Grave: Left Trap": 0x1C, "Buried Chamber: Central Secret": 0x20, @@ -27,8 +31,10 @@ "Great Stairway: Lower Grand Staircase Corner Nook": 0x23, "Master's Keep: Entrance": 0x24, "Forgotten City: Big Shaft Room Left": 0x26, + "Nation of Fools: Left Upper Corner Room": 0x27, "13th Street: Triple Moth Room": 0x29, "Tower of Death: Secret Room": 0x2A, + "Nation of Fools: Right Horizontal Room": 0x2B, "The Throne Room: Great Stairs Under Stairs": 0x2E, "13th Street: Behind Final Guard": 0x2F, "Buried Chamber: Right Secret": 0x30, @@ -37,8 +43,10 @@ "Great Stairway: Left Loft Lower": 0x33, "Entrance: Center Hidden Room": 0x35, "Forgotten City: Big Shaft Room Right": 0x36, + "Nation of Fools: Right Bottom Corner Room": 0x37, "Master's Keep: Hidden Room": 0x38, "13th Street: Three Amalaric Snipers": 0x39, + "Nation of Fools: Top Corner Room": 0x3B, "The Throne Room: Great Stairs Center": 0x3E, "Forgotten City: Upper Underground Square": 0x3F, "Entrance: Double Room Lower": 0x40, @@ -46,6 +54,7 @@ "Sandy Grave: Pyramid East 1F": 0x42, "City of Haze: Register Room": 0x46, "City of Haze: Bar Left": 0x47, + "Nation of Fools: Bottom-Left Room Building": 0x48, "Sandy Grave: Pyramid 3F Lower": 0x49, "Tower of Death: Rampart Room": 0x4A, "Entrance: Beyond Great Armor Hallway": 0x4B, @@ -67,6 +76,7 @@ "City of Haze: Left Plaze": 0x65, "Great Stairway: Underground Hidden Room": 0x66, "Sandy Grave: Lower Big Underground Room Top": 0x67, + "Nation of Fools: Upper Upside Down": 0x68, "Forgotten City: Lower Boulder Room Tunnel Alcove": 0x69, "City of Haze: Center Room Lower": 0x6E, "Sandy Grave: On Statue": 0x6F, @@ -93,6 +103,7 @@ "City of Haze: Underground Square Left": 0xA0, "Great Stairway: Upper Grand Staircase Upper Alcove": 0xA1, "City of Haze: Right Dance Hall Secret Room": 0xA7, + "Nation of Fools: Bottom Left Medium Square On Wall": 0xA8, "Buried Chamber: Final Shaft Item": 0xAA, "Great Stairway: Central Upper": 0xAB, "Sandy Grave: Boulder Room Corner Alcove": 0xAE, @@ -105,6 +116,7 @@ "Great Stairway: Big Underground Room Hidden Item": 0xBF, "Great Stairway: Right Loft": 0xC0, "Great Stairway: Left Loft": 0xC1, + "Nation of Fools: Bottom-Right Room Left Building": 0xC2, "13th Street: Many Nyxes Room": 0xC4, "The Throne Room: Above Throne Right": 0xC5, "City of Haze: Tunnel Room Right": 0xC8, @@ -114,17 +126,20 @@ "Entrance: Hub Room": 0xCD, "Buried Chamber: Eastmost Room": 0xCE, "Tower of Death: Hidden Room": 0xD1, + "Nation of Fools: Bottom-Right Room Middle Building": 0xD4, "Forgotten City: Pyramid 5F": 0xD5, "13th Street: Train Room Secret Right": 0xD6, "City of Haze: Left Dance Hall Left Item": 0xD8, "Great Stairway: Big Underground Room Lower": 0xDA, "Tower of Death: Elevator Room Middle": 0xDB, + "Nation of Fools: Crevice Item": 0xDC, "City of Haze: First Passsage": 0xDD, "Forgotten City: Gorgon Room": 0xDE, "City of Haze: Axe Armor Item": 0xE0, "Sandy Grave: Giant Shaft Upper Item": 0xE1, "City of Haze: Dance Halls Vertical Hallway": 0xE2, "Tower of Death: Above Motorcycles": 0xE3, + "Nation of Fools: Top-Right Room Axe Item": 0xE4, "13th Street: Underground Secret Room": 0xE6, "Great Stairway: Big Underground Room Upper": 0xE7, "13th Street: Underground Demon Room": 0xEA, @@ -141,16 +156,20 @@ "Sandy Grave: Giant Shaft Middle Item": 0xFD, "Entrance: Lilith Room": 0x101, "Sandy Grave: Right Trap": 0x103, + "Nation of Fools: Left Bottom Corner Room": 0x104, "Forgotten City: Lower Underground Square Upper Item": 0x105, "City of Haze: Center Room Top": 0x10E, "Great Stairway: Upper Grand Staircase Middle Alcove": 0x10F, "Buried Chamber: Right Shaft Top": 0x110, "Sandy Grave: Pyramid 3F Upper": 0x113, + "Nation of Fools: Bottom Right Medium Square On Wall": 0x114, "Sandy Grave: Lonely Mimic Alcove": 0x116, "13th Street: Near Big Room": 0x118, "Forgotten City: Pyramid East 3F": 0x119, "Great Stairway: Upper Grand Staircase Lower Alcove": 0x11B, + "Nation of Fools: Right Vertical Room": 0x11C, "Sandy Grave: Fleaman Pit": 0x11E, + "Nation of Fools: Top-Right Room Bottom Item": 0x11F, "Forgotten City: Pyramid East 5F": 0x122, "Buried Chamber: West Shaft": 0x124, "Great Stairway: Lower Grand Staircase Middle Alcove": 0x127, @@ -158,8 +177,11 @@ "Entrance: First Passage Hidden Wall": 0x12D, "Tower of Death: Lower Passage Hidden Item": 0x131, "Forgotten City: Armored Fleaman Pit": 0x137, + "Nation of Fools: Top-Left Room Right Building": 0x13F, "Sandy Grave: Giant Shaft Low Item": 0x144, "The Throne Room: Great Stairs Hidden": 0x145, + "Nation of Fools: Top-Right Room Breakable Wall": 0x146, + "Nation of Fools: Right Center Room": 0x149, "Buried Chamber: Hidden Wall Item": 0x014C, "13th Street: Bakery Middle": 0x14D, "City of Haze: Cart Secret Right": 0x14E, @@ -169,7 +191,10 @@ "City of Haze: Bar Right": 0x154, "13th Street: Square Room": 0x155, "13th Street: Bakery Right": 0x156, - "Sandy Grave: Pyramid East 5F": 0x173 + "Nation of Fools: Left Center Room": 0x15F, + "Nation of Fools: Upside Down Building": 0x161, + "Nation of Fools: Secret Room": 0x166, + "Sandy Grave: Pyramid East 5F": 0x173, "Great Stairway: Connector Pipe Left": 0x180, "Great Stairway: Connector Pipe Right": 0x181, "The Throne Room: Great Stairs Left": 0x182, @@ -188,6 +213,12 @@ + + + + + + #"Master's Keep: Whip Trial": ????? #"Tower of Death: Stella Item": ????, From d9006ff209c805ee597735c0842d70183e4733a6 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 15 Apr 2026 23:03:06 -0500 Subject: [PATCH 17/58] Burnt paradise --- worlds/cv_por/static_location_data.py | 31 ++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index 7e097aa4564f..a8f03fee4ee2 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -35,6 +35,8 @@ "13th Street: Triple Moth Room": 0x29, "Tower of Death: Secret Room": 0x2A, "Nation of Fools: Right Horizontal Room": 0x2B, + "Burnt Paradise: Left Lower Isolated Room": 0x2C, + "Burnt Paradise: Right Lower Isolated Room": 0x2D, "The Throne Room: Great Stairs Under Stairs": 0x2E, "13th Street: Behind Final Guard": 0x2F, "Buried Chamber: Right Secret": 0x30, @@ -47,6 +49,8 @@ "Master's Keep: Hidden Room": 0x38, "13th Street: Three Amalaric Snipers": 0x39, "Nation of Fools: Top Corner Room": 0x3B, + "Burnt Paradise: Left Upper Isolated Room": 0x3C, + "Burnt Paradise: Right Upper Isolated Room": 0x3D, "The Throne Room: Great Stairs Center": 0x3E, "Forgotten City: Upper Underground Square": 0x3F, "Entrance: Double Room Lower": 0x40, @@ -61,6 +65,7 @@ "Tower of Death: Elevator Room Lower": 0x4C, "Forgotten City: Outside Left": 0x4E, "Sandy Grave: Upper Big Underground Top Left": 0x4F, + "Burnt Paradise: Left Upper Big Corner": 0x51, "Great Stairway: Upper Grand Staircase Top Left Item": 0x53, "City of Haze: Axe Courtyard": 0x54, "Tower of Death: Top of the Tower": 0x55, @@ -82,20 +87,24 @@ "Sandy Grave: On Statue": 0x6F, "Tower of Death: Third Gears Room": 0x70, "Entrance: Above Metal Block Room": 0x71, + "Burnt Paradise: Bottom Corner": 0x73, "Great Stairway: Before Keremet Room": 0x76, "Forgotten City: Lower Boulder Room Upper Alcove": 0x77, "Entrance: First Passage Upper Item": 0x78, "Great Stairway: Upper Grand Staircase Corner Nook": 0x79, + "Burnt Paradise: Bottom Right Big Room": 0x7A, "Entrance: Right Hidden Room": 0x7B, "Entrance: First Passage Lower Item": 0x7C, "City of Haze: Left Dance Hall Right Item": 0x7E, "City of Haze: Tunnel Room Left": 0x7F, + "Burnt Paradise: Upper Left Big Room Middle Building": 0x82, "City of Haze: Chunky Room": 0x84, "Entrance: Pre-Behemoth Boss Room": 0x88, "Great Stairway: After Keremet Upper": 0x89, "Tower of Death: Gear Belt Room": 0x8A, "City of Haze: Dance Halls Basement": 0x8B, "City of Haze: Central Hallway Lower": 0x8C, + "Burnt Paradise: Upper Right Big Room Right Side": 0x8F, "Buried Chamber: Central Hall": 0x92, "Great Stairway: Central Nook": 0x93, "Sandy Grave: Eastmost Item": 0x94, @@ -142,12 +151,15 @@ "Nation of Fools: Top-Right Room Axe Item": 0xE4, "13th Street: Underground Secret Room": 0xE6, "Great Stairway: Big Underground Room Upper": 0xE7, + "Burnt Paradise: Upper Left Big Room Left Building": 0xE9, "13th Street: Underground Demon Room": 0xEA, "Sandy Grave: Mega Ghost Room Lower": 0xEB, "13th Street: Left Big Room": 0xEC, "City of Haze: Bread Shelf": 0xED, + "Burnt Paradise: Upper Left Lower Corner Upright Building": 0xEE, "City of Haze: Underground Secret Room": 0xEF, "Sandy Grave: Mega Ghost Room Upper": 0xF0, + "Burnt Paradise: Lower Left Big Room": 0xF1, "13th Street: Secret Floor Room": 0xF3, "City of Haze: Right Dance Hall Item": 0xF7, "Tower of Death: Top Gears Room": 0xF9, @@ -171,6 +183,7 @@ "Sandy Grave: Fleaman Pit": 0x11E, "Nation of Fools: Top-Right Room Bottom Item": 0x11F, "Forgotten City: Pyramid East 5F": 0x122, + "Burnt Paradise: Lower Vertical Hall": 0x123, "Buried Chamber: West Shaft": 0x124, "Great Stairway: Lower Grand Staircase Middle Alcove": 0x127, "Entrance: Behemoth Chase Room": 0x12C, @@ -181,7 +194,10 @@ "Sandy Grave: Giant Shaft Low Item": 0x144, "The Throne Room: Great Stairs Hidden": 0x145, "Nation of Fools: Top-Right Room Breakable Wall": 0x146, + "Burnt Paradise: Bottom Right Breakable Wall": 0x147, + "Burnt Paradise: Right Upper Big Corner On Wall": 0x148, "Nation of Fools: Right Center Room": 0x149, + "Burnt Paradise: Left Big Corner": 0x14A, "Buried Chamber: Hidden Wall Item": 0x014C, "13th Street: Bakery Middle": 0x14D, "City of Haze: Cart Secret Right": 0x14E, @@ -191,9 +207,11 @@ "City of Haze: Bar Right": 0x154, "13th Street: Square Room": 0x155, "13th Street: Bakery Right": 0x156, + "Burnt Paradise: Bottom Right Corner Room": 0x157, "Nation of Fools: Left Center Room": 0x15F, "Nation of Fools: Upside Down Building": 0x161, "Nation of Fools: Secret Room": 0x166, + "Burnt Paradise: Upper Right Big Room": 0x16B, "Sandy Grave: Pyramid East 5F": 0x173, "Great Stairway: Connector Pipe Left": 0x180, "Great Stairway: Connector Pipe Right": 0x181, @@ -205,18 +223,7 @@ - - - - - - - - - - - - + #"Master's Keep: Whip Trial": ????? From 7d0c3b39e9ce3269aa4ad1223cbbed18739b3dd1 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 15 Apr 2026 23:25:22 -0500 Subject: [PATCH 18/58] Forest of doom --- worlds/cv_por/static_location_data.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index a8f03fee4ee2..ef2a4c23a709 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -16,6 +16,7 @@ "Entrance: Above Hub": 0x0E, "Entrance: Double Room Upper": 0x0F, "Nation of Fools: Legion Arena": 0x10, + "Forest of Doom: Post-Boss Room": 0x11, "The Throne Room: Above Throne Left": 0x13, "Sandy Grave: Boulder Room Tunnel": 0x14, "City of Haze: Before Cart Room": 0x16, @@ -30,6 +31,7 @@ "Tower of Death: Second Gears Room Left": 0x22, "Great Stairway: Lower Grand Staircase Corner Nook": 0x23, "Master's Keep: Entrance": 0x24, + "Forest of Doom: Left Atrium Big Room": 0x25, "Forgotten City: Big Shaft Room Left": 0x26, "Nation of Fools: Left Upper Corner Room": 0x27, "13th Street: Triple Moth Room": 0x29, @@ -63,6 +65,7 @@ "Tower of Death: Rampart Room": 0x4A, "Entrance: Beyond Great Armor Hallway": 0x4B, "Tower of Death: Elevator Room Lower": 0x4C, + "Forest of Doom: Right Atrium East Square Room Right": 0x4D, "Forgotten City: Outside Left": 0x4E, "Sandy Grave: Upper Big Underground Top Left": 0x4F, "Burnt Paradise: Left Upper Big Corner": 0x51, @@ -83,11 +86,13 @@ "Sandy Grave: Lower Big Underground Room Top": 0x67, "Nation of Fools: Upper Upside Down": 0x68, "Forgotten City: Lower Boulder Room Tunnel Alcove": 0x69, + "Forest of Doom: Secret Cave Room": 0x6C, "City of Haze: Center Room Lower": 0x6E, "Sandy Grave: On Statue": 0x6F, "Tower of Death: Third Gears Room": 0x70, "Entrance: Above Metal Block Room": 0x71, "Burnt Paradise: Bottom Corner": 0x73, + "Forest of Doom: Left Atrium Top": 0x75, "Great Stairway: Before Keremet Room": 0x76, "Forgotten City: Lower Boulder Room Upper Alcove": 0x77, "Entrance: First Passage Upper Item": 0x78, @@ -115,11 +120,13 @@ "Nation of Fools: Bottom Left Medium Square On Wall": 0xA8, "Buried Chamber: Final Shaft Item": 0xAA, "Great Stairway: Central Upper": 0xAB, + "Forest of Doom: Right Atrium East Top Room": 0xAC, "Sandy Grave: Boulder Room Corner Alcove": 0xAE, "Forgotten City: Post-Boss Item": 0xAF, "City of Haze: Cart Secret Left": 0xB0, "City of Haze: Winding Rooms": 0xB3, "Great Stairway: Lower Grand Staircase Lower Alcove": 0xB4, + "Forest of Doom: Right Atrium East Square Room Left": 0xB5, "Forgotten City: Bottom Chamber": 0xB9, "City of Haze: Meat Room": 0xBB, "Great Stairway: Big Underground Room Hidden Item": 0xBF, @@ -151,6 +158,7 @@ "Nation of Fools: Top-Right Room Axe Item": 0xE4, "13th Street: Underground Secret Room": 0xE6, "Great Stairway: Big Underground Room Upper": 0xE7, + "Forest of Doom: Right Atrium West Top Room": 0xE8, "Burnt Paradise: Upper Left Big Room Left Building": 0xE9, "13th Street: Underground Demon Room": 0xEA, "Sandy Grave: Mega Ghost Room Lower": 0xEB, @@ -191,6 +199,7 @@ "Tower of Death: Lower Passage Hidden Item": 0x131, "Forgotten City: Armored Fleaman Pit": 0x137, "Nation of Fools: Top-Left Room Right Building": 0x13F, + "Forest of Doom: Left Atrium Breakable Wall": 0x140, "Sandy Grave: Giant Shaft Low Item": 0x144, "The Throne Room: Great Stairs Hidden": 0x145, "Nation of Fools: Top-Right Room Breakable Wall": 0x146, @@ -211,8 +220,11 @@ "Nation of Fools: Left Center Room": 0x15F, "Nation of Fools: Upside Down Building": 0x161, "Nation of Fools: Secret Room": 0x166, + "Forest of Doom: Left Atrium Middle Room": 0x167, "Burnt Paradise: Upper Right Big Room": 0x16B, + "Forest of Doom: Near Cave Save Breakable Wall": 0x16C, "Sandy Grave: Pyramid East 5F": 0x173, + "Forest of Doom: Left Atrium Upper Room": 0x177, "Great Stairway: Connector Pipe Left": 0x180, "Great Stairway: Connector Pipe Right": 0x181, "The Throne Room: Great Stairs Left": 0x182, @@ -223,6 +235,13 @@ + + + + + + + From 6038f59a682bffa9c7ff2195280a8db3f6b033fd Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Thu, 16 Apr 2026 00:12:31 -0500 Subject: [PATCH 19/58] Dark academy --- worlds/cv_por/static_location_data.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index ef2a4c23a709..b646f938c429 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -17,6 +17,7 @@ "Entrance: Double Room Upper": 0x0F, "Nation of Fools: Legion Arena": 0x10, "Forest of Doom: Post-Boss Room": 0x11, + "Dark Academy: Right Building Center Room": 0x12, "The Throne Room: Above Throne Left": 0x13, "Sandy Grave: Boulder Room Tunnel": 0x14, "City of Haze: Before Cart Room": 0x16, @@ -34,6 +35,7 @@ "Forest of Doom: Left Atrium Big Room": 0x25, "Forgotten City: Big Shaft Room Left": 0x26, "Nation of Fools: Left Upper Corner Room": 0x27, + "Dark Academy: Main Building Ghoul Room": 0x28, "13th Street: Triple Moth Room": 0x29, "Tower of Death: Secret Room": 0x2A, "Nation of Fools: Right Horizontal Room": 0x2B, @@ -50,6 +52,7 @@ "Nation of Fools: Right Bottom Corner Room": 0x37, "Master's Keep: Hidden Room": 0x38, "13th Street: Three Amalaric Snipers": 0x39, + "Dark Academy: Main Building Three Toads": 0x3A, "Nation of Fools: Top Corner Room": 0x3B, "Burnt Paradise: Left Upper Isolated Room": 0x3C, "Burnt Paradise: Right Upper Isolated Room": 0x3D, @@ -86,11 +89,14 @@ "Sandy Grave: Lower Big Underground Room Top": 0x67, "Nation of Fools: Upper Upside Down": 0x68, "Forgotten City: Lower Boulder Room Tunnel Alcove": 0x69, + "Dark Academy: Main Building Secret Ceiling": 0x6B, "Forest of Doom: Secret Cave Room": 0x6C, + "Nest of Evil: Doppelganger Reward": 0x6D, "City of Haze: Center Room Lower": 0x6E, "Sandy Grave: On Statue": 0x6F, "Tower of Death: Third Gears Room": 0x70, "Entrance: Above Metal Block Room": 0x71, + "Dark Academy: Main Building Entrance": 0x72, "Burnt Paradise: Bottom Corner": 0x73, "Forest of Doom: Left Atrium Top": 0x75, "Great Stairway: Before Keremet Room": 0x76, @@ -121,6 +127,7 @@ "Buried Chamber: Final Shaft Item": 0xAA, "Great Stairway: Central Upper": 0xAB, "Forest of Doom: Right Atrium East Top Room": 0xAC, + "Dark Academy: Right Building Top Floor": 0xAD, "Sandy Grave: Boulder Room Corner Alcove": 0xAE, "Forgotten City: Post-Boss Item": 0xAF, "City of Haze: Cart Secret Left": 0xB0, @@ -156,6 +163,7 @@ "City of Haze: Dance Halls Vertical Hallway": 0xE2, "Tower of Death: Above Motorcycles": 0xE3, "Nation of Fools: Top-Right Room Axe Item": 0xE4, + "Dark Academy: Main Building Secret Malphas Room": 0xE5, "13th Street: Underground Secret Room": 0xE6, "Great Stairway: Big Underground Room Upper": 0xE7, "Forest of Doom: Right Atrium West Top Room": 0xE8, @@ -197,6 +205,7 @@ "Entrance: Behemoth Chase Room": 0x12C, "Entrance: First Passage Hidden Wall": 0x12D, "Tower of Death: Lower Passage Hidden Item": 0x131, + "Dark Academy: West Building Breakable Wall": 0x134, "Forgotten City: Armored Fleaman Pit": 0x137, "Nation of Fools: Top-Left Room Right Building": 0x13F, "Forest of Doom: Left Atrium Breakable Wall": 0x140, @@ -218,17 +227,23 @@ "13th Street: Bakery Right": 0x156, "Burnt Paradise: Bottom Right Corner Room": 0x157, "Nation of Fools: Left Center Room": 0x15F, + "Dark Academy: Main Building Square Room Breakable Wall": 0x160, "Nation of Fools: Upside Down Building": 0x161, + "Dark Academy: Main Building Minotaur Room": 0x163, + "Dark Academy: West Building Top Floor Right": 0x164, "Nation of Fools: Secret Room": 0x166, "Forest of Doom: Left Atrium Middle Room": 0x167, "Burnt Paradise: Upper Right Big Room": 0x16B, "Forest of Doom: Near Cave Save Breakable Wall": 0x16C, "Sandy Grave: Pyramid East 5F": 0x173, + "Nest of Evil: First Item": 0x175, + "Nest of Evil: Second Item": 0x176, "Forest of Doom: Left Atrium Upper Room": 0x177, "Great Stairway: Connector Pipe Left": 0x180, "Great Stairway: Connector Pipe Right": 0x181, "The Throne Room: Great Stairs Left": 0x182, "13th Street: Right Dance Hall Top Left": 0x183, + "Dark Academy: West Building Top Floor Left": 0x184, "Forgotten City: Pyramid East 1F": 0x185, "Forgotten City: Lower Underground Square Lower Item": 0x186, @@ -236,6 +251,14 @@ + + + + + + + + From 2a6c4a4796c9da6cab04526f751bafd18f3c8e15 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Thu, 16 Apr 2026 00:18:58 -0500 Subject: [PATCH 20/58] add quests --- worlds/cv_por/static_location_data.py | 64 ++++++++++++++++----------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index b646f938c429..09702bfb93c6 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -246,33 +246,45 @@ "Dark Academy: West Building Top Floor Left": 0x184, "Forgotten City: Pyramid East 1F": 0x185, "Forgotten City: Lower Underground Square Lower Item": 0x186, + "Tower of Death: Stella Item": 0x187, + "Master's Keep: Whip Trial": 0x188, - - - - - - - - - - - - - - - - - - - - - - #"Master's Keep: Whip Trial": ????? - #"Tower of Death: Stella Item": ????, - - - + "Quest: Preparations": 0x0200, + "Quest: Supersonic Punch": 0x0201, + "Quest: Ghosts of the Desert": 0x0202, + "Quest: Defender of the Stairs": 0x0203, + "Quest: The Spinning Art": 0x0204, + "Quest: Art of the Zephyr": 0x0205, + "Quest: Find the King of Birds": 0x0206, + "Quest: Overcome the Curse": 0x0207, + "Quest: The Statue's Tear": 0x0208, + "Quest: The Martial Art": 0x0209, + "Quest: Holy Appearance": 0x020A, + "Quest: Number of Fortune": 0x020B, + "Quest: Mental Training 1": 0x020C, + "Quest: Mental Training 2": 0x020D, + "Quest: The Spear of Legend": 0x020E, + "Quest: Mental Training 3": 0x020F, + "Quest: Defeat the Ghoul King": 0x0211, + "Quest: Abandon Greed": 0x0212, + "Quest: A Rank Hunter": 0x0213, + "Quest: Mental Training 4": 0x0214, + "Quest: S Rank Hunter": 0x0215, + "Quest: The Gambler": 0x0216, + "Quest: Hands of the Clock": 0x0217, + "Quest: Poison vs. Poison": 0x0218, + "Quest: Build Your Strength 1": 0x0219, + "Quest: Build Your Strength 2": 0x021A, + "Quest: The Lonely Stage": 0x021B, + "Quest: Build Your Strength 3": 0x021C, + "Quest: Pray Before the Cross": 0x021D, + "Quest: Build Your Strength 4": 0x021E, + "Quest: Lost Page": 0x021F, + "Quest: The Hundred Tasks": 0x0220, + "Quest: Master the Holy Power": 0x0221, + "Quest: Almighty": 0x0222, + "Quets: The Great Sage": 0x0223, + "Quest: Kill Gergoth": 0x0224 } game_events = { From 284279c6fcfb733952c257f12ac6dd07638359e5 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Mon, 20 Apr 2026 00:25:26 -0500 Subject: [PATCH 21/58] Add regions --- worlds/cv_por/Locations.py | 19 ++ worlds/cv_por/Options.py | 8 +- worlds/cv_por/Regions.py | 241 ++++++++++++++++++++++++++ worlds/cv_por/__init__.py | 11 +- worlds/cv_por/generator_main.py | 17 +- worlds/cv_por/setup_game.py | 16 ++ worlds/cv_por/static_location_data.py | 2 +- 7 files changed, 307 insertions(+), 7 deletions(-) create mode 100644 worlds/cv_por/Locations.py create mode 100644 worlds/cv_por/Regions.py create mode 100644 worlds/cv_por/setup_game.py diff --git a/worlds/cv_por/Locations.py b/worlds/cv_por/Locations.py new file mode 100644 index 000000000000..348fd6098238 --- /dev/null +++ b/worlds/cv_por/Locations.py @@ -0,0 +1,19 @@ +from typing import List, Optional, NamedTuple, TYPE_CHECKING +from .static_location_data import location_ids + +if TYPE_CHECKING: + from . import PoRWorld + + +class LocationData(NamedTuple): + region: str + name: str + code: Optional[int] + +def get_locations(world: "DoSWorld") -> List[LocationData]: + + location_table: List[LocationData] = [ + #LocationData("Lost Village Upper", "Lost Village: Above Entrance", 0x01) + ] + + return location_table \ No newline at end of file diff --git a/worlds/cv_por/Options.py b/worlds/cv_por/Options.py index 8874b1b0f2b3..7c79d37b9c67 100644 --- a/worlds/cv_por/Options.py +++ b/worlds/cv_por/Options.py @@ -83,6 +83,10 @@ class PoRExcludeLocations(ExcludeLocations): default = frozenset({"The Throne Room: Above Throne Left", "The Throne Room: Above Throne Right", "The Throne Room: Great Stairs Left", "The Throne Room: Great Stairs Center", "The Throne Room: Great Stairs Under Stairs", "The Throne Room: Great Stairs Hidden"}) +class StartWithChangeCube(Toggle): # TODO!!! Implement + """If enabled, you'll start with the Change Cube by default""" + display_name = "Start with Change Cube" + @dataclass class PoROptions(PerGameCommonOptions): goal: Goal @@ -96,6 +100,7 @@ class PoROptions(PerGameCommonOptions): reveal_hidden_walls: RevealBreakableWalls experience_percentage: ExperiencePercent exclude_locations: PoRExcludeLocations + start_with_change_cube: StartWithChangeCube por_option_groups = [ OptionGroup("Goal Options", [ @@ -113,7 +118,8 @@ class PoROptions(PerGameCommonOptions): ]), OptionGroup("Item Options", [ - ShuffleWhip + ShuffleWhip, + StartWithChangeCube ]), diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py new file mode 100644 index 000000000000..d79673fd636f --- /dev/null +++ b/worlds/cv_por/Regions.py @@ -0,0 +1,241 @@ +from BaseClasses import Region, Location +from typing import List, TYPE_CHECKING, Dict +from rule_builder.rules import HasAll, HasAny, Has, OptionFilter, CanReachLocation +from .Locations import LocationData, get_locations +from .Options import StartWithChangeCube +if TYPE_CHECKING: + from . import PoRWorld + + +def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: + has_change_cube = Has("Change Cube", options=[OptionFilter(StartWithChangeCube, 0)], filtered_resolution=True) + + can_cast_spell = Has("Call Cube") | has_change_cube + small_uppies = HasAny("Stone of Flight", "Griffon Wing") | HasAll("Call Cube", "Acrobat Cube") | (can_cast_spell & Has("Owl Morph")) + medium_uppies = HasAny("Stone of Flight", "Griffon Wing") | (can_cast_spell & Has("Owl Morph")) + big_uppies = Has("Griffon Wing") | (can_cast_spell & Has("Owl Morph")) + is_smol = Has("Lizard Tail") | (can_cast_spell & Has("Owl Morph")) | (can_cast_spell & Has("Toad Morph")) + + locations_per_region = get_locations_per_region(locations) + + region_list = [ + "Entrance - Wind's Room", # Used for quests/shops + "Entrance - Hub", # The entire starting area up through the hub + "Entrance - Behemoth Area", # The area past the first jump check, Behemoth's boss room + "Entrance - Underground Passage", # The tunnel leading to the Nest of Evil portrait + "Entrance - Hub Painting Room", # The City of Haze portrait + "Entrance - Upper Area", # The upper route from the statue room + + "Buried Chamber", # The ENTIRE buried Chamber, since it all falls under the same logic + + "Great Stairway - Lower", # Ground level up to Keremet and the check afterwards + "Great Stairway - Staircases", # The big stairs rooms and surrounding areas + "Great Stairway - Entrance Connector", # The part that connects to the entrance and the wall switch. There's an awkward jump to the upper level that can't be made with acrobat. + "Great Stairway - Upper", # The towers to the left of the staircase rooms + "Great Stairway - Central Painting Area", # The painting but also the secret room with the nun robes + "Great Stairway - Underground Painting", # The sandy graves portrait room + "Great Stairway - Central Staircases", + + "Tower of Death - Bottom", + "Tower of Death - Motorcycles", + "Tower of Death - Belt Area", + "Tower of Death - Painting Room", + "Tower of Death - Elevator Room", + "Tower of Death - First Gear Room", + "Tower of Death - Ascent", + "Tower of Death - Second Gear Room", + "Tower of Death - Top of the Tower", + + "Master's Keep - Bridge", + "Master's Keep - Lower", + "Master's Keep - Main", + "Master's Keep - Upper Quarters", + "Master's Keep - Portrait Room", + + "City of Haze", + "City of Haze - East", + + "13th Street", + "13th Street - Main", + + "Sandy Grave", + "Sandy Grave - Pyramid Top", + + "Forgotten City", + "Forgotten City - Inner", + + "Nation of Fools", + "Nation of Fools - Right Lower", + "Nation of Fools - Main", + + "Forest of Doom", + "Forest of Doom - Main", + "Forest of Doom - Cave", + + "Dark Academy", + "Dark Academy - Right Building", + "Dark Academy - Main", + + "Burnt Paradise", + "Burnt Paradise - Bottom", + "Nest of Evil" + + ] + + if world.options.goal: + regions.append("Throne Room") + + regions = [] + + for area in region_list: + regions.append(create_region(world, world.player, locations_per_region, area)) + + world.multiworld.regions += regions + + world.get_region("Entrance - Hub").add_exits(["Entrance - Wind's Room", "Entrance - Behemoth Area", "Entrance - Hub Painting Room", "Entrance - Upper Area", "Entrance - Underground Passage"], + {"Entrance - Behemoth Area": small_uppies, + "Entrance - Hub Painting Room": is_smol, + "Entrance - Upper Area": HasAll("Acrobat Cube", "Call Cube", "Stone of Flight") | big_uppies, + "Entrance - Underground Passage": Has("Portrait Clear", world.options.nest_portraits.value)}) + + world.get_region("Entrance - Hub Painting Room").add_exits(["Entrance - Hub", world.portrait_connections["City of Haze"]], + {"Entrance - Hub": is_smol}) + + world.get_region("Entrance - Underground Passage").add_exits(["Entrance - Hub", world.portrait_connections["Nest of Evil"]], + {"Entrance - Hub": small_uppies}) + + world.get_region("Entrance - Upper Area").add_exits(["Entrance - Hub", "Great Stairway - Entrance Connector"]) # Stairway connector + + world.get_region("Entrance - Behemoth Area").add_exits(["Entrance - Hub", "Great Stairway - Lower", "Buried Chamber"], + {"Great Stairway - Lower": HasAll("Strength Glove", "Push Cube", "Call Cube")}) + + world.get_region("Buried Chamber").add_exits(["Entrance - Hub", "Great Stairway - Lower"]) + + world.get_region("Great Stairway - Lower").add_exits(["Entrance - Hub", "Great Stairway - Central Staircases", "Buried Chamber"], + {"Great Stairway - Central Staircases": Has("Stone of Flight") | big_uppies}) + + world.get_region("Great Stairway - Staircases").add_exits(["Great Stairway - Lower", "Great Stairway - Underground Painting", "Tower of Death - Bottom", "Great Stairway - Upper"], + {"Great Stairway - Central Staircases": Has("Stone of Flight") | big_uppies, + "Tower of Death - Bottom": HasAll("Strength Glove", "Push Cube", "Call Cube"), + "Great Stairway - Upper": small_uppies | Has("Puppet Master")}) + + world.get_region("Great Stairway - Underground Painting").add_exits(["Great Stairway - Staircases", world.portrait_connections["Sandy Grave"]]) + + world.get_region("Great Stairway - Entrance Connector").add_exits(["Great Stairway - Staircases", "Entrance - Upper Area", "Great Stairway - Upper"], + {"Great Stairway - Upper": (HasAll("Call Cube", "Acrobat Cube", "Puppet Master")) | medium_uppies}) + + world.get_region("Great Stairway - Upper").add_exits(["Great Stairway - Staircases", "Great Stairway - Entrance Connector", "Tower of Death - Belt Area", "Great Stairway - Central Painting Area"], + {"Tower of Death - Belt Area": can_cast_spell & HasAny("Owl Morph", "Toad Morph"), + "Great Stairway - Central Painting Area": small_uppies}) + + world.get_region("Great Stairway - Central Painting Area").add_exits([world.portrait_connections["Nation of Fools"], "Great Stairway - Upper"]) + + world.get_region("Tower of Death - Bottom").add_exits(["Tower of Death - Ascent", "Tower of Death - First Gear Room", "Tower of Death - Motorcycles"], + {"Tower of Death - Motorcycles": Has("Cog"), + "Tower of Death - First Gear Room": small_uppies}) + + world.get_region("Tower of Death - Motorcycles").add_exits(["Tower of Death - Bottom", "Tower of Death - Belt Area"], + {"Tower of Death - Belt Area": HasAll("Wait Cube", "Call Cube") & has_change_cube}) + + world.get_region("Tower of Death - Belt Area").add_exits(["Tower of Death - Painting Room", "Great Stairway - Upper", "Master's Keep - Bridge"], + {"Great Stairway - Upper": can_cast_spell & HasAny("Owl Morph", "Toad Morph")}) + + world.get_region("Tower of Death - Painting Room").add_exits(["Tower of Death - Belt Area", world.portrait_connections["Forest of Doom"]]) + + world.get_region("Tower of Death - Elevator Room").add_exits(["Master's Keep - Bridge", "Tower of Death - Top of the Tower", "Tower of Death - First Gear Room", "Master's Keep - Lower"], + {"Master's Keep - Lower": small_uppies & Has("Tower Elevator Active")}) + + world.get_region("Tower of Death - First Gear Room").add_exits(["Tower of Death - Bottom", "Tower of Death - Ascent"], + {"Tower of Death - Ascent": can_cast_spell & HasAny("Owl Morph", "Toad Morph")}) + + + world.get_region("Tower of Death - Ascent").add_exits(["Tower of Death - First Gear Room", "Tower of Death - Second Gear Room"], + {"Tower of Death - First Gear Room": can_cast_spell & HasAny("Owl Morph", "Toad Morph"), + "Tower of Death - Second Gear Room": medium_uppies}) + + world.get_region("Tower of Death - Second Gear Room").add_exits(["Tower of Death - Ascent", "Tower of Death - Top of the Tower"]) + + world.get_region("Tower of Death - Top of the Tower").add_exits(["Tower of Death - Second Gear Room", "Tower of Death - Elevator Room"]) + + + world.get_region("Master's Keep - Bridge").add_exits(["Tower of Death - Belt Area", "Tower of Death - Elevator Room"]) + + world.get_region("Master's Keep - Lower").add_exits(["Tower of Death - Elevator Room", "Master's Keep - Bridge", "Master's Keep - Main"], + {"Tower of Death - Elevator Room": Has("Tower Elevator Active"), + "Master's Keep - Main": medium_uppies | (small_uppies & Has("Puppet Master"))}) + + world.get_region("Master's Keep - Main").add_exits(["Master's Keep - Lower", "Master's Keep - Upper Quarters"], + {"Master's Keep - Upper Quarters": medium_uppies | (small_uppies & Has("Puppet Master"))}) + + world.get_region("Master's Keep - Upper Quarters").add_exits(["Master's Keep - Portrait Room", "Master's Keep - Main"], + {"Master's Keep - Portrait Room": can_cast_spell & Has("Sanctuary")}) + + world.get_region("Master's Keep - Portrait Room").add_exits([world.portrait_connections["Forgotten City"], world.portrait_connections["Burnt Paradise"], world.portrait_connections["Dark Academy"], world.portrait_connections["13th Street"]], + {world.portrait_connections["13th Street"]: CanReachLocation("Forgotten City - Boss Room"), + world.portrait_connections["Burnt Paradise"]: CanReachLocation("13th Street - Boss Room")}) + + world.get_region("City of Haze").add_exits(["City of Haze - East"], + {"City of Haze - East": Has("Puppet Master") | (has_change_cube & Has("Call Cube"))}) + + world.get_region("Sandy Grave").add_exits(["Sandy Grave - Upper Pyramid"], + {"Sandy Grave - Upper Pyramid": small_uppies}) + + world.get_region("Nation of Fools").add_exits(["Nation of Fools - Right Lower", "Nation of Fools - Main"], + {"Nation of Fools - Right Lower": small_uppies | Has("Puppet Master"), + "Nation of Fools - Main": medium_uppies}) + + world.get_region("Sandy Grave - Upper Pyramid").add_exits(["Sandy Grave - Pyramid Top"], + {"Sandy Grave - Pyramid Top": medium_uppies}) + + world.get_region("Forest of Doom").add_exits(["Forest of Doom - Main"], + {"Forest of Doom - Main": small_uppies}) + + world.get_region("Forest of Doom - Main").add_exits(["Forest of Doom - Cave"], + {"Forest of Doom - Cave": HasAll("Strength Glove", "Push Cube", "Call Cube")}) + + world.get_region("Dark Academy").add_exits(["Dark Academy - Right Building"], + {"Dark Academy - Right Building": small_uppies | Has("Puppet Master")}) + + world.get_region("Dark Academy - Right Building").add_exits(["Dark Academy - Main"], + {"Dark Academy - Main": big_uppies}) + + world.get_region("Forgotten City").add_exits(["Forgotten City - Inner"], + {"Forgotten City - Inner": medium_uppies | Has("Puppet Master")}) + + world.get_region("13th Street").add_exits(["13th Street - Main"], + {"13th Street - Main": HasAll("Strength Glove", "Push Cube", "Call Cube")}) + + world.get_region("Burnt Paradise").add_exits(["Burnt Paradise - Entrance"], + {"Burnt Paradise - Entrance": small_uppies | Has("Puppet Master")}) + + world.get_region("Burnt Paradise - Entrance").add_exits(["Burnt Paradise - Bottom"], + {"Burnt Paradise - Bottom": big_uppies}) + + if world.options.goal: + # Add a connection to the throne room if necessary + world.get_region("Master's Keep - Upper Quarters").connect(world.get_region("Throne Room"), "Throne Barrier", Has("Brauner Defeated")) + +def create_location(player: int, location_data: LocationData, region: Region) -> Location: + location = PoRLocation(player, location_data.name, location_data.code, region) + location.region = location_data.region + + return location + +def create_region(world: "PoRWorld", player: int, locations_per_region: Dict[str, List[LocationData]], name: str) -> Region: + region = Region(name, player, world.multiworld) + + if name in locations_per_region: + for location_data in locations_per_region[name]: + location = create_location(player, location_data, region) + region.locations.append(location) + + return region + + +def get_locations_per_region(locations: List[LocationData]) -> Dict[str, List[LocationData]]: + per_region: Dict[str, List[LocationData]] = {} + + for location in locations: + per_region.setdefault(location.region, []).append(location) + + return per_region \ No newline at end of file diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py index 0ea22432e850..ecdc10fbe96a 100644 --- a/worlds/cv_por/__init__.py +++ b/worlds/cv_por/__init__.py @@ -7,7 +7,6 @@ from .Items import item_table, get_item_names_per_category from .Options import PoROptions, por_option_groups -from .generator_main import generate_game from .static_location_data import location_ids class PoRWeb(WebWorld): @@ -67,5 +66,11 @@ def __init__(self, multiworld: MultiWorld, player: int): self.locked_locations = [] self.location_cache = [] self.extra_item_count = 0 - - generate_game(self) \ No newline at end of file + + def generate_early(self): + from .generator_main import generate_early + generate_early(self) + + def create_regions(self): + from .generator_main import create_regions + create_regions(self) \ No newline at end of file diff --git a/worlds/cv_por/generator_main.py b/worlds/cv_por/generator_main.py index 7eee07b2f025..e1ef9fa1eea2 100644 --- a/worlds/cv_por/generator_main.py +++ b/worlds/cv_por/generator_main.py @@ -1,2 +1,15 @@ -def generate_game(world): - print("hi") \ No newline at end of file +from .Locations import get_locations + +def generate_early(world) -> None: + from .setup_game import setup_game + setup_game(world) + + world.auth_id = world.random.getrandbits(32) + + +def create_regions(world) -> None: + from .setup_game import place_static_items + from .Regions import init_areas + + init_areas(world, get_locations(world)) + place_static_items(world) \ No newline at end of file diff --git a/worlds/cv_por/setup_game.py b/worlds/cv_por/setup_game.py new file mode 100644 index 000000000000..e22c1dd8d647 --- /dev/null +++ b/worlds/cv_por/setup_game.py @@ -0,0 +1,16 @@ +def setup_game(world): + world.portrait_connections = { + "City of Haze": "City of Haze", + "13th Street": "13th Street", + "Sandy Grave": "Sandy Grave", + "Forgotten City": "Forgotten City", + "Nation of Fools": "Nation of Fools", + "Burnt Paradise": "Burnt Paradise", + "Forest of Doom": "Forest of Doom", + "Dark Academy": "Dark Academy", + "Nest of Evil": "Nest of Evil" + } + +def place_static_items(world): + print("aaaaaaaaa") + #world.get_location("Lost Village: Moat Drain Switch").place_locked_item(world.create_item("Moat Drained")) \ No newline at end of file diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index 09702bfb93c6..ee73b75aaeec 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -283,7 +283,7 @@ "Quest: The Hundred Tasks": 0x0220, "Quest: Master the Holy Power": 0x0221, "Quest: Almighty": 0x0222, - "Quets: The Great Sage": 0x0223, + "Quest: The Great Sage": 0x0223, "Quest: Kill Gergoth": 0x0224 } From 0edc676270860a768f147260c09bd257d9b2c681 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Sun, 26 Apr 2026 00:35:48 -0500 Subject: [PATCH 22/58] Add great stairs locations --- worlds/cv_por/Locations.py | 64 ++++++++++++++++++++++++++++++++++++-- worlds/cv_por/Regions.py | 21 ++++++++----- 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/worlds/cv_por/Locations.py b/worlds/cv_por/Locations.py index 348fd6098238..9375ab81f44e 100644 --- a/worlds/cv_por/Locations.py +++ b/worlds/cv_por/Locations.py @@ -8,12 +8,70 @@ class LocationData(NamedTuple): region: str name: str - code: Optional[int] + is_event: Optional[bool] = False -def get_locations(world: "DoSWorld") -> List[LocationData]: +def get_locations(world: "PoRWorld") -> List[LocationData]: location_table: List[LocationData] = [ - #LocationData("Lost Village Upper", "Lost Village: Above Entrance", 0x01) + LocationData("Entrance - Hub", "Entrance: Drawbridge Pit Item"), + LocationData("Entrance - Hub", "Entrance: Drawbridge Switch Item"), + LocationData("Entrance - Hub", "Entrance: Drawbridge Upper Item"), + LocationData("Entrance - Hub", "Entrance: First Passage Upper Item"), + LocationData("Entrance - Hub", "Entrance: First Passage Lower Item"), + LocationData("Entrance - Hub", "Entrance: First Passage Hidden Wall"), + LocationData("Entrance - Hub", "Entrance: Hub Room"), + LocationData("Entrance - Hub", "Entrance: Above Hub"), + LocationData("Entrance - Hub", "Entrance: Statue Room Upper"), + LocationData("Entrance - Hub", "Entrance: Statue Room Above Statue"), + LocationData("Entrance - Hub", "Entrance: Behemoth Chase Room"), + LocationData("Entrance - Hub", "Entrance: Double Room Lower"), + LocationData("Entrance - Behemoth Area", "Entrance: Double Room Upper"), + LocationData("Entrance - Behemoth Area", "Entrance: Pre-Behemoth Boss Room"), + LocationData("Entrance - Behemoth Area", "Entrance: Right Hidden Room"), + LocationData("Entrance - Upper Area", "Entrance: Lilith Room"), + LocationData("Entrance - Upper Area", "Entrance: Above Metal Block Room"), + LocationData("Entrance - Upper Area", "Entrance: Center Hidden Room"), + LocationData("Entrance - Upper Area", "Entrance: Beyond Great Armor Hallway"), + + LocationData("Buried Chamber", "Buried Chamber: Westmost Room"), + LocationData("Buried Chamber", "Buried Chamber: Central Secret"), + LocationData("Buried Chamber", "Buried Chamber: West Shaft"), + LocationData("Buried Chamber", "Buried Chamber: Central Hall"), + LocationData("Buried Chamber", "Buried Chamber: Hidden Wall Item"), + LocationData("Buried Chamber", "Buried Chamber: Right Secret"), + LocationData("Buried Chamber", "Buried Chamber: Right Shade Hall"), + LocationData("Buried Chamber", "Buried Chamber: Final Shaft Item"), + LocationData("Buried Chamber", "Buried Chamber: Right Shaft Top"), + LocationData("Buried Chamber", "Buried Chamber: Eastmost Room"), + + LocationData("Great Stairway - Lower", "Great Stairway: After Keremet Lower"), + LocationData("Great Stairway - Lower", "Great Stairway: Before Keremet Room"), + LocationData("Great Stairway - Central Painting Area", "Great Stairway: Left Loft"), + LocationData("Great Stairway - Central Painting Area", "Great Stairway: Left Loft Lower"), + LocationData("Great Stairway - Entrance Connector", "Great Stairway: Central NookDummy"), + LocationData("Great Stairway - Staircases", "Great Stairway: Upper Grand Staircase Corner Nook"), + LocationData("Great Stairway - Staircases", "Great Stairway: Upper Grand Staircase Middle Alcove"), + LocationData("Great Stairway - Staircases", "Great Stairway: Upper Grand Staircase Lower Alcove"), + LocationData("Great Stairway - Staircases", "Great Stairway: Upper Grand Staircase Upper Alcove"), + LocationData("Great Stairway - Staircases", "Great Stairway: Lower Grand Staircase Corner Nook"), + LocationData("Great Stairway - Staircases", "Great Stairway: Lower Grand Staircase Lower Alcove"), + LocationData("Great Stairway - Staircases", "Great Stairway: Lower Grand Staircase Middle Alcove"), + LocationData("Great Stairway - Staircases", "Great Stairway: Lower Grand Staircase Upper Alcove"), + LocationData("Great Stairway - Staircases", "Great Stairway: Upper Underground Room"), + LocationData("Great Stairway - Staircases", "Great Stairway: After Keremet Upper"), + LocationData("Great Stairway - Staircases", "Great Stairway: Big Underground Room Upper"), + LocationData("Great Stairway - Staircases", "Great Stairway: Big Underground Room Hidden Item"), + LocationData("Great Stairway - Staircases", "Great Stairway: Big Underground Room Lower"), + LocationData("Great Stairway - Staircases", "Great Stairway: Underground Hidden Room"), + LocationData("Great Stairway - Staircases", "Great Stairway: Underground East Room"), + LocationData("Great Stairway - Upper", "Great Stairway: Connector Pipe Left"), + LocationData("Great Stairway - Upper", "Great Stairway: Connector Pipe Right"), + LocationData("Great Stairway - Upper", "Great Stairway: Upper Grand Staircase Top Left Item"), + LocationData("Great Stairway - Upper", "Great Stairway: Central Upper"), + LocationData("Great Stairway - Upper", "Great Stairway: Right Loft"), + + LocationData("Dummy", "Dummy"), + ] return location_table \ No newline at end of file diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py index d79673fd636f..92eadc62f605 100644 --- a/worlds/cv_por/Regions.py +++ b/worlds/cv_por/Regions.py @@ -6,6 +6,12 @@ if TYPE_CHECKING: from . import PoRWorld +class PoRLocation(Location): + game: str = "Castlevania: Portrait of Ruin" + + def __init__(self, player: int, name: str = " ", address: int = None, parent=None): + super().__init__(player, name, address, parent) + def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: has_change_cube = Has("Change Cube", options=[OptionFilter(StartWithChangeCube, 0)], filtered_resolution=True) @@ -34,7 +40,6 @@ def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: "Great Stairway - Upper", # The towers to the left of the staircase rooms "Great Stairway - Central Painting Area", # The painting but also the secret room with the nun robes "Great Stairway - Underground Painting", # The sandy graves portrait room - "Great Stairway - Central Staircases", "Tower of Death - Bottom", "Tower of Death - Motorcycles", @@ -111,12 +116,11 @@ def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: world.get_region("Buried Chamber").add_exits(["Entrance - Hub", "Great Stairway - Lower"]) - world.get_region("Great Stairway - Lower").add_exits(["Entrance - Hub", "Great Stairway - Central Staircases", "Buried Chamber"], - {"Great Stairway - Central Staircases": Has("Stone of Flight") | big_uppies}) + world.get_region("Great Stairway - Lower").add_exits(["Entrance - Hub", "Great Stairway - Staircases", "Buried Chamber"], + {"Great Stairway - Staircases": Has("Stone of Flight") | big_uppies}) world.get_region("Great Stairway - Staircases").add_exits(["Great Stairway - Lower", "Great Stairway - Underground Painting", "Tower of Death - Bottom", "Great Stairway - Upper"], - {"Great Stairway - Central Staircases": Has("Stone of Flight") | big_uppies, - "Tower of Death - Bottom": HasAll("Strength Glove", "Push Cube", "Call Cube"), + {"Tower of Death - Bottom": HasAll("Strength Glove", "Push Cube", "Call Cube"), "Great Stairway - Upper": small_uppies | Has("Puppet Master")}) world.get_region("Great Stairway - Underground Painting").add_exits(["Great Stairway - Staircases", world.portrait_connections["Sandy Grave"]]) @@ -140,7 +144,9 @@ def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: world.get_region("Tower of Death - Belt Area").add_exits(["Tower of Death - Painting Room", "Great Stairway - Upper", "Master's Keep - Bridge"], {"Great Stairway - Upper": can_cast_spell & HasAny("Owl Morph", "Toad Morph")}) - world.get_region("Tower of Death - Painting Room").add_exits(["Tower of Death - Belt Area", world.portrait_connections["Forest of Doom"]]) + world.get_region("Tower of Death - Painting Room").add_exits(["Tower of Death - Belt Area", world.portrait_connections["Forest of Doom"]], { + world.portrait_connections["Forest of Doom"]: Has("Stella's Locket") + }) world.get_region("Tower of Death - Elevator Room").add_exits(["Master's Keep - Bridge", "Tower of Death - Top of the Tower", "Tower of Death - First Gear Room", "Master's Keep - Lower"], {"Master's Keep - Lower": small_uppies & Has("Tower Elevator Active")}) @@ -216,8 +222,9 @@ def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: world.get_region("Master's Keep - Upper Quarters").connect(world.get_region("Throne Room"), "Throne Barrier", Has("Brauner Defeated")) def create_location(player: int, location_data: LocationData, region: Region) -> Location: - location = PoRLocation(player, location_data.name, location_data.code, region) + location = PoRLocation(player, location_data.name, region) location.region = location_data.region + location.code = 1 return location From 43433ebf73648b70434325e8560475e11ea23519 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Sun, 26 Apr 2026 18:48:38 -0500 Subject: [PATCH 23/58] Map checks for city of haze --- worlds/cv_por/Locations.py | 62 +++++++++++- worlds/cv_por/Options.py | 15 ++- worlds/cv_por/Regions.py | 137 ++++++++++++++------------ worlds/cv_por/static_location_data.py | 5 +- 4 files changed, 149 insertions(+), 70 deletions(-) diff --git a/worlds/cv_por/Locations.py b/worlds/cv_por/Locations.py index 9375ab81f44e..33142dffd7d8 100644 --- a/worlds/cv_por/Locations.py +++ b/worlds/cv_por/Locations.py @@ -48,7 +48,7 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Great Stairway - Lower", "Great Stairway: Before Keremet Room"), LocationData("Great Stairway - Central Painting Area", "Great Stairway: Left Loft"), LocationData("Great Stairway - Central Painting Area", "Great Stairway: Left Loft Lower"), - LocationData("Great Stairway - Entrance Connector", "Great Stairway: Central NookDummy"), + LocationData("Great Stairway - Entrance Connector", "Great Stairway: Central Nook"), LocationData("Great Stairway - Staircases", "Great Stairway: Upper Grand Staircase Corner Nook"), LocationData("Great Stairway - Staircases", "Great Stairway: Upper Grand Staircase Middle Alcove"), LocationData("Great Stairway - Staircases", "Great Stairway: Upper Grand Staircase Lower Alcove"), @@ -70,8 +70,66 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Great Stairway - Upper", "Great Stairway: Central Upper"), LocationData("Great Stairway - Upper", "Great Stairway: Right Loft"), + LocationData("Tower of Death - Bottom", "Tower of Death: Lower Passage Hidden Item"), + LocationData("Tower of Death - Bottom", "Tower of Death: Stella Item"), + LocationData("Tower of Death - First Gear Room", "Tower of Death: Lower Gears Room Spike Shaft"), + LocationData("Tower of Death - Motorcycles", "Tower of Death: Above Motorcycles"), + LocationData("Tower of Death - Belt Area", "Tower of Death: Rampart Room"), + LocationData("Tower of Death - Belt Area", "Tower of Death: Hidden Room"), + LocationData("Tower of Death - Belt Area", "Tower of Death: Gear Belt Room"), + LocationData("Tower of Death - Elevator Room", "Tower of Death: Secret Room"), + LocationData("Tower of Death - Elevator Room", "Tower of Death: Elevator Room Lower"), + LocationData("Tower of Death - Elevator Room", "Tower of Death: Elevator Room Middle"), + LocationData("Tower of Death - Elevator Room", "Tower of Death: Elevator Room Top"), + LocationData("Tower of Death - Second Gear Room", "Tower of Death: Second Gears Room Left"), + LocationData("Tower of Death - Second Gear Room", "Tower of Death: Passage Hidden Item"), + LocationData("Tower of Death - Second Gear Room", "Tower of Death: Second Gears Room Right"), + LocationData("Tower of Death - Second Gear Room", "Tower of Death: Top Gears Room"), + LocationData("Tower of Death - Second Gear Room", "Tower of Death: Third Gears Room"), + LocationData("Tower of Death - Top of the Tower", "Tower of Death: Top of the Tower"), + + LocationData("Master's Keep - Lower", "Master's Keep: Entrance"), + LocationData("Master's Keep - Main", "Master's Keep: Hidden Room"), + LocationData("Master's Keep - Main", "Master's Keep: Right Square Room"), + LocationData("Master's Keep - Portrait Room", "Master's Keep: Whip Trial"), + + LocationData("City of Haze", "City of Haze: Meat Room"), + LocationData("City of Haze", "City of Haze: First Passage"), + LocationData("City of Haze", "City of Haze: First Big Room"), + LocationData("City of Haze", "City of Haze: Corner Room"), + LocationData("City of Haze", "City of Haze: Winding Rooms"), + LocationData("City of Haze", "City of Haze: Chunky Room"), + LocationData("City of Haze", "City of Haze: Right Corner Room"), + LocationData("City of Haze", "City of Haze: Bar Left"), + LocationData("City of Haze", "City of Haze: Bar Right"), + LocationData("City of Haze", "City of Haze: Register Room"), + LocationData("City of Haze", "City of Haze: Axe Armor Item"), + LocationData("City of Haze", "City of Haze: Center Room Top"), + LocationData("City of Haze", "City of Haze: Center Room Lower"), + LocationData("City of Haze", "City of Haze: Bread Shelf"), + LocationData("City of Haze", "City of Haze: Left Plaza"), + LocationData("City of Haze", "City of Haze: Before Cart Room"), + LocationData("City of Haze", "City of Haze: Cart Secret Left"), + LocationData("City of Haze", "City of Haze: Cart Secret Right"), + LocationData("City of Haze - East", "City of Haze: Church Hallway"), + LocationData("City of Haze - East", "City of Haze: Central Hallway Stairs"), + LocationData("City of Haze - East", "City of Haze: Heavy Cart Item"), + LocationData("City of Haze - East", "City of Haze: Axe Courtyard"), + LocationData("City of Haze - East", "City of Haze: Left Dance Hall Left Item"), + LocationData("City of Haze - East", "City of Haze: Left Dance Hall Right Item"), + LocationData("City of Haze - East", "City of Haze: Dance Halls Basement"), + LocationData("City of Haze - East", "City of Haze: Right Dance Hall Item"), + LocationData("City of Haze - East", "City of Haze: Right Dance Hall Secret Room"), + LocationData("City of Haze - East", "City of Haze: Post-Boss Room"), + LocationData("City of Haze - East", "City of Haze: Tunnel Room Right"), + LocationData("City of Haze - East", "City of Haze: Tunnel Room Left"), + LocationData("City of Haze - East", "City of Haze: Dance Halls Vertical Hallway"), + LocationData("City of Haze - East", "City of Haze: Underground Square Right"), + LocationData("City of Haze - East", "City of Haze: Underground Secret Room"), + LocationData("City of Haze - East", "City of Haze: Underground Square Left"), + LocationData("City of Haze - East", "Dummy"), + LocationData("Dummy", "Dummy"), - ] return location_table \ No newline at end of file diff --git a/worlds/cv_por/Options.py b/worlds/cv_por/Options.py index 7c79d37b9c67..b0304ed893ba 100644 --- a/worlds/cv_por/Options.py +++ b/worlds/cv_por/Options.py @@ -87,6 +87,15 @@ class StartWithChangeCube(Toggle): # TODO!!! Implement """If enabled, you'll start with the Change Cube by default""" display_name = "Start with Change Cube" +class AddExtraItems(Toggle): # TODO!!! Implement + """If enabled, some items and spells that are otherwise only obtainable + in other modes will be added to the item pool, such as Puppet Master.""" + display_name = "Add Extra Items" + +class ExcludeOwlMorph(Toggle): # TODO!!! Implement + """If enabled, Owl Morph will not be added to the item pool.""" + display_name = "Exclude Owl Morph" + @dataclass class PoROptions(PerGameCommonOptions): goal: Goal @@ -101,6 +110,8 @@ class PoROptions(PerGameCommonOptions): experience_percentage: ExperiencePercent exclude_locations: PoRExcludeLocations start_with_change_cube: StartWithChangeCube + add_extra_items: AddExtraItems + exclude_owl_morph: ExcludeOwlMorph por_option_groups = [ OptionGroup("Goal Options", [ @@ -119,7 +130,9 @@ class PoROptions(PerGameCommonOptions): OptionGroup("Item Options", [ ShuffleWhip, - StartWithChangeCube + StartWithChangeCube, + AddExtraItems, + ExcludeOwlMorph ]), diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py index 92eadc62f605..e02d343bd30c 100644 --- a/worlds/cv_por/Regions.py +++ b/worlds/cv_por/Regions.py @@ -12,6 +12,69 @@ class PoRLocation(Location): def __init__(self, player: int, name: str = " ", address: int = None, parent=None): super().__init__(player, name, address, parent) +region_list = [ + "Entrance - Wind's Room", # Used for quests/shops + "Entrance - Hub", # The entire starting area up through the hub + "Entrance - Behemoth Area", # The area past the first jump check, Behemoth's boss room + "Entrance - Underground Passage", # The tunnel leading to the Nest of Evil portrait + "Entrance - Hub Painting Room", # The City of Haze portrait + "Entrance - Upper Area", # The upper route from the statue room + + "Buried Chamber", # The ENTIRE buried Chamber, since it all falls under the same logic + + "Great Stairway - Lower", # Ground level up to Keremet and the check afterwards + "Great Stairway - Staircases", # The big stairs rooms and surrounding areas + "Great Stairway - Entrance Connector", # The part that connects to the entrance and the wall switch. There's an awkward jump to the upper level that can't be made with acrobat. + "Great Stairway - Upper", # The towers to the left of the staircase rooms + "Great Stairway - Central Painting Area", # The painting but also the secret room with the nun robes + "Great Stairway - Underground Painting", # The sandy graves portrait room + + "Tower of Death - Bottom", + "Tower of Death - Motorcycles", + "Tower of Death - Belt Area", + "Tower of Death - Painting Room", + "Tower of Death - Elevator Room", + "Tower of Death - First Gear Room", + "Tower of Death - Ascent", + "Tower of Death - Second Gear Room", + "Tower of Death - Top of the Tower", + + "Master's Keep - Bridge", + "Master's Keep - Lower", + "Master's Keep - Main", + "Master's Keep - Upper Quarters", + "Master's Keep - Portrait Room", + + "City of Haze", + "City of Haze - East", + + "13th Street", + "13th Street - Main", + + "Sandy Grave", + "Sandy Grave - Pyramid Top", + + "Forgotten City", + "Forgotten City - Inner", + + "Nation of Fools", + "Nation of Fools - Right Lower", + "Nation of Fools - Main", + + "Forest of Doom", + "Forest of Doom - Main", + "Forest of Doom - Cave", + + "Dark Academy", + "Dark Academy - Right Building", + "Dark Academy - Main", + + "Burnt Paradise", + "Burnt Paradise - Bottom", + "Nest of Evil" + +] + def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: has_change_cube = Has("Change Cube", options=[OptionFilter(StartWithChangeCube, 0)], filtered_resolution=True) @@ -24,69 +87,6 @@ def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: locations_per_region = get_locations_per_region(locations) - region_list = [ - "Entrance - Wind's Room", # Used for quests/shops - "Entrance - Hub", # The entire starting area up through the hub - "Entrance - Behemoth Area", # The area past the first jump check, Behemoth's boss room - "Entrance - Underground Passage", # The tunnel leading to the Nest of Evil portrait - "Entrance - Hub Painting Room", # The City of Haze portrait - "Entrance - Upper Area", # The upper route from the statue room - - "Buried Chamber", # The ENTIRE buried Chamber, since it all falls under the same logic - - "Great Stairway - Lower", # Ground level up to Keremet and the check afterwards - "Great Stairway - Staircases", # The big stairs rooms and surrounding areas - "Great Stairway - Entrance Connector", # The part that connects to the entrance and the wall switch. There's an awkward jump to the upper level that can't be made with acrobat. - "Great Stairway - Upper", # The towers to the left of the staircase rooms - "Great Stairway - Central Painting Area", # The painting but also the secret room with the nun robes - "Great Stairway - Underground Painting", # The sandy graves portrait room - - "Tower of Death - Bottom", - "Tower of Death - Motorcycles", - "Tower of Death - Belt Area", - "Tower of Death - Painting Room", - "Tower of Death - Elevator Room", - "Tower of Death - First Gear Room", - "Tower of Death - Ascent", - "Tower of Death - Second Gear Room", - "Tower of Death - Top of the Tower", - - "Master's Keep - Bridge", - "Master's Keep - Lower", - "Master's Keep - Main", - "Master's Keep - Upper Quarters", - "Master's Keep - Portrait Room", - - "City of Haze", - "City of Haze - East", - - "13th Street", - "13th Street - Main", - - "Sandy Grave", - "Sandy Grave - Pyramid Top", - - "Forgotten City", - "Forgotten City - Inner", - - "Nation of Fools", - "Nation of Fools - Right Lower", - "Nation of Fools - Main", - - "Forest of Doom", - "Forest of Doom - Main", - "Forest of Doom - Cave", - - "Dark Academy", - "Dark Academy - Right Building", - "Dark Academy - Main", - - "Burnt Paradise", - "Burnt Paradise - Bottom", - "Nest of Evil" - - ] - if world.options.goal: regions.append("Throne Room") @@ -134,7 +134,7 @@ def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: world.get_region("Great Stairway - Central Painting Area").add_exits([world.portrait_connections["Nation of Fools"], "Great Stairway - Upper"]) - world.get_region("Tower of Death - Bottom").add_exits(["Tower of Death - Ascent", "Tower of Death - First Gear Room", "Tower of Death - Motorcycles"], + world.get_region("Tower of Death - Bottom").add_exits(["Tower of Death - First Gear Room", "Tower of Death - Motorcycles"], {"Tower of Death - Motorcycles": Has("Cog"), "Tower of Death - First Gear Room": small_uppies}) @@ -222,9 +222,14 @@ def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: world.get_region("Master's Keep - Upper Quarters").connect(world.get_region("Throne Room"), "Throne Barrier", Has("Brauner Defeated")) def create_location(player: int, location_data: LocationData, region: Region) -> Location: + from .static_location_data import location_ids location = PoRLocation(player, location_data.name, region) location.region = location_data.region - location.code = 1 + + if location.is_event: + location.code = None + else: + location.code = location_ids[location_data.name] return location @@ -243,6 +248,8 @@ def get_locations_per_region(locations: List[LocationData]) -> Dict[str, List[Lo per_region: Dict[str, List[LocationData]] = {} for location in locations: + if location.region not in region_list: + raise ValueError(f"Error: Invalid region {location.region} provided for location {location.name}") per_region.setdefault(location.region, []).append(location) return per_region \ No newline at end of file diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index ee73b75aaeec..bffc018c05f9 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -84,7 +84,7 @@ "City of Haze: Central Hallway Stairs": 0x60, "Buried Chamber: Right Shade Hall": 0x61, "City of Haze: Right Corner Room": 0x64, - "City of Haze: Left Plaze": 0x65, + "City of Haze: Left Plaza": 0x65, "Great Stairway: Underground Hidden Room": 0x66, "Sandy Grave: Lower Big Underground Room Top": 0x67, "Nation of Fools: Upper Upside Down": 0x68, @@ -124,6 +124,7 @@ "Great Stairway: Upper Grand Staircase Upper Alcove": 0xA1, "City of Haze: Right Dance Hall Secret Room": 0xA7, "Nation of Fools: Bottom Left Medium Square On Wall": 0xA8, + "City of Haze: First Big Room": 0xA9, "Buried Chamber: Final Shaft Item": 0xAA, "Great Stairway: Central Upper": 0xAB, "Forest of Doom: Right Atrium East Top Room": 0xAC, @@ -156,7 +157,7 @@ "Great Stairway: Big Underground Room Lower": 0xDA, "Tower of Death: Elevator Room Middle": 0xDB, "Nation of Fools: Crevice Item": 0xDC, - "City of Haze: First Passsage": 0xDD, + "City of Haze: First Passage": 0xDD, "Forgotten City: Gorgon Room": 0xDE, "City of Haze: Axe Armor Item": 0xE0, "Sandy Grave: Giant Shaft Upper Item": 0xE1, From be5ca419d216946428851070ca07da0371a18110 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Sun, 26 Apr 2026 19:18:22 -0500 Subject: [PATCH 24/58] Map checks for sandy grave --- worlds/cv_por/Locations.py | 49 +++++++++++++++++++++++++++++++++++++- worlds/cv_por/Regions.py | 1 + 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/worlds/cv_por/Locations.py b/worlds/cv_por/Locations.py index 33142dffd7d8..1f4fb08a6d55 100644 --- a/worlds/cv_por/Locations.py +++ b/worlds/cv_por/Locations.py @@ -127,7 +127,54 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("City of Haze - East", "City of Haze: Underground Square Right"), LocationData("City of Haze - East", "City of Haze: Underground Secret Room"), LocationData("City of Haze - East", "City of Haze: Underground Square Left"), - LocationData("City of Haze - East", "Dummy"), + LocationData("City of Haze - East", "City of Haze: Central Hallway Lower"), + LocationData("City of Haze - East", "City of Haze: Boss Room"), + + LocationData("13th Street - Main", "13th Street: Triple Moth Room"), + LocationData("13th Street - Main", "13th Street: Behind Final Guard"), + LocationData("13th Street - Main", "13th Street: Three Amalaric Snipers"), + LocationData("13th Street - Main", "13th Street: Many Nyxes Room"), + LocationData("13th Street - Main", "13th Street: Train Room Secret Right"), + LocationData("13th Street - Main", "13th Street: Underground Secret Room"), + LocationData("13th Street - Main", "13th Street: Underground Demon Room"), + LocationData("13th Street - Main", "13th Street: Left Big Room"), + LocationData("13th Street - Main", "13th Street: Secret Floor Room"), + LocationData("13th Street - Main", "13th Street: East Courtyard"), + LocationData("13th Street - Main", "13th Street: Near Big Room"), + LocationData("13th Street - Main", "13th Street: Bakery Middle"), + LocationData("13th Street - Main", "13th Street: Right Dance Hall Top Right"), + LocationData("13th Street - Main", "13th Street: Bakery Left"), + LocationData("13th Street - Main", "13th Street: Square Room"), + LocationData("13th Street - Main", "13th Street: Bakery Right"), + LocationData("13th Street - Main", "13th Street: Right Dance Hall Top Left"), + LocationData("13th Street - Main", "13th Street: Train Room Secret Left"), + LocationData("13th Street - Main", "13th Street: Boss Room"), + + LocationData("Sandy Grave", "Sandy Grave: Sandworm Stomach"), + LocationData("Sandy Grave", "Sandy Grave: On Statue"), + LocationData("Sandy Grave", "Sandy Grave: Behind Bricks"), + LocationData("Sandy Grave", "Sandy Grave: Left Trap"), + LocationData("Sandy Grave", "Sandy Grave: Right Trap"), + LocationData("Sandy Grave", "Sandy Grave: Bottom Right Room"), + LocationData("Sandy Grave", "Sandy Grave: Lower Big Underground Room Bottom"), + LocationData("Sandy Grave", "Sandy Grave: Lower Big Underground Room Top"), + LocationData("Sandy Grave", "Sandy Grave: Lonely Mimic Alcove"), + LocationData("Sandy Grave", "Sandy Grave: Boulder Room Corner Alcove"), + LocationData("Sandy Grave", "Sandy Grave: Boulder Room Tunnel"), + LocationData("Sandy Grave", "Sandy Grave: Upper Big Underground Top Righ"), + LocationData("Sandy Grave", "Sandy Grave: Upper Big Underground Top Left"), + LocationData("Sandy Grave", "Sandy Grave: Eastmost Item"), + LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Pyramid East 1F"), + LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Giant Shaft Upper Item"), + LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Giant Shaft Middle Item"), + LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Giant Shaft Low Item"), + LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Pyramid 3F Lower"), + LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Pyramid 3F Upper"), + LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Pyramid East 5F"), # check if this is top + LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Fleaman Pit"), + LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Mega Ghost Room Upper"), + LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Mega Ghost Room Lower"), + LocationData("Sandy Grave - Pyramid Top", "Sandy Grave: Pyramid 5F Nook"), LocationData("Dummy", "Dummy"), ] diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py index e02d343bd30c..189ddce7e5cb 100644 --- a/worlds/cv_por/Regions.py +++ b/worlds/cv_por/Regions.py @@ -52,6 +52,7 @@ def __init__(self, player: int, name: str = " ", address: int = None, parent=Non "13th Street - Main", "Sandy Grave", + "Sandy Grave - Upper Pyramid", "Sandy Grave - Pyramid Top", "Forgotten City", From 198042a1ed21c9d3fe9dac3c2118596ddcdef783 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Sun, 26 Apr 2026 22:27:56 -0500 Subject: [PATCH 25/58] Check mapping for forgotten city --- worlds/cv_por/Locations.py | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/worlds/cv_por/Locations.py b/worlds/cv_por/Locations.py index 1f4fb08a6d55..ad41f73d3e08 100644 --- a/worlds/cv_por/Locations.py +++ b/worlds/cv_por/Locations.py @@ -176,6 +176,50 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Mega Ghost Room Lower"), LocationData("Sandy Grave - Pyramid Top", "Sandy Grave: Pyramid 5F Nook"), + LocationData("Forgotten City", "Forgotten City: Outside Left"), + LocationData("Forgotten City", "Forgotten City: Pyramid East 5F"), + LocationData("Forgotten City", "Forgotten City: Armored Fleaman Pit"), + LocationData("Forgotten City", "Forgotten City: Pyramid East 3F"), + LocationData("Forgotten City", "Forgotten City: Pyramid 5F"), + LocationData("Forgotten City", "Forgotten City: Pyramid 1F Bricks"), + LocationData("Forgotten City", "Forgotten City: Lower Boulder Room Tunnel Alcove"), + LocationData("Forgotten City", "Forgotten City: Lower Boulder Room Upper Alcove"), + LocationData("Forgotten City", "Forgotten City: Gorgon Room"), + LocationData("Forgotten City", "Forgotten City: Bottom Chamber"), + LocationData("Forgotten City - Inner", "Forgotten City: Lower Underground Square Lower Item"), + LocationData("Forgotten City - Inner", "Forgotten City: Lower Underground Square Upper Item"), + LocationData("Forgotten City - Inner", "Forgotten City: Upper Underground Square"), + LocationData("Forgotten City - Inner", "Forgotten City: Pyramid East 1F"), + LocationData("Forgotten City - Inner", "Forgotten City: Big Shaft Room Left"), + LocationData("Forgotten City - Inner", "Forgotten City: Big Shaft Room Right"), + LocationData("Forgotten City - Inner", "Forgotten City: Post-Boss Item"), + + LocationData("Nation of Fools - Right Lower", "Nation of Fools: Bottom Corner Room"), + LocationData("Nation of Fools - Right Lower", "Nation of Fools: Bottom-Right Room Left Building"), + LocationData("Nation of Fools - Right Lower", "Nation of Fools: Bottom-Right Room Middle Building"), + LocationData("Nation of Fools - Main", "Nation of Fools: Central Top Shaft"), + LocationData("Nation of Fools - Main", "Nation of Fools: Legion Arena"), + LocationData("Nation of Fools - Main", "Nation of Fools: Mid-Right Corner Room"), + LocationData("Nation of Fools - Main", "Nation of Fools: Left Upper Corner Room"), + LocationData("Nation of Fools - Main", "Nation of Fools: Right Horizontal Room"), + LocationData("Nation of Fools - Main", "Nation of Fools: Right Bottom Corner Room"), + LocationData("Nation of Fools - Main", "Nation of Fools: Top Corner Room"), + LocationData("Nation of Fools - Main", "Nation of Fools: Bottom-Left Room Building"), + LocationData("Nation of Fools - Main", "Nation of Fools: Upper Upside Down"), + LocationData("Nation of Fools - Main", "Nation of Fools: Bottom Left Medium Square On Wall"), + LocationData("Nation of Fools - Main", "Nation of Fools: Crevice Item"), + LocationData("Nation of Fools - Main", "Nation of Fools: Top-Right Room Axe Item"), + LocationData("Nation of Fools - Main", "Nation of Fools: Left Bottom Corner Room"), + LocationData("Nation of Fools - Main", "Nation of Fools: Bottom Right Medium Square On Wall"), + LocationData("Nation of Fools - Main", "Nation of Fools: Right Vertical Room"), + LocationData("Nation of Fools - Main", "Nation of Fools: Top-Right Room Bottom Item"), + LocationData("Nation of Fools - Main", "Nation of Fools: Top-Left Room Right Building"), + LocationData("Nation of Fools - Main", "Nation of Fools: Top-Right Room Breakable Wall"), + LocationData("Nation of Fools - Main", "Nation of Fools: Right Center Room"), + LocationData("Nation of Fools - Main", "Nation of Fools: Left Center Room"), + LocationData("Nation of Fools - Main", "Nation of Fools: Upside Down Building"), + LocationData("Nation of Fools - Main", "Nation of Fools: Secret Room"), + LocationData("Dummy", "Dummy"), ] From 810a918607d20c29d60dcb6b04e91c8dd6a244f7 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Sun, 26 Apr 2026 22:45:45 -0500 Subject: [PATCH 26/58] Finish Base Check Mapping --- worlds/cv_por/Locations.py | 50 +++++++++++++++++++++++++++++++++++--- worlds/cv_por/Regions.py | 2 ++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/worlds/cv_por/Locations.py b/worlds/cv_por/Locations.py index ad41f73d3e08..87825cbfe458 100644 --- a/worlds/cv_por/Locations.py +++ b/worlds/cv_por/Locations.py @@ -220,7 +220,51 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Nation of Fools - Main", "Nation of Fools: Upside Down Building"), LocationData("Nation of Fools - Main", "Nation of Fools: Secret Room"), - LocationData("Dummy", "Dummy"), - ] + LocationData("Burnt Paradise - Entrance", "Burnt Paradise: Left Lower Isolated Room"), + LocationData("Burnt Paradise - Entrance", "Burnt Paradise: Right Lower Isolated Room"), + LocationData("Burnt Paradise - Entrance", "Burnt Paradise: Left Upper Isolated Room"), + LocationData("Burnt Paradise - Entrance", "Burnt Paradise: Right Upper Isolated Room"), + LocationData("Burnt Paradise - Entrance", "Burnt Paradise: Left Upper Big Corner"), + LocationData("Burnt Paradise - Entrance", "Burnt Paradise: Upper Left Big Room Middle Building"), + LocationData("Burnt Paradise - Entrance", "Burnt Paradise: Upper Right Big Room Right Side"), + LocationData("Burnt Paradise - Entrance", "Burnt Paradise: Upper Left Big Room Left Building"), + LocationData("Burnt Paradise - Entrance", "Burnt Paradise: Upper Left Lower Corner Upright Building"), + LocationData("Burnt Paradise - Entrance", "Burnt Paradise: Right Upper Big Corner On Wall"), + LocationData("Burnt Paradise - Entrance", "Burnt Paradise: Upper Right Big Room"), + LocationData("Burnt Paradise - Bottom", "Burnt Paradise: Left Big Corner"), + LocationData("Burnt Paradise - Bottom", "Burnt Paradise: Lower Left Big Room"), + LocationData("Burnt Paradise - Bottom", "Burnt Paradise: Lower Vertical Hall"), + LocationData("Burnt Paradise - Bottom", "Burnt Paradise: Bottom Corner"), + LocationData("Burnt Paradise - Bottom", "Burnt Paradise: Bottom Right Breakable Wall"), + LocationData("Burnt Paradise - Bottom", "Burnt Paradise: Bottom Right Big Room"), + LocationData("Burnt Paradise - Bottom", "Burnt Paradise: Bottom Right Corner Room"), + + LocationData("Forest of Doom - Main", "Forest of Doom: Left Atrium Big Room"), + LocationData("Forest of Doom - Main", "Forest of Doom: Right Atrium East Square Room Right"), + LocationData("Forest of Doom - Main", "Forest of Doom: Left Atrium Top"), + LocationData("Forest of Doom - Main", "Forest of Doom: Right Atrium East Top Room"), + LocationData("Forest of Doom - Main", "Forest of Doom: Right Atrium East Square Room Left"), + LocationData("Forest of Doom - Main", "Forest of Doom: Right Atrium West Top Room"), + LocationData("Forest of Doom - Main", "Forest of Doom: Left Atrium Breakable Wall"), + LocationData("Forest of Doom - Main", "Forest of Doom: Left Atrium Middle Room"), + LocationData("Forest of Doom - Main", "Forest of Doom: Left Atrium Upper Room"), + LocationData("Forest of Doom - Cave", "Forest of Doom: Secret Cave Room"), + LocationData("Forest of Doom - Cave", "Forest of Doom: Near Cave Save Breakable Wall"), + LocationData("Forest of Doom - Cave", "Forest of Doom: Post-Boss Room"), - return location_table \ No newline at end of file + LocationData("Dark Academy - Right Building", "Dark Academy: Right Building Center Room"), + LocationData("Dark Academy - Right Building", "Dark Academy: Right Building Top Floor"), + LocationData("Dark Academy - Main", "Dark Academy: Main Building Ghoul Room"), + LocationData("Dark Academy - Main", "Dark Academy: Main Building Three Toads"), + LocationData("Dark Academy - Main", "Dark Academy: Main Building Secret Ceiling"), + LocationData("Dark Academy - Main", "Dark Academy: Main Building Entrance"), + LocationData("Dark Academy - Main", "Dark Academy: Main Building Secret Malphas Room"), + LocationData("Dark Academy - Main", "Dark Academy: West Building Breakable Wall"), + LocationData("Dark Academy - Main", "Dark Academy: Main Building Square Room Breakable Wall"), + LocationData("Dark Academy - Main", "Dark Academy: Main Building Minotaur Room"), + LocationData("Dark Academy - Main", "Dark Academy: West Building Top Floor Right"), + LocationData("Dark Academy - Main", "Dark Academy: West Building Top Floor Left") + ] + + return location_table + # LocationData("Dummy", "Dummy"), \ No newline at end of file diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py index 189ddce7e5cb..b47de3c34b83 100644 --- a/worlds/cv_por/Regions.py +++ b/worlds/cv_por/Regions.py @@ -71,7 +71,9 @@ def __init__(self, player: int, name: str = " ", address: int = None, parent=Non "Dark Academy - Main", "Burnt Paradise", + "Burnt Paradise - Entrance", "Burnt Paradise - Bottom", + "Nest of Evil" ] From 2a418b268f230c5ac70289b86e95ac0e40295660 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Sun, 26 Apr 2026 23:42:58 -0500 Subject: [PATCH 27/58] huh --- worlds/cv_por/Locations.py | 20 +++++++++++++++----- worlds/cv_por/Regions.py | 6 +++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/worlds/cv_por/Locations.py b/worlds/cv_por/Locations.py index 87825cbfe458..e09dce151f1b 100644 --- a/worlds/cv_por/Locations.py +++ b/worlds/cv_por/Locations.py @@ -13,6 +13,8 @@ class LocationData(NamedTuple): def get_locations(world: "PoRWorld") -> List[LocationData]: location_table: List[LocationData] = [ + LocationData("Entrance - Wind's Room", "Quest: Preparations"), + LocationData("Entrance - Hub", "Entrance: Drawbridge Pit Item"), LocationData("Entrance - Hub", "Entrance: Drawbridge Switch Item"), LocationData("Entrance - Hub", "Entrance: Drawbridge Upper Item"), @@ -87,11 +89,13 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Tower of Death - Second Gear Room", "Tower of Death: Top Gears Room"), LocationData("Tower of Death - Second Gear Room", "Tower of Death: Third Gears Room"), LocationData("Tower of Death - Top of the Tower", "Tower of Death: Top of the Tower"), + LocationData("Tower of Death - Top of the Tower", "Tower of Death: Elevator Switch", True), LocationData("Master's Keep - Lower", "Master's Keep: Entrance"), LocationData("Master's Keep - Main", "Master's Keep: Hidden Room"), LocationData("Master's Keep - Main", "Master's Keep: Right Square Room"), LocationData("Master's Keep - Portrait Room", "Master's Keep: Whip Trial"), + LocationData("Master's Keep - Portrait Room", "Lost Gallery: Studio Portrait Fight", True), LocationData("City of Haze", "City of Haze: Meat Room"), LocationData("City of Haze", "City of Haze: First Passage"), @@ -128,7 +132,7 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("City of Haze - East", "City of Haze: Underground Secret Room"), LocationData("City of Haze - East", "City of Haze: Underground Square Left"), LocationData("City of Haze - East", "City of Haze: Central Hallway Lower"), - LocationData("City of Haze - East", "City of Haze: Boss Room"), + LocationData("City of Haze - East", "City of Haze: Boss Room", True), LocationData("13th Street - Main", "13th Street: Triple Moth Room"), LocationData("13th Street - Main", "13th Street: Behind Final Guard"), @@ -148,7 +152,7 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("13th Street - Main", "13th Street: Bakery Right"), LocationData("13th Street - Main", "13th Street: Right Dance Hall Top Left"), LocationData("13th Street - Main", "13th Street: Train Room Secret Left"), - LocationData("13th Street - Main", "13th Street: Boss Room"), + LocationData("13th Street - Main", "13th Street: Boss Room", True), LocationData("Sandy Grave", "Sandy Grave: Sandworm Stomach"), LocationData("Sandy Grave", "Sandy Grave: On Statue"), @@ -161,7 +165,7 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Sandy Grave", "Sandy Grave: Lonely Mimic Alcove"), LocationData("Sandy Grave", "Sandy Grave: Boulder Room Corner Alcove"), LocationData("Sandy Grave", "Sandy Grave: Boulder Room Tunnel"), - LocationData("Sandy Grave", "Sandy Grave: Upper Big Underground Top Righ"), + LocationData("Sandy Grave", "Sandy Grave: Upper Big Underground Top Right"), LocationData("Sandy Grave", "Sandy Grave: Upper Big Underground Top Left"), LocationData("Sandy Grave", "Sandy Grave: Eastmost Item"), LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Pyramid East 1F"), @@ -175,6 +179,7 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Mega Ghost Room Upper"), LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Mega Ghost Room Lower"), LocationData("Sandy Grave - Pyramid Top", "Sandy Grave: Pyramid 5F Nook"), + LocationData("Sandy Grave - Pyramid Top", "Sandy Grave: Boss Room", True), LocationData("Forgotten City", "Forgotten City: Outside Left"), LocationData("Forgotten City", "Forgotten City: Pyramid East 5F"), @@ -193,6 +198,7 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Forgotten City - Inner", "Forgotten City: Big Shaft Room Left"), LocationData("Forgotten City - Inner", "Forgotten City: Big Shaft Room Right"), LocationData("Forgotten City - Inner", "Forgotten City: Post-Boss Item"), + LocationData("Forgotten City - Inner", "Forgotten City: Boss Room", True), LocationData("Nation of Fools - Right Lower", "Nation of Fools: Bottom Corner Room"), LocationData("Nation of Fools - Right Lower", "Nation of Fools: Bottom-Right Room Left Building"), @@ -219,6 +225,7 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Nation of Fools - Main", "Nation of Fools: Left Center Room"), LocationData("Nation of Fools - Main", "Nation of Fools: Upside Down Building"), LocationData("Nation of Fools - Main", "Nation of Fools: Secret Room"), + LocationData("Nation of Fools - Main", "Nation of Fools: Boss Room", True), LocationData("Burnt Paradise - Entrance", "Burnt Paradise: Left Lower Isolated Room"), LocationData("Burnt Paradise - Entrance", "Burnt Paradise: Right Lower Isolated Room"), @@ -238,6 +245,7 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Burnt Paradise - Bottom", "Burnt Paradise: Bottom Right Breakable Wall"), LocationData("Burnt Paradise - Bottom", "Burnt Paradise: Bottom Right Big Room"), LocationData("Burnt Paradise - Bottom", "Burnt Paradise: Bottom Right Corner Room"), + LocationData("Burnt Paradise - Bottom", "Burnt Paradise: Boss Room", True), LocationData("Forest of Doom - Main", "Forest of Doom: Left Atrium Big Room"), LocationData("Forest of Doom - Main", "Forest of Doom: Right Atrium East Square Room Right"), @@ -251,6 +259,7 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Forest of Doom - Cave", "Forest of Doom: Secret Cave Room"), LocationData("Forest of Doom - Cave", "Forest of Doom: Near Cave Save Breakable Wall"), LocationData("Forest of Doom - Cave", "Forest of Doom: Post-Boss Room"), + LocationData("Forest of Doom - Cave", "Forest of Doom: Boss Room", True), LocationData("Dark Academy - Right Building", "Dark Academy: Right Building Center Room"), LocationData("Dark Academy - Right Building", "Dark Academy: Right Building Top Floor"), @@ -263,8 +272,9 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Dark Academy - Main", "Dark Academy: Main Building Square Room Breakable Wall"), LocationData("Dark Academy - Main", "Dark Academy: Main Building Minotaur Room"), LocationData("Dark Academy - Main", "Dark Academy: West Building Top Floor Right"), - LocationData("Dark Academy - Main", "Dark Academy: West Building Top Floor Left") + LocationData("Dark Academy - Main", "Dark Academy: West Building Top Floor Left"), + LocationData("Dark Academy - Main", "Dark Academy: Boss Room", True) ] - + return location_table # LocationData("Dummy", "Dummy"), \ No newline at end of file diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py index b47de3c34b83..baa0899e9b9f 100644 --- a/worlds/cv_por/Regions.py +++ b/worlds/cv_por/Regions.py @@ -180,8 +180,8 @@ def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: {"Master's Keep - Portrait Room": can_cast_spell & Has("Sanctuary")}) world.get_region("Master's Keep - Portrait Room").add_exits([world.portrait_connections["Forgotten City"], world.portrait_connections["Burnt Paradise"], world.portrait_connections["Dark Academy"], world.portrait_connections["13th Street"]], - {world.portrait_connections["13th Street"]: CanReachLocation("Forgotten City - Boss Room"), - world.portrait_connections["Burnt Paradise"]: CanReachLocation("13th Street - Boss Room")}) + {world.portrait_connections["13th Street"]: CanReachLocation("Forgotten City: Boss Room"), + world.portrait_connections["Burnt Paradise"]: CanReachLocation("Dark Academy: Boss Room")}) world.get_region("City of Haze").add_exits(["City of Haze - East"], {"City of Haze - East": Has("Puppet Master") | (has_change_cube & Has("Call Cube"))}) @@ -229,7 +229,7 @@ def create_location(player: int, location_data: LocationData, region: Region) -> location = PoRLocation(player, location_data.name, region) location.region = location_data.region - if location.is_event: + if location_data.is_event: location.code = None else: location.code = location_ids[location_data.name] From 4879ea9064c4468ab4c8e4023ef3352ca4605890 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Mon, 27 Apr 2026 00:30:39 -0500 Subject: [PATCH 28/58] refactor region building --- worlds/cv_por/Regions.py | 97 ++++++++++++++++++--------------------- worlds/cv_por/__init__.py | 2 +- 2 files changed, 45 insertions(+), 54 deletions(-) diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py index baa0899e9b9f..adaf98ce3317 100644 --- a/worlds/cv_por/Regions.py +++ b/worlds/cv_por/Regions.py @@ -6,11 +6,10 @@ if TYPE_CHECKING: from . import PoRWorld + class PoRLocation(Location): game: str = "Castlevania: Portrait of Ruin" - def __init__(self, player: int, name: str = " ", address: int = None, parent=None): - super().__init__(player, name, address, parent) region_list = [ "Entrance - Wind's Room", # Used for quests/shops @@ -78,28 +77,55 @@ def __init__(self, player: int, name: str = " ", address: int = None, parent=Non ] +has_change_cube = Has("Change Cube", options=[OptionFilter(StartWithChangeCube, 0)], filtered_resolution=True) -def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: - has_change_cube = Has("Change Cube", options=[OptionFilter(StartWithChangeCube, 0)], filtered_resolution=True) +can_cast_spell = Has("Call Cube") | has_change_cube +small_uppies = HasAny("Stone of Flight", "Griffon Wing") | HasAll("Call Cube", "Acrobat Cube") | (can_cast_spell & Has("Owl Morph")) +medium_uppies = HasAny("Stone of Flight", "Griffon Wing") | (can_cast_spell & Has("Owl Morph")) +big_uppies = Has("Griffon Wing") | (can_cast_spell & Has("Owl Morph")) +is_smol = Has("Lizard Tail") | (can_cast_spell & Has("Owl Morph")) | (can_cast_spell & Has("Toad Morph")) - can_cast_spell = Has("Call Cube") | has_change_cube - small_uppies = HasAny("Stone of Flight", "Griffon Wing") | HasAll("Call Cube", "Acrobat Cube") | (can_cast_spell & Has("Owl Morph")) - medium_uppies = HasAny("Stone of Flight", "Griffon Wing") | (can_cast_spell & Has("Owl Morph")) - big_uppies = Has("Griffon Wing") | (can_cast_spell & Has("Owl Morph")) - is_smol = Has("Lizard Tail") | (can_cast_spell & Has("Owl Morph")) | (can_cast_spell & Has("Toad Morph")) - locations_per_region = get_locations_per_region(locations) +def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: + regions = [] if world.options.goal: regions.append("Throne Room") - regions = [] - for area in region_list: - regions.append(create_region(world, world.player, locations_per_region, area)) + regions.append(Region(area, world.player, world.multiworld)) world.multiworld.regions += regions + create_locations(world) + connect_regions(world) + + +def create_location(player: int, location_data: LocationData, region: Region) -> Location: + from .static_location_data import location_ids + location = PoRLocation(player, location_data.name, None if location_data.is_event else location_ids[location_data.name], region) + location.region = location_data.region + + return location + + +def create_locations(world): + from .static_location_data import location_ids + all_locations = get_locations(world) + + for location in all_locations: + if location.is_event: + code = None + else: + code = location_ids[location.name] # Lookup the appropriate ID number + + if location.region not in region_list: + raise ValueError(f"Error: Region {location.name} is invalid for location {location.name}.") + else: + region = world.get_region(location.region) + region.locations.append(PoRLocation(world.player, location.name, code, region)) + +def connect_regions(world): world.get_region("Entrance - Hub").add_exits(["Entrance - Wind's Room", "Entrance - Behemoth Area", "Entrance - Hub Painting Room", "Entrance - Upper Area", "Entrance - Underground Passage"], {"Entrance - Behemoth Area": small_uppies, "Entrance - Hub Painting Room": is_smol, @@ -157,7 +183,6 @@ def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: world.get_region("Tower of Death - First Gear Room").add_exits(["Tower of Death - Bottom", "Tower of Death - Ascent"], {"Tower of Death - Ascent": can_cast_spell & HasAny("Owl Morph", "Toad Morph")}) - world.get_region("Tower of Death - Ascent").add_exits(["Tower of Death - First Gear Room", "Tower of Death - Second Gear Room"], {"Tower of Death - First Gear Room": can_cast_spell & HasAny("Owl Morph", "Toad Morph"), "Tower of Death - Second Gear Room": medium_uppies}) @@ -166,7 +191,6 @@ def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: world.get_region("Tower of Death - Top of the Tower").add_exits(["Tower of Death - Second Gear Room", "Tower of Death - Elevator Room"]) - world.get_region("Master's Keep - Bridge").add_exits(["Tower of Death - Belt Area", "Tower of Death - Elevator Room"]) world.get_region("Master's Keep - Lower").add_exits(["Tower of Death - Elevator Room", "Master's Keep - Bridge", "Master's Keep - Main"], @@ -206,53 +230,20 @@ def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: {"Dark Academy - Right Building": small_uppies | Has("Puppet Master")}) world.get_region("Dark Academy - Right Building").add_exits(["Dark Academy - Main"], - {"Dark Academy - Main": big_uppies}) + {"Dark Academy - Main": big_uppies}) world.get_region("Forgotten City").add_exits(["Forgotten City - Inner"], - {"Forgotten City - Inner": medium_uppies | Has("Puppet Master")}) + {"Forgotten City - Inner": medium_uppies | Has("Puppet Master")}) world.get_region("13th Street").add_exits(["13th Street - Main"], - {"13th Street - Main": HasAll("Strength Glove", "Push Cube", "Call Cube")}) + {"13th Street - Main": HasAll("Strength Glove", "Push Cube", "Call Cube")}) world.get_region("Burnt Paradise").add_exits(["Burnt Paradise - Entrance"], - {"Burnt Paradise - Entrance": small_uppies | Has("Puppet Master")}) + {"Burnt Paradise - Entrance": small_uppies | Has("Puppet Master")}) world.get_region("Burnt Paradise - Entrance").add_exits(["Burnt Paradise - Bottom"], - {"Burnt Paradise - Bottom": big_uppies}) + {"Burnt Paradise - Bottom": big_uppies}) if world.options.goal: # Add a connection to the throne room if necessary world.get_region("Master's Keep - Upper Quarters").connect(world.get_region("Throne Room"), "Throne Barrier", Has("Brauner Defeated")) - -def create_location(player: int, location_data: LocationData, region: Region) -> Location: - from .static_location_data import location_ids - location = PoRLocation(player, location_data.name, region) - location.region = location_data.region - - if location_data.is_event: - location.code = None - else: - location.code = location_ids[location_data.name] - - return location - -def create_region(world: "PoRWorld", player: int, locations_per_region: Dict[str, List[LocationData]], name: str) -> Region: - region = Region(name, player, world.multiworld) - - if name in locations_per_region: - for location_data in locations_per_region[name]: - location = create_location(player, location_data, region) - region.locations.append(location) - - return region - - -def get_locations_per_region(locations: List[LocationData]) -> Dict[str, List[LocationData]]: - per_region: Dict[str, List[LocationData]] = {} - - for location in locations: - if location.region not in region_list: - raise ValueError(f"Error: Invalid region {location.region} provided for location {location.name}") - per_region.setdefault(location.region, []).append(location) - - return per_region \ No newline at end of file diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py index ecdc10fbe96a..2e95dc66fbe9 100644 --- a/worlds/cv_por/__init__.py +++ b/worlds/cv_por/__init__.py @@ -42,7 +42,7 @@ class PoRWorld(World): game = "Castlevania: Portrait of Ruin" option_definitions = PoROptions data_version = 1 - origin_region_name = "Dracula's Castle: Entrance" + origin_region_name = "Entrance - Hub" item_name_to_id = {item: item_table[item].code for item in item_table} location_name_to_id = location_ids From 0e3cfacf89b9214c8202d036f21273b68b55d651 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Mon, 27 Apr 2026 00:31:09 -0500 Subject: [PATCH 29/58] , --- worlds/cv_por/Regions.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py index adaf98ce3317..16e2b0aef184 100644 --- a/worlds/cv_por/Regions.py +++ b/worlds/cv_por/Regions.py @@ -100,13 +100,6 @@ def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: connect_regions(world) -def create_location(player: int, location_data: LocationData, region: Region) -> Location: - from .static_location_data import location_ids - location = PoRLocation(player, location_data.name, None if location_data.is_event else location_ids[location_data.name], region) - location.region = location_data.region - - return location - def create_locations(world): from .static_location_data import location_ids From ed96c3122c35819ef69dd0b74115418a1f8ccde1 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Mon, 27 Apr 2026 00:51:41 -0500 Subject: [PATCH 30/58] Finish location mapping --- worlds/cv_por/Locations.py | 25 +++++++++++++++++++++---- worlds/cv_por/Regions.py | 12 +++--------- worlds/cv_por/__init__.py | 6 +++++- worlds/cv_por/generator_main.py | 28 ++++++++++++++++++++++++++-- worlds/cv_por/setup_game.py | 2 +- 5 files changed, 56 insertions(+), 17 deletions(-) diff --git a/worlds/cv_por/Locations.py b/worlds/cv_por/Locations.py index e09dce151f1b..3381e55582d0 100644 --- a/worlds/cv_por/Locations.py +++ b/worlds/cv_por/Locations.py @@ -1,5 +1,5 @@ from typing import List, Optional, NamedTuple, TYPE_CHECKING -from .static_location_data import location_ids +from .Options import NestofEvil if TYPE_CHECKING: from . import PoRWorld @@ -10,8 +10,8 @@ class LocationData(NamedTuple): name: str is_event: Optional[bool] = False -def get_locations(world: "PoRWorld") -> List[LocationData]: +def get_locations(world: "PoRWorld") -> List[LocationData]: location_table: List[LocationData] = [ LocationData("Entrance - Wind's Room", "Quest: Preparations"), @@ -174,7 +174,7 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Giant Shaft Low Item"), LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Pyramid 3F Lower"), LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Pyramid 3F Upper"), - LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Pyramid East 5F"), # check if this is top + LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Pyramid East 5F"), # check if this is top LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Fleaman Pit"), LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Mega Ghost Room Upper"), LocationData("Sandy Grave - Upper Pyramid", "Sandy Grave: Mega Ghost Room Lower"), @@ -276,5 +276,22 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Dark Academy - Main", "Dark Academy: Boss Room", True) ] + if world.options.goal: + location_table += [ + LocationData("The Throne Room", "The Throne Room: Above Throne Left"), + LocationData("The Throne Room", "The Throne Room: Great Stairs Under Stairs"), + LocationData("The Throne Room", "The Throne Room: Great Stairs Center"), + LocationData("The Throne Room", "The Throne Room: Above Throne Right"), + LocationData("The Throne Room", "The Throne Room: Great Stairs Hidden"), + LocationData("The Throne Room", "The Throne Room: Great Stairs Left"), + LocationData("The Throne Room", "The Throne Room: Dracula", True), + ] + + if world.options.nest_of_evil_state != NestofEvil.option_removed: + location_table += [ + LocationData("Nest of Evil", "Nest of Evil: Doppelganger Reward"), + LocationData("Nest of Evil", "Nest of Evil: First Item"), + LocationData("Nest of Evil", "Nest of Evil: Second Item")] + return location_table - # LocationData("Dummy", "Dummy"), \ No newline at end of file + # LocationData("Dummy", "Dummy"), diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py index 16e2b0aef184..ebf710a80b7f 100644 --- a/worlds/cv_por/Regions.py +++ b/worlds/cv_por/Regions.py @@ -86,7 +86,7 @@ class PoRLocation(Location): is_smol = Has("Lizard Tail") | (can_cast_spell & Has("Owl Morph")) | (can_cast_spell & Has("Toad Morph")) -def init_areas(world: "PoRWorld", locations: List[LocationData]) -> None: +def init_areas(world: "PoRWorld") -> None: regions = [] if world.options.goal: @@ -106,16 +106,10 @@ def create_locations(world): all_locations = get_locations(world) for location in all_locations: - if location.is_event: - code = None - else: - code = location_ids[location.name] # Lookup the appropriate ID number - if location.region not in region_list: raise ValueError(f"Error: Region {location.name} is invalid for location {location.name}.") - else: - region = world.get_region(location.region) - region.locations.append(PoRLocation(world.player, location.name, code, region)) + region = world.get_region(location.region) + region.locations.append(PoRLocation(world.player, location.name, None if location.is_event else location_ids[location.name], region)) def connect_regions(world): diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py index 2e95dc66fbe9..8d2d9cf7abf1 100644 --- a/worlds/cv_por/__init__.py +++ b/worlds/cv_por/__init__.py @@ -73,4 +73,8 @@ def generate_early(self): def create_regions(self): from .generator_main import create_regions - create_regions(self) \ No newline at end of file + create_regions(self) + + def create_items(self): + from .generator_main import create_items + create_items(self) \ No newline at end of file diff --git a/worlds/cv_por/generator_main.py b/worlds/cv_por/generator_main.py index e1ef9fa1eea2..3d244f874921 100644 --- a/worlds/cv_por/generator_main.py +++ b/worlds/cv_por/generator_main.py @@ -1,4 +1,6 @@ +from BaseClasses import Item from .Locations import get_locations +from typing import List def generate_early(world) -> None: from .setup_game import setup_game @@ -11,5 +13,27 @@ def create_regions(world) -> None: from .setup_game import place_static_items from .Regions import init_areas - init_areas(world, get_locations(world)) - place_static_items(world) \ No newline at end of file + init_areas(world) + place_static_items(world) + +def create_items(world) -> None: + pool = get_item_pool(world.get_excluded_items()) + fill_pool(pool) + + world.multiworld.itempool += pool + +def fill_pool(world, pool: List[Item]) -> None: + for _ in range(len(world.multiworld.get_unfilled_locations(world.player)) - len(pool) - world.extra_item_count): # Change to fix event count + item = self.set_classifications(world.get_filler_item_name()) + pool.append(item) + + +def get_item_pool(world) -> List[Item]: + pool: List[Item] = [] + + for name, data in item_table.items(): + for _ in range(data.amount): + item = self.set_classifications(name) + pool.append(item) + + return pool \ No newline at end of file diff --git a/worlds/cv_por/setup_game.py b/worlds/cv_por/setup_game.py index e22c1dd8d647..2515a046cb7a 100644 --- a/worlds/cv_por/setup_game.py +++ b/worlds/cv_por/setup_game.py @@ -12,5 +12,5 @@ def setup_game(world): } def place_static_items(world): - print("aaaaaaaaa") + print("TODO. THIS.") #world.get_location("Lost Village: Moat Drain Switch").place_locked_item(world.create_item("Moat Drained")) \ No newline at end of file From fa16d81cd57506f84585430e2695bb6023e6befe Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Mon, 27 Apr 2026 17:07:59 -0500 Subject: [PATCH 31/58] Fill the item pool --- worlds/cv_por/Items.py | 134 +++++++++++--- worlds/cv_por/Locations.py | 6 +- worlds/cv_por/Options.py | 4 +- worlds/cv_por/Regions.py | 5 +- worlds/cv_por/__init__.py | 257 +++++++++++++++++++++++++- worlds/cv_por/generator_main.py | 91 +++++++-- worlds/cv_por/setup_game.py | 3 +- worlds/cv_por/static_location_data.py | 2 +- 8 files changed, 450 insertions(+), 52 deletions(-) diff --git a/worlds/cv_por/Items.py b/worlds/cv_por/Items.py index 8f7efb56717e..cb32bf380873 100644 --- a/worlds/cv_por/Items.py +++ b/worlds/cv_por/Items.py @@ -1,11 +1,13 @@ from typing import Dict, Set, NamedTuple, Optional from BaseClasses import ItemClassification + class ItemData(NamedTuple): category: str code: Optional[int] classification: ItemClassification - amount: Optional[int] = 1 + default_count: Optional[int] = 1 + item_table: Dict[str, ItemData] = { "$1": ItemData("Money", 0x100, ItemClassification.filler, 0), @@ -151,7 +153,7 @@ class ItemData(NamedTuple): "Jagdplaute": ItemData("Swords", 0x0324, ItemClassification.filler, 0), "Stellar Sword": ItemData("Swords", 0x0325, ItemClassification.useful, 0), "Damascus Sword": ItemData("Swords", 0x0326, ItemClassification.filler, 0), - "Claymore": ItemData("Greatswords", 0x0327, ItemClassification.useful), + "Claymore": ItemData("Greatswords", 0x0327, ItemClassification.filler, 0), "Falchion": ItemData("Greatswords", 0x0328, ItemClassification.filler, 0), "Great Sword": ItemData("Greatswords", 0x0329, ItemClassification.filler, 0), "Zweihander": ItemData("Greatswords", 0x032A, ItemClassification.filler, 0), @@ -188,7 +190,7 @@ class ItemData(NamedTuple): "Casual Clothes": ItemData("Body Armor", 0x0401, ItemClassification.filler, 0), "Hobo's Clothes": ItemData("Body Armor", 0x0402, ItemClassification.filler, 0), - "Houppelande": ItemData("Body Armor", 0x0403, ItemClassification.useful), + "Houppelande": ItemData("Body Armor", 0x0403, ItemClassification.filler, 0), "Poncho": ItemData("Body Armor", 0x0404, ItemClassification.filler, 0), "Combat Fatigues": ItemData("Body Armor", 0x0405, ItemClassification.filler, 0), "Adrenaline Gear": ItemData("Body Armor", 0x0406, ItemClassification.filler, 0), @@ -197,18 +199,18 @@ class ItemData(NamedTuple): "Tailcoat": ItemData("Jonathan Armor", 0x0409, ItemClassification.filler, 0), "Biker's Jacket": ItemData("Jonathan Armor", 0x040A, ItemClassification.filler, 0), "Justaucorps": ItemData("Jonathan Armor", 0x040B, ItemClassification.filler, 0), - "Tuxedo Coat": ItemData("Jonathan Armor", 0x040C, ItemClassification.useful), - "Battle Jacket": ItemData("Jonathan Armor", 0x040D, ItemClassification.useful), + "Tuxedo Coat": ItemData("Jonathan Armor", 0x040C, ItemClassification.filler, 0), + "Battle Jacket": ItemData("Jonathan Armor", 0x040D, ItemClassification.filler, 0), "Surcoat": ItemData("Jonathan Armor", 0x040E, ItemClassification.filler, 0), "Leather Cuirass": ItemData("Jonathan Armor", 0x040F, ItemClassification.filler, 0), "Copper Plate": ItemData("Jonathan Armor", 0x0410, ItemClassification.filler, 0), "Iron Plate": ItemData("Jonathan Armor", 0x0411, ItemClassification.filler, 0), "Silver Plate": ItemData("Jonathan Armor", 0x0412, ItemClassification.filler, 0), "Gold Plate": ItemData("Jonathan Armor", 0x0413, ItemClassification.filler, 0), - "Lorica": ItemData("Jonathan Armor", 0x0414, ItemClassification.useful), + "Lorica": ItemData("Jonathan Armor", 0x0414, ItemClassification.filler, 0), "Platinum Plate": ItemData("Jonathan Armor", 0x0415, ItemClassification.filler, 0), "Samurai Plate": ItemData("Jonathan Armor", 0x0416, ItemClassification.filler, 0), - "Holy Mail": ItemData("Jonathan Armor", 0x0417, ItemClassification.useful), + "Holy Mail": ItemData("Jonathan Armor", 0x0417, ItemClassification.filler, 0), "Berserker Mail": ItemData("Jonathan Armor", 0x0418, ItemClassification.filler, 0), "Ancient Armor": ItemData("Jonathan Armor", 0x0419, ItemClassification.filler, 0), "Healing Mail": ItemData("Jonathan Armor", 0x041A, ItemClassification.useful), @@ -226,19 +228,19 @@ class ItemData(NamedTuple): "Lilith Corset": ItemData("Charlotte Armor", 0x0426, ItemClassification.filler, 0), "Platinum Corset": ItemData("Charlotte Armor", 0x0427, ItemClassification.useful), "Diamond Corset": ItemData("Charlotte Armor", 0x0428, ItemClassification.filler, 0), - "Kirtle": ItemData("Charlotte Armor", 0x0429, ItemClassification.useful), + "Kirtle": ItemData("Charlotte Armor", 0x0429, ItemClassification.filler, 0), "Miko Dress": ItemData("Charlotte Armor", 0x042A, ItemClassification.filler, 0), "Cotton Apron": ItemData("Charlotte Armor", 0x042B, ItemClassification.filler, 0), "Silk Negligee": ItemData("Charlotte Armor", 0x042C, ItemClassification.filler, 0), - "Bathrobe": ItemData("Charlotte Armor", 0x042D, ItemClassification.useful), + "Bathrobe": ItemData("Charlotte Armor", 0x042D, ItemClassification.filler, 0), "Frilly Camisole": ItemData("Charlotte Armor", 0x042E, ItemClassification.filler, 0), - "Sequined Dress": ItemData("Charlotte Armor", 0x042F, ItemClassification.useful), + "Sequined Dress": ItemData("Charlotte Armor", 0x042F, ItemClassification.filler, 0), "Clown Shirt": ItemData("Charlotte Armor", 0x0430, ItemClassification.filler, 0), "Nun's Robes": ItemData("Charlotte Armor", 0x0431, ItemClassification.useful), "Cocktail Dress": ItemData("Charlotte Armor", 0x0432, ItemClassification.filler, 0), - "Dancer's Blouse": ItemData("Charlotte Armor", 0x0433, ItemClassification.useful), + "Dancer's Blouse": ItemData("Charlotte Armor", 0x0433, ItemClassification.filler, 0), "Cotehardie": ItemData("Charlotte Armor", 0x0434, ItemClassification.filler, 0), - "Wedding Dress": ItemData("Charlotte Armor", 0x0435, ItemClassification.useful), + "Wedding Dress": ItemData("Charlotte Armor", 0x0435, ItemClassification.filler, 0), "Europa's Dress": ItemData("Charlotte Armor", 0x0436, ItemClassification.filler, 0), "Dalmatica": ItemData("Charlotte Armor", 0x0437, ItemClassification.filler, 0), "Princess Coat": ItemData("Charlotte Armor", 0x0438, ItemClassification.filler, 0), @@ -247,7 +249,7 @@ class ItemData(NamedTuple): "Eye for Decay": ItemData("Headgear", 0x0501, ItemClassification.filler, 0), "Gambler Glasses": ItemData("Headgear", 0x0502, ItemClassification.useful, 0), "Sunglasses": ItemData("Headgear", 0x0503, ItemClassification.filler, 0), - "Thick Glasses": ItemData("Headgear", 0x0504, ItemClassification.useful), + "Thick Glasses": ItemData("Headgear", 0x0504, ItemClassification.filler, 0), "Glasses": ItemData("Headgear", 0x0505, ItemClassification.filler, 0), "Monocle": ItemData("Headgear", 0x0506, ItemClassification.filler, 0), "Goggles": ItemData("Headgear", 0x0507, ItemClassification.filler, 0), @@ -272,8 +274,8 @@ class ItemData(NamedTuple): "Witch's Hat": ItemData("Charlotte Headgear", 0x051A, ItemClassification.filler, 0), "Mourning Veil": ItemData("Charlotte Headgear", 0x051B, ItemClassification.filler, 0), "Nun's Habit": ItemData("Charlotte Headgear", 0x051C, ItemClassification.useful), - "Open Veil": ItemData("Charlotte Headgear", 0x051D, ItemClassification.useful), - "Holy Veil": ItemData("Charlotte Headgear", 0x051E, ItemClassification.useful), + "Open Veil": ItemData("Charlotte Headgear", 0x051D, ItemClassification.filler, 0), + "Holy Veil": ItemData("Charlotte Headgear", 0x051E, ItemClassification.filler, 0), "Clover Tiara": ItemData("Charlotte Headgear", 0x051F, ItemClassification.useful), "Pearl Tiara": ItemData("Charlotte Headgear", 0x0520, ItemClassification.filler, 0), "Diamond Tiara": ItemData("Charlotte Headgear", 0x0521, ItemClassification.filler, 0), @@ -295,8 +297,8 @@ class ItemData(NamedTuple): "Oxfords": ItemData("Jonathan Boots", 0x060B, ItemClassification.filler, 0), "Wingtips": ItemData("Jonathan Boots", 0x060C, ItemClassification.filler, 0), "Iron Leggings": ItemData("Jonathan Boots", 0x060D, ItemClassification.filler, 0), - "Silver Leggings": ItemData("Jonathan Boots", 0x060E, ItemClassification.useful), - "Gold Leggings": ItemData("Jonathan Boots", 0x060F, ItemClassification.useful), + "Silver Leggings": ItemData("Jonathan Boots", 0x060E, ItemClassification.filler, 0), + "Gold Leggings": ItemData("Jonathan Boots", 0x060F, ItemClassification.filler, 0), "Steel Greaves": ItemData("Jonathan Boots", 0x0610, ItemClassification.useful), "Platinum Lggngs": ItemData("Jonathan Boots", 0x0611, ItemClassification.filler, 0), "Samurai Leggngs": ItemData("Jonathan Boots", 0x0612, ItemClassification.useful), @@ -329,9 +331,9 @@ class ItemData(NamedTuple): "Immunity Ring": ItemData("Accessories", 0x0710, ItemClassification.useful, 0), "Blessed Ring": ItemData("Accessories", 0x0711, ItemClassification.useful, 0), "Hercules Ring": ItemData("Accessories", 0x0712, ItemClassification.filler, 0), - "Cape": ItemData("Accessories", 0x0713, ItemClassification.useful), + "Cape": ItemData("Accessories", 0x0713, ItemClassification.filler, 0), "Hero's Cape": ItemData("Accessories", 0x0714, ItemClassification.filler, 0), - "Blue Cape": ItemData("Accessories", 0x0715, ItemClassification.useful), + "Blue Cape": ItemData("Accessories", 0x0715, ItemClassification.filler, 0), "Elven Cape": ItemData("Accessories", 0x0716, ItemClassification.filler, 0), "Assassin's Cape": ItemData("Accessories", 0x0717, ItemClassification.useful), "Invisible Cape": ItemData("Accessories", 0x0718, ItemClassification.filler, 0), @@ -362,8 +364,8 @@ class ItemData(NamedTuple): "Suzaku": ItemData("Subweapons", 0x0807, ItemClassification.filler, 0), "Byakko": ItemData("Subweapons", 0x0808, ItemClassification.filler, 0), "Gnebu": ItemData("Subweapons", 0x0809, ItemClassification.filler, 0), - "Knife": ItemData("Subweapons", 0x080A, ItemClassification.useful), - "Axe": ItemData("Subweapons", 0x080B, ItemClassification.filler, 0), + "Knife Subweapon": ItemData("Subweapons", 0x080A, ItemClassification.useful), + "Axe Subweapon": ItemData("Subweapons", 0x080B, ItemClassification.filler, 0), "Cross": ItemData("Subweapons", 0x080C, ItemClassification.useful, 0), "Holy Water": ItemData("Subweapons", 0x080D, ItemClassification.useful, 0), "Bible": ItemData("Subweapons", 0x080E, ItemClassification.useful, 0), @@ -464,6 +466,7 @@ class ItemData(NamedTuple): "Book of Spirits": ItemData("Relics", 0x086B, ItemClassification.useful) } + def get_item_names_per_category() -> Dict[str, Set[str]]: categories: Dict[str, Set[str]] = {} @@ -472,3 +475,92 @@ def get_item_names_per_category() -> Dict[str, Set[str]]: categories.setdefault(data.category, set()).add(name) return categories + + +money_table = [ + "$1", + "$10", + "$50", + "$100", + "$500", + "$1000", + "$2000" +] + +consumable_table = [ + "Potion", + "High Potion", + "Tonic", + "High Tonic", + "Anti-Venom", + "Uncurse Potion", + "Beef Jerky", + "Rice Ball", + "Mushroom", + "Corn Soup", + "Mint Sundae", + "Cream Puff", + "Milk", + "Coffee", + "Tea", + "Pudding", + "Strawberry", + "Grapes", + "Hamburger", + "Fried Chicken", + "Hot Dog", + "Pumpkin Pie", + "Pancake", + "Tinned Spinach", + "Paella", + "Spaghetti", + "Ice Cream", + "Meuniere", + "Boiled Starfish", + "Wheat Roll", + "Sachertorte", + "NY Cheesecake", + "Mille-feuille", + "Tarte au Poire", + "Gateau Fraise", + "Kugelhopf", + "Green Tea Cake", + "Gateau Marron", + "Langues de Chat", + "Financier", + "Tropical Sundae", + "Pudding Bucket", + "Piyoko", + "1000 Year Egg", + "Jamon Iberico", + "Spicy Shrimp", + "Pasta Carbonara", + "Penne Arrabiata", + "Powdered Milk", + "Rotten Durian", + "Ground Meat", + "Noodle Bun", + "Touch & Pop", + "Moldy Bread", + "Penters Natural", + "Beehive", + "Amanita", + "Rotten Meat", + "Spoiled Milk", + "Rusty Food Tin" +] + +good_food_table = [ + "Super Potion", + "Super Tonic", + "Tasty Meat", + "New York Steak", + "Curry", + "Ramen Noodles", + "Melon", + "Foie Gras", + "Caviar", + "Truffle", + "Peking Duck", + "Birthday Cake" +] diff --git a/worlds/cv_por/Locations.py b/worlds/cv_por/Locations.py index 3381e55582d0..ab37b434aac2 100644 --- a/worlds/cv_por/Locations.py +++ b/worlds/cv_por/Locations.py @@ -289,9 +289,9 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: if world.options.nest_of_evil_state != NestofEvil.option_removed: location_table += [ - LocationData("Nest of Evil", "Nest of Evil: Doppelganger Reward"), - LocationData("Nest of Evil", "Nest of Evil: First Item"), - LocationData("Nest of Evil", "Nest of Evil: Second Item")] + LocationData("Nest of Evil", "Nest of Evil: Doppelganger Reward"), + LocationData("Nest of Evil", "Nest of Evil: First Item"), + LocationData("Nest of Evil", "Nest of Evil: Second Item")] return location_table # LocationData("Dummy", "Dummy"), diff --git a/worlds/cv_por/Options.py b/worlds/cv_por/Options.py index b0304ed893ba..3dde9dfe331d 100644 --- a/worlds/cv_por/Options.py +++ b/worlds/cv_por/Options.py @@ -87,12 +87,12 @@ class StartWithChangeCube(Toggle): # TODO!!! Implement """If enabled, you'll start with the Change Cube by default""" display_name = "Start with Change Cube" -class AddExtraItems(Toggle): # TODO!!! Implement +class AddExtraItems(Toggle): """If enabled, some items and spells that are otherwise only obtainable in other modes will be added to the item pool, such as Puppet Master.""" display_name = "Add Extra Items" -class ExcludeOwlMorph(Toggle): # TODO!!! Implement +class ExcludeOwlMorph(Toggle): """If enabled, Owl Morph will not be added to the item pool.""" display_name = "Exclude Owl Morph" diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py index ebf710a80b7f..52ceb8741d2d 100644 --- a/worlds/cv_por/Regions.py +++ b/worlds/cv_por/Regions.py @@ -1,7 +1,7 @@ from BaseClasses import Region, Location -from typing import List, TYPE_CHECKING, Dict +from typing import TYPE_CHECKING from rule_builder.rules import HasAll, HasAny, Has, OptionFilter, CanReachLocation -from .Locations import LocationData, get_locations +from .Locations import get_locations from .Options import StartWithChangeCube if TYPE_CHECKING: from . import PoRWorld @@ -100,7 +100,6 @@ def init_areas(world: "PoRWorld") -> None: connect_regions(world) - def create_locations(world): from .static_location_data import location_ids all_locations = get_locations(world) diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py index 8d2d9cf7abf1..cc4e1add4780 100644 --- a/worlds/cv_por/__init__.py +++ b/worlds/cv_por/__init__.py @@ -9,6 +9,7 @@ from .Options import PoROptions, por_option_groups from .static_location_data import location_ids + class PoRWeb(WebWorld): theme = "ocean" @@ -25,8 +26,6 @@ class PoRWeb(WebWorld): option_groups = por_option_groups tutorials = [setup_en] -class CVPoRItem(Item): - game: str = "Castlevania: Portrait of Ruin" class PoRSettings(settings.Group): class RomFile(settings.UserFilePath): @@ -35,6 +34,7 @@ class RomFile(settings.UserFilePath): copy_to = "CASTLEVANIA2_ACBEA4_00.nds" md5 = "2edd57540cae45842fbd19c45a4214f9" + class PoRWorld(World): """The year is 1944. Dracula's castle has reappeared, and it's been taken over by the mad vampire artist, Brauner, and his two twin daughters. Explore the @@ -56,8 +56,8 @@ class PoRWorld(World): options_dataclass = PoROptions options: PoROptions - #locked_locations: List[str] - #ocation_cache: List[Location] + # locked_locations: List[str] + # ocation_cache: List[Location] def __init__(self, multiworld: MultiWorld, player: int): self.rom_name_available_event = threading.Event() @@ -65,7 +65,248 @@ def __init__(self, multiworld: MultiWorld, player: int): self.locked_locations = [] self.location_cache = [] - self.extra_item_count = 0 + self.has_tried_magus_ring = False + + self.subweapon_filler_table = [ + "Axe Subweapon", + "Cross", + "Holy Water", + "Bible", + "Javelin", + "Ricochet Rock", + "Boomerang", + "Shuriken", + "Yagyu Shuriken", + "Kunimitsu", + "Kunai", + "Crossbow", + "Dart", + "Defensive Form", + "Wrecking Ball", + "Rampage", + "Berserker", + "Time Stop", + "Heal", + "Cure Curse", + "LUCK Boost", + "Rock Riot", + "Spirit of Light", + "Dark Rift", + "Stone Circle", + "Ice Needle", + "Explosion", + "Chain Lightning", + "Nightmare", + "Summon Medusa", + "Acidic Bubbles", + "Hex", + "Salamander", + "Cocytus", + "Thor's Bellow", + "Summon Crow", + "Summon Skeleton", + "Summon Ghost", + "Summon Gunman", + "Summon Frog" + ] + + self.weapon_table = [ + "Leather Whip", + "Steel Whip", + "Rose Stem Whip", + "Blank Book", + "Knife", + "Combat Knife", + "Baselard", + "Short Sword", + "Cutlass", + "Gladius", + "Rahab's Frost", + "Agni's Flame", + "Claymore", + "Falchion", + "Great Sword", + "Zweihander", + "Spear", + "Partisan", + "Lance", + "Trident", + "Long Spear", + "Couse", + "Axe", + "Mace", + "Battle Axe", + "Morning Star", + "Bhuj", + "Brass Knuckles", + "Cestus", + "Bullet Punch" + ] + + self.good_weapon_table = [ + "Medusa Whip", + "Undead Killer", + "Tome of Arms X", + "Katar", + "Cinquedea", + "Assassin Blade", + "Heaven's Sword", + "Jagdplaute", + "Stellar Sword", + "Damascus Sword", + "Dragon Slayer", + "Final Sword", + "Holy Claymore", + "Royal Sword", + "Sarissa", + "Alucard's Spear", + "Voulge", + "Bullova", + "Golden Axe", + "Illusion Fist" + ] + + self.armor_table = [ + "Casual Clothes", + "Hobo's Clothes", + "Houppelande", + "Poncho", + "Combat Fatigues", + "Andrenaline Gear", + "Kalasiris", + "Tailcoat", + "Biker's Jacket", + "Justaucorps", + "Tuxedo Coat", + "Battle Jacket", + "Surcoat", + "Leather Cuirass", + "Copper Plate", + "Iron Plate", + "Silver Plate", + "Gold Plate", + "Holy Mail", + "Mirror Cuirass", + "Spiked Mail", + "Leather Corset", + "Jade Corset", + "Amethyst Corset", + "Emerald Corset", + "Ruby Corset", + "Sapphire Corset", + "Lilith Corset", + "Diamond Corset", + "Kirtle", + "Cotton Apron", + "Silk Negligee", + "Bathrobe", + "Frilly Camisole", + "Sequined Dress", + "Clown Shirt", + "Cocktail Dress", + "Dancer's Blouse", + "Cotehardie", + "Eye for Decay", + "Gambler Glasses", + "Sunglasses", + "Thick Glasses", + "Glasses", + "Monocle", + "Goggles", + "Uraeus", + "Royal Crown", + "Headguard", + "Iron Helmet", + "Stone Mask", + "Viking Helmet", + "Ballroom Masque", + "Fedora", + "Porkpie Hat", + "Silk Hat", + "Traveler's Hat", + "Skull Mask", + "Witch's Hat", + "Pearl Tiara", + "Diamond Tiara", + "Midnight Tiara", + "Sandals", + "Engineer Boots", + "Royal Sandals", + "Combat Boots", + "Slick Boots", + "Hiking Boots", + "Oxfords", + "Wingtips", + "Iron Leggings", + "Silver Leggings", + "Enamel Pinheels", + "Inuit Boots", + "Glass Shoes", + "Diamond Shoes" + ] + + self.good_armor_table = [ + "Feather Gear", + "Lorica", + "Platinum Plate", + "Samurai Plate", + "Berserker Mail", + "Ancient Armor", + "Scout Armor", + "Heavy Armor", + "Impervious Mail", + "Diamond Corset", + "Miko Dress", + "Europa's Dress", + "Dalmatica", + "Princess Coat", + "Wedding Dress", + "Samurai Helmet", + "Mourning Veil", + "Open Veil", + "Holy Veil", + "Arachne Hennin", + "Muse's Tiara", + "Gold Leggings", + "Platinum Lggngs", + "Succubus Boots", + "Prima Shoes", + "Silent Sandals" + ] + + self.accessory_table = [ + "Studded Choker", + "Astral Ring", + "Gold Ring", + "Cape", + "Blue Cape", + "Miser Ring", + "Sage Ring", + "Thief Ring", + "Pearl Ring", + "Tough Ring", + "Master Ring", + "Skull Ring", + "Immunity Ring", + "Blessed Ring", + "Hercules Ring", + "Hero's Cape", + "Elven Cape", + "Invisible Cape", + "Holy Mantle", + "Paludamentum", + "Blessed Ankh", + "Orchid Corsage", + "Rose Corsage", + "Sorceress Crest", + "Forget-Me-Not", + "Platinum Chain", + "Heart Brooch", + "Astral Brooch", + "Abalone Brooch", + "Moon Brooch" + + ] def generate_early(self): from .generator_main import generate_early @@ -77,4 +318,8 @@ def create_regions(self): def create_items(self): from .generator_main import create_items - create_items(self) \ No newline at end of file + create_items(self) + + def get_filler_item_name(self): + from .generator_main import get_filler_item_name + get_filler_item_name(self) diff --git a/worlds/cv_por/generator_main.py b/worlds/cv_por/generator_main.py index 3d244f874921..7af0a20deed0 100644 --- a/worlds/cv_por/generator_main.py +++ b/worlds/cv_por/generator_main.py @@ -1,6 +1,10 @@ from BaseClasses import Item -from .Locations import get_locations -from typing import List +from .Items import item_table + + +class CVPoRItem(Item): + game: str = "Castlevania: Portrait of Ruin" + def generate_early(world) -> None: from .setup_game import setup_game @@ -16,24 +20,81 @@ def create_regions(world) -> None: init_areas(world) place_static_items(world) + def create_items(world) -> None: - pool = get_item_pool(world.get_excluded_items()) - fill_pool(pool) + pool = [] + for name, data in item_table.items(): + for _ in range(data.default_count): + item = set_classifications(world, name) + pool.append(item) - world.multiworld.itempool += pool + if world.options.shuffle_whip: + pool.append(set_classifications(world, "True Vampire Killer")) + + if world.options.add_extra_items: + pool.extend([ + set_classifications(world, "Puppet Master"), + set_classifications(world, "Tori"), + set_classifications(world, "Seiryu"), + set_classifications(world, "Suzaku"), + set_classifications(world, "Byakko"), + set_classifications(world, "Gnebu"), + ]) + + if not world.options.exclude_owl_morph: + pool.append(set_classifications(world, "Owl Morph")) -def fill_pool(world, pool: List[Item]) -> None: - for _ in range(len(world.multiworld.get_unfilled_locations(world.player)) - len(pool) - world.extra_item_count): # Change to fix event count - item = self.set_classifications(world.get_filler_item_name()) + if world.options.nest_of_evil_state: + pool.extend([ + set_classifications(world, "Greatest Five"), + set_classifications(world, "Tome of Arms p1"), + set_classifications(world, "Tome of Arms p2"), + ]) + + filler_location_count = len(world.get_locations()) - len(pool) + for i in range(filler_location_count): + item = set_classifications(world, get_filler_item_name(world)) pool.append(item) + world.multiworld.itempool += pool -def get_item_pool(world) -> List[Item]: - pool: List[Item] = [] - for name, data in item_table.items(): - for _ in range(data.amount): - item = self.set_classifications(name) - pool.append(item) +def set_classifications(world, name) -> CVPoRItem: + # Make quest items be prog, here. + item_data = item_table[name] + item = CVPoRItem(name, item_data.classification, item_data.code, world.player) + return item + + +def get_filler_item_name(world) -> str: + from .Items import money_table, good_food_table, consumable_table + weights = {"subweapon": 5, "good_weapon": 7, "accessory": 8, "good_food": 10, "good_armor": 15, "money": 20, + "weapon": 30, "armor": 40, "consumable": 60} + + filler_type = world.random.choices(list(weights), weights=list(weights.values()), k=1)[0] + weight_table = { + "subweapon": world.subweapon_filler_table, + "good_weapon": world.good_weapon_table, + "weapon": world.weapon_table, + "armor": world.armor_table, + "good_armor": world.good_armor_table, + "money": money_table, + "consumable": consumable_table, + "good_food": good_food_table, + "accessory": world.accessory_table + } + + filler_item = world.random.choice(weight_table[filler_type]) + if not world.has_tried_magus_ring: + world.has_tried_magus_ring = True + if world.random.randint(0, 101) <= 10: # Magus ring should have a single 10/100 chance to be placed + filler_item = "Magus Ring" + return filler_item + + if filler_type not in ["consumable", "good_food", "money"]: + weight_table[filler_type].remove(filler_item) # Remove equipment from the corresponding table so it doesn't gen again + + if not weight_table[filler_type]: # If we have exhausted the entire pool + weights[filler_type] = 0 # Make sure it won't be rolled again - return pool \ No newline at end of file + return filler_item diff --git a/worlds/cv_por/setup_game.py b/worlds/cv_por/setup_game.py index 2515a046cb7a..0c5531d7d17b 100644 --- a/worlds/cv_por/setup_game.py +++ b/worlds/cv_por/setup_game.py @@ -11,6 +11,7 @@ def setup_game(world): "Nest of Evil": "Nest of Evil" } + def place_static_items(world): print("TODO. THIS.") - #world.get_location("Lost Village: Moat Drain Switch").place_locked_item(world.create_item("Moat Drained")) \ No newline at end of file + #world.get_location("Lost Village: Moat Drain Switch").place_locked_item(world.create_item("Moat Drained")) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index bffc018c05f9..5deab6ea1bb2 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -291,4 +291,4 @@ game_events = { "Tower of Death: Stella Item", "Master's Keep: Whip Trial" -} \ No newline at end of file +} From c9818678c20c869e38fe9763910b1b4c77ee4591 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Mon, 27 Apr 2026 19:10:10 -0500 Subject: [PATCH 32/58] Place static items --- worlds/cv_por/Items.py | 9 +++++++-- worlds/cv_por/Options.py | 4 ++-- worlds/cv_por/Regions.py | 16 ++++++++++------ worlds/cv_por/__init__.py | 10 +++++++++- worlds/cv_por/generator_main.py | 7 ++++++- worlds/cv_por/setup_game.py | 25 +++++++++++++++++++++++-- 6 files changed, 57 insertions(+), 14 deletions(-) diff --git a/worlds/cv_por/Items.py b/worlds/cv_por/Items.py index cb32bf380873..39a81254fe4b 100644 --- a/worlds/cv_por/Items.py +++ b/worlds/cv_por/Items.py @@ -395,7 +395,7 @@ class ItemData(NamedTuple): "Rocket Slash": ItemData("Subweapons", 0x0826, ItemClassification.useful, 0), "Toad Morph": ItemData("Spells", 0x0827, ItemClassification.progression), - "Owl Morph": ItemData("Spells", 0x0828, ItemClassification.progression), + "Owl Morph": ItemData("Spells", 0x0828, ItemClassification.progression, 0), "Sanctuary": ItemData("Spells", 0x0829, ItemClassification.progression), "Speed Up": ItemData("Spells", 0x082A, ItemClassification.useful), "Berserker": ItemData("Spells", 0x082B, ItemClassification.filler, 0), @@ -463,7 +463,12 @@ class ItemData(NamedTuple): "Critical Art": ItemData("Relics", 0x0868, ItemClassification.useful), "Whip Skill 1": ItemData("Relics", 0x0869, ItemClassification.useful), "Whip Skill 2": ItemData("Relics", 0x086A, ItemClassification.useful), - "Book of Spirits": ItemData("Relics", 0x086B, ItemClassification.useful) + "Book of Spirits": ItemData("Relics", 0x086B, ItemClassification.useful), + + "Portrait Clear": ItemData("Events", None, ItemClassification.progression, 0), + "Tower Elevator Active": ItemData("Events", None, ItemClassification.progression, 0), + "Dracula Defeated": ItemData("Events", None, ItemClassification.progression, 0), + "Brauner Defeated": ItemData("Events", None, ItemClassification.progression, 0), } diff --git a/worlds/cv_por/Options.py b/worlds/cv_por/Options.py index 3dde9dfe331d..3997e4801cae 100644 --- a/worlds/cv_por/Options.py +++ b/worlds/cv_por/Options.py @@ -8,7 +8,7 @@ class Goal(Choice): # TODO!!!! IMPLEMENT display_name = "Goal" option_brauner = 0 option_dracula = 1 - default = 0 + default = 1 class RevealMap(DefaultOnToggle): # TODO!!! IMPLEMENT """Start with the entire map visible.""" @@ -72,7 +72,7 @@ class ExperiencePercent(NamedRange): # TODO!!! IMPLEMENT "quadruple": 400 } -class ShuffleWhip(Toggle): # TODO!!! Implement +class ShuffleWhip(Toggle): """If enabled, the True Vampire Killer will be added to the item pool, and the Whip's Memory fight will grant you a random item. Otherwise, it will be at its normal location.""" diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py index 52ceb8741d2d..cf0e8c390b4a 100644 --- a/worlds/cv_por/Regions.py +++ b/worlds/cv_por/Regions.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING from rule_builder.rules import HasAll, HasAny, Has, OptionFilter, CanReachLocation from .Locations import get_locations -from .Options import StartWithChangeCube +from .Options import StartWithChangeCube, NestofEvil if TYPE_CHECKING: from . import PoRWorld @@ -44,6 +44,8 @@ class PoRLocation(Location): "Master's Keep - Upper Quarters", "Master's Keep - Portrait Room", + "The Throne Room", + "City of Haze", "City of Haze - East", @@ -72,9 +74,7 @@ class PoRLocation(Location): "Burnt Paradise", "Burnt Paradise - Entrance", "Burnt Paradise - Bottom", - "Nest of Evil" - ] has_change_cube = Has("Change Cube", options=[OptionFilter(StartWithChangeCube, 0)], filtered_resolution=True) @@ -88,9 +88,13 @@ class PoRLocation(Location): def init_areas(world: "PoRWorld") -> None: regions = [] + active_regions = region_list.copy() - if world.options.goal: - regions.append("Throne Room") + if not world.options.goal: + active_regions.remove("The Throne Room") + + if world.options.nest_of_evil_state == NestofEvil.option_removed: + active_regions.remove("Nest of Evil") for area in region_list: regions.append(Region(area, world.player, world.multiworld)) @@ -232,4 +236,4 @@ def connect_regions(world): if world.options.goal: # Add a connection to the throne room if necessary - world.get_region("Master's Keep - Upper Quarters").connect(world.get_region("Throne Room"), "Throne Barrier", Has("Brauner Defeated")) + world.get_region("Master's Keep - Upper Quarters").connect(world.get_region("The Throne Room"), "Throne Barrier", Has("Brauner Defeated")) diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py index cc4e1add4780..9bc81217b9ed 100644 --- a/worlds/cv_por/__init__.py +++ b/worlds/cv_por/__init__.py @@ -8,6 +8,7 @@ from .Items import item_table, get_item_names_per_category from .Options import PoROptions, por_option_groups from .static_location_data import location_ids +from .generator_main import CVPoRItem class PoRWeb(WebWorld): @@ -172,7 +173,7 @@ def __init__(self, multiworld: MultiWorld, player: int): "Houppelande", "Poncho", "Combat Fatigues", - "Andrenaline Gear", + "Adrenaline Gear", "Kalasiris", "Tailcoat", "Biker's Jacket", @@ -323,3 +324,10 @@ def create_items(self): def get_filler_item_name(self): from .generator_main import get_filler_item_name get_filler_item_name(self) + + def create_item(self, name: str) -> CVPoRItem: + from .generator_main import set_classifications + data = set_classifications(self, name) + + return CVPoRItem(name, data.classification, data.code, self.player) + diff --git a/worlds/cv_por/generator_main.py b/worlds/cv_por/generator_main.py index 7af0a20deed0..9653501240ce 100644 --- a/worlds/cv_por/generator_main.py +++ b/worlds/cv_por/generator_main.py @@ -51,7 +51,7 @@ def create_items(world) -> None: set_classifications(world, "Tome of Arms p2"), ]) - filler_location_count = len(world.get_locations()) - len(pool) + filler_location_count = len(world.multiworld.get_unfilled_locations(world.player)) - len(pool) for i in range(filler_location_count): item = set_classifications(world, get_filler_item_name(world)) pool.append(item) @@ -98,3 +98,8 @@ def get_filler_item_name(world) -> str: weights[filler_type] = 0 # Make sure it won't be rolled again return filler_item + +def create_item(world, name: str) -> CVPoRItem: + data = set_classifications(world, name) + print(name) + return CVPoRItem(name, data.classification, data.code, world.player) diff --git a/worlds/cv_por/setup_game.py b/worlds/cv_por/setup_game.py index 0c5531d7d17b..f54b632ea895 100644 --- a/worlds/cv_por/setup_game.py +++ b/worlds/cv_por/setup_game.py @@ -13,5 +13,26 @@ def setup_game(world): def place_static_items(world): - print("TODO. THIS.") - #world.get_location("Lost Village: Moat Drain Switch").place_locked_item(world.create_item("Moat Drained")) + world.get_location("Tower of Death: Elevator Switch").place_locked_item(world.create_item("Tower Elevator Active")) + world.get_location("City of Haze: Boss Room").place_locked_item(world.create_item("Portrait Clear")) + world.get_location("13th Street: Boss Room").place_locked_item(world.create_item("Portrait Clear")) + world.get_location("Sandy Grave: Boss Room").place_locked_item(world.create_item("Portrait Clear")) + world.get_location("Forgotten City: Boss Room").place_locked_item(world.create_item("Portrait Clear")) + world.get_location("Nation of Fools: Boss Room").place_locked_item(world.create_item("Portrait Clear")) + world.get_location("Burnt Paradise: Boss Room").place_locked_item(world.create_item("Portrait Clear")) + world.get_location("Forest of Doom: Boss Room").place_locked_item(world.create_item("Portrait Clear")) + world.get_location("Dark Academy: Boss Room").place_locked_item(world.create_item("Portrait Clear")) + + if world.options.goal: + world.get_location("Lost Gallery: Studio Portrait Fight").place_locked_item(world.create_item("Brauner Defeated")) + world.get_location("The Throne Room: Dracula").place_locked_item(world.create_item("Dracula Defeated")) + else: + world.get_location("Lost Gallery: Studio Portrait Fight").place_locked_item(world.create_item("Dracula Defeated")) + + if not world.options.shuffle_whip: + world.get_location("Master's Keep: Whip Trial").place_locked_item(world.create_item("True Vampire Killer")) + + if not world.options.nest_of_evil_state: + world.get_location("Nest of Evil: First Item").place_locked_item(world.create_item("Tome of Arms p1")) + world.get_location("Nest of Evil: Second Item").place_locked_item(world.create_item("Tome of Arms p2")) + world.get_location("Nest of Evil: Doppelganger Reward").place_locked_item(world.create_item("Greatest Five")) From 5d4ad9a5680e751ed6dc1525cc3e8696cdafaa3c Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Mon, 27 Apr 2026 23:44:06 -0500 Subject: [PATCH 33/58] Add logic for castle checks --- worlds/cv_por/Items.py | 2 +- worlds/cv_por/Locations.py | 2 +- worlds/cv_por/Regions.py | 8 +++---- worlds/cv_por/Rules.py | 40 +++++++++++++++++++++++++++++++++ worlds/cv_por/__init__.py | 5 ++++- worlds/cv_por/generator_main.py | 5 ++++- 6 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 worlds/cv_por/Rules.py diff --git a/worlds/cv_por/Items.py b/worlds/cv_por/Items.py index 39a81254fe4b..d7042fed6407 100644 --- a/worlds/cv_por/Items.py +++ b/worlds/cv_por/Items.py @@ -397,7 +397,7 @@ class ItemData(NamedTuple): "Toad Morph": ItemData("Spells", 0x0827, ItemClassification.progression), "Owl Morph": ItemData("Spells", 0x0828, ItemClassification.progression, 0), "Sanctuary": ItemData("Spells", 0x0829, ItemClassification.progression), - "Speed Up": ItemData("Spells", 0x082A, ItemClassification.useful), + "Speed Up": ItemData("Spells", 0x082A, ItemClassification.progression), "Berserker": ItemData("Spells", 0x082B, ItemClassification.filler, 0), "Eye for an Eye": ItemData("Spells", 0x082C, ItemClassification.useful), "Clear Skies": ItemData("Spells", 0x082D, ItemClassification.useful), diff --git a/worlds/cv_por/Locations.py b/worlds/cv_por/Locations.py index ab37b434aac2..d9a0ae0fc85f 100644 --- a/worlds/cv_por/Locations.py +++ b/worlds/cv_por/Locations.py @@ -68,7 +68,7 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Great Stairway - Staircases", "Great Stairway: Underground East Room"), LocationData("Great Stairway - Upper", "Great Stairway: Connector Pipe Left"), LocationData("Great Stairway - Upper", "Great Stairway: Connector Pipe Right"), - LocationData("Great Stairway - Upper", "Great Stairway: Upper Grand Staircase Top Left Item"), + LocationData("Great Stairway - Staircases", "Great Stairway: Upper Grand Staircase Top Left Item"), LocationData("Great Stairway - Upper", "Great Stairway: Central Upper"), LocationData("Great Stairway - Upper", "Great Stairway: Right Loft"), diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py index cf0e8c390b4a..6c8591871c8b 100644 --- a/worlds/cv_por/Regions.py +++ b/worlds/cv_por/Regions.py @@ -96,7 +96,7 @@ def init_areas(world: "PoRWorld") -> None: if world.options.nest_of_evil_state == NestofEvil.option_removed: active_regions.remove("Nest of Evil") - for area in region_list: + for area in active_regions: regions.append(Region(area, world.player, world.multiworld)) world.multiworld.regions += regions @@ -118,7 +118,7 @@ def create_locations(world): def connect_regions(world): world.get_region("Entrance - Hub").add_exits(["Entrance - Wind's Room", "Entrance - Behemoth Area", "Entrance - Hub Painting Room", "Entrance - Upper Area", "Entrance - Underground Passage"], {"Entrance - Behemoth Area": small_uppies, - "Entrance - Hub Painting Room": is_smol, + "Entrance - Hub Painting Room": is_smol | Has("Puppet Master"), "Entrance - Upper Area": HasAll("Acrobat Cube", "Call Cube", "Stone of Flight") | big_uppies, "Entrance - Underground Passage": Has("Portrait Clear", world.options.nest_portraits.value)}) @@ -145,7 +145,7 @@ def connect_regions(world): world.get_region("Great Stairway - Underground Painting").add_exits(["Great Stairway - Staircases", world.portrait_connections["Sandy Grave"]]) world.get_region("Great Stairway - Entrance Connector").add_exits(["Great Stairway - Staircases", "Entrance - Upper Area", "Great Stairway - Upper"], - {"Great Stairway - Upper": (HasAll("Call Cube", "Acrobat Cube", "Puppet Master")) | medium_uppies}) + {"Great Stairway - Upper": (HasAll("Call Cube", "Acrobat Cube", "Puppet Master")) | medium_uppies | HasAll("Call Cube", "Acrobat Cube", "Speed Up")}) world.get_region("Great Stairway - Upper").add_exits(["Great Stairway - Staircases", "Great Stairway - Entrance Connector", "Tower of Death - Belt Area", "Great Stairway - Central Painting Area"], {"Tower of Death - Belt Area": can_cast_spell & HasAny("Owl Morph", "Toad Morph"), @@ -236,4 +236,4 @@ def connect_regions(world): if world.options.goal: # Add a connection to the throne room if necessary - world.get_region("Master's Keep - Upper Quarters").connect(world.get_region("The Throne Room"), "Throne Barrier", Has("Brauner Defeated")) + world.get_region("Master's Keep - Upper Quarters").connect(world.get_region("The Throne Room"), "Throne Barrier", Has("Brauner Defeated") & Has("Portrait Clear", world.options.dracula_portraits.value)) diff --git a/worlds/cv_por/Rules.py b/worlds/cv_por/Rules.py new file mode 100644 index 000000000000..11fa009e1485 --- /dev/null +++ b/worlds/cv_por/Rules.py @@ -0,0 +1,40 @@ +from worlds.generic.Rules import set_rule, add_rule +from rule_builder.rules import HasAll, HasAny, Has, OptionFilter, CanReachLocation +from .Regions import small_uppies, big_uppies, can_cast_spell, has_change_cube, medium_uppies + +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from . import PoRWorld + +def set_location_rules(world): + set_rule = world.set_rule + world.set_completion_rule(Has("Dracula Defeated")) + + set_rule(world.get_location("Entrance: Drawbridge Upper Item"), big_uppies | HasAll("Acrobat Cube", "Call Cube", "Stone of Flight", "Puppet Master")) + set_rule(world.get_location("Entrance: Above Metal Block Room"), big_uppies | (HasAll("Strength Glove", "Push Cube", "Call Cube") & (medium_uppies) | (HasAll("Acrobat Cube", "Puppet Master"))) | (HasAll("Puppet Master", "Stone of Flight", "Acrobat Cube", "Call Cube"))) + + set_rule(world.get_location("Great Stairway: Lower Grand Staircase Lower Alcove"), small_uppies | Has("Puppet Master")) + set_rule(world.get_location("Great Stairway: Lower Grand Staircase Upper Alcove"), medium_uppies | HasAny("Speed Up", "Puppet Master")) + set_rule(world.get_location("Great Stairway: Lower Grand Staircase Middle Alcove"), medium_uppies | HasAll("Speed Up", "Puppet Master")) + set_rule(world.get_location("Great Stairway: Upper Grand Staircase Lower Alcove"), small_uppies | Has("Puppet Master")) + set_rule(world.get_location("Great Stairway: Upper Grand Staircase Middle Alcove"), medium_uppies | HasAll("Speed Up", "Puppet Master")) + set_rule(world.get_location("Great Stairway: Upper Grand Staircase Upper Alcove"), medium_uppies | HasAny("Speed Up", "Puppet Master")) + set_rule(world.get_location("Great Stairway: Upper Grand Staircase Top Left Item"), small_uppies | HasAny("Speed Up", "Puppet Master")) + set_rule(world.get_location("Great Stairway: Connector Pipe Left"), can_cast_spell & HasAny("Owl Morph", "Toad Morph")) + set_rule(world.get_location("Great Stairway: Connector Pipe Right"), can_cast_spell & HasAny("Owl Morph", "Toad Morph")) + set_rule(world.get_location("Great Stairway: Right Loft"), small_uppies) + set_rule(world.get_location("Great Stairway: Left Loft"), small_uppies) + set_rule(world.get_location("Great Stairway: Left Loft Lower"), small_uppies) + set_rule(world.get_location("Great Stairway: Central Nook"), medium_uppies) + + set_rule(world.get_location("Tower of Death: Secret Room"), medium_uppies | HasAll("Puppet Master", "Call Cube", "Acrobat Cube")) + set_rule(world.get_location("Tower of Death: Elevator Room Lower"), big_uppies) + set_rule(world.get_location("Tower of Death: Elevator Room Middle"), Has("Tower Elevator Active")) + set_rule(world.get_location("Tower of Death: Elevator Room Top"), Has("Tower Elevator Active")) + set_rule(world.get_location("Tower of Death: Above Motorcycles"), big_uppies | HasAll("Stone of Flight", "Call Cube", "Acrobat Cube")) + + + + + set_rule(world.get_location("Dummy"), big_uppies | Has("Dummy")) + \ No newline at end of file diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py index 9bc81217b9ed..9a59f7a384f0 100644 --- a/worlds/cv_por/__init__.py +++ b/worlds/cv_por/__init__.py @@ -321,6 +321,10 @@ def create_items(self): from .generator_main import create_items create_items(self) + def set_rules(self): + from .generator_main import set_rules + set_rules(self) + def get_filler_item_name(self): from .generator_main import get_filler_item_name get_filler_item_name(self) @@ -328,6 +332,5 @@ def get_filler_item_name(self): def create_item(self, name: str) -> CVPoRItem: from .generator_main import set_classifications data = set_classifications(self, name) - return CVPoRItem(name, data.classification, data.code, self.player) diff --git a/worlds/cv_por/generator_main.py b/worlds/cv_por/generator_main.py index 9653501240ce..e9690f5138d8 100644 --- a/worlds/cv_por/generator_main.py +++ b/worlds/cv_por/generator_main.py @@ -58,6 +58,10 @@ def create_items(world) -> None: world.multiworld.itempool += pool +def set_rules(world) -> None: + from .Rules import set_location_rules + set_location_rules(world) + def set_classifications(world, name) -> CVPoRItem: # Make quest items be prog, here. @@ -101,5 +105,4 @@ def get_filler_item_name(world) -> str: def create_item(world, name: str) -> CVPoRItem: data = set_classifications(world, name) - print(name) return CVPoRItem(name, data.classification, data.code, world.player) From 0a8621b900e609fb2cc556d0177dbab59f28222d Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Tue, 28 Apr 2026 01:04:04 -0500 Subject: [PATCH 34/58] City of haze rules --- worlds/cv_por/Options.py | 20 +++++++++++++++-- worlds/cv_por/Regions.py | 8 +++++-- worlds/cv_por/Rules.py | 39 +++++++++++++++++++++++---------- worlds/cv_por/__init__.py | 2 +- worlds/cv_por/generator_main.py | 2 ++ 5 files changed, 54 insertions(+), 17 deletions(-) diff --git a/worlds/cv_por/Options.py b/worlds/cv_por/Options.py index 3997e4801cae..8e37ddc75daf 100644 --- a/worlds/cv_por/Options.py +++ b/worlds/cv_por/Options.py @@ -1,6 +1,7 @@ from dataclasses import dataclass from Options import PerGameCommonOptions, Choice, DefaultOnToggle, Range, NamedRange, Toggle, ExcludeLocations, OptionGroup + class Goal(Choice): # TODO!!!! IMPLEMENT """The goal for your game. Brauner: Defeat Brauner in the studio portrait. @@ -10,14 +11,17 @@ class Goal(Choice): # TODO!!!! IMPLEMENT option_dracula = 1 default = 1 + class RevealMap(DefaultOnToggle): # TODO!!! IMPLEMENT """Start with the entire map visible.""" display_name = "Reveal Map" + class RevealBreakableWalls(Toggle): # TODO!!! IMPLEMENT """Automatically reveals all breakable walls/objects.""" display_name = "Reveal Hidden Walls" + class NestofEvil(Choice): # TODO!!! IMPLEMENT """The state of the Nest of Evil. Vanilla: Nest of Evil has its original rewards, the two tome pages and Greatest Five. @@ -31,11 +35,13 @@ class NestofEvil(Choice): # TODO!!! IMPLEMENT option_removed = 3 default = 0 + class BraunerRequired(DefaultOnToggle): # TODO!!! IMPLEMENT """If enabled, you will be required to fight Brauner before you can access the Throne Room, Of course, this won't do anything if the goal is set to Brauner.""" display_name = "Brauner Required" + class NestPortraits(Range): # TODO!!! Implement """How many Portraits are required to be cleared to access the underground passage at the castle entrance.""" @@ -44,6 +50,7 @@ class NestPortraits(Range): # TODO!!! Implement range_end = 8 default = 8 + class BraunerPortraits(Range): #TODO!!! IMPLEMENT """How many of ANY portraits are required to be cleared to access Brauner's portrait.""" display_name = "Brauner Portraits" @@ -51,6 +58,7 @@ class BraunerPortraits(Range): #TODO!!! IMPLEMENT range_end = 8 default = 4 + class DraculaPortraits(Range): # TODO!!! IMPLEMENT """How many portraits are required to be cleared to access the Throne Room.""" display_name = "Throne Portraits" @@ -58,6 +66,7 @@ class DraculaPortraits(Range): # TODO!!! IMPLEMENT range_end = 8 default = 0 + class ExperiencePercent(NamedRange): # TODO!!! IMPLEMENT """What percentage of EXP enemies give you. This is a percent of their original EXP amount.""" display_name = "Experience Percentage" @@ -72,30 +81,36 @@ class ExperiencePercent(NamedRange): # TODO!!! IMPLEMENT "quadruple": 400 } + class ShuffleWhip(Toggle): """If enabled, the True Vampire Killer will be added to the item pool, and the Whip's Memory fight will grant you a random item. Otherwise, it will be at its normal location.""" display_name = "Shuffle True Vampire Killer" + class PoRExcludeLocations(ExcludeLocations): """Prevent these locations from having an important item.""" default = frozenset({"The Throne Room: Above Throne Left", "The Throne Room: Above Throne Right", "The Throne Room: Great Stairs Left", "The Throne Room: Great Stairs Center", "The Throne Room: Great Stairs Under Stairs", "The Throne Room: Great Stairs Hidden"}) + class StartWithChangeCube(Toggle): # TODO!!! Implement """If enabled, you'll start with the Change Cube by default""" display_name = "Start with Change Cube" + class AddExtraItems(Toggle): """If enabled, some items and spells that are otherwise only obtainable in other modes will be added to the item pool, such as Puppet Master.""" display_name = "Add Extra Items" + class ExcludeOwlMorph(Toggle): """If enabled, Owl Morph will not be added to the item pool.""" display_name = "Exclude Owl Morph" + @dataclass class PoROptions(PerGameCommonOptions): goal: Goal @@ -112,7 +127,8 @@ class PoROptions(PerGameCommonOptions): start_with_change_cube: StartWithChangeCube add_extra_items: AddExtraItems exclude_owl_morph: ExcludeOwlMorph - + + por_option_groups = [ OptionGroup("Goal Options", [ Goal, @@ -145,4 +161,4 @@ class PoROptions(PerGameCommonOptions): RevealMap, RevealBreakableWalls ]) -] \ No newline at end of file +] diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py index 6c8591871c8b..be6189b60a34 100644 --- a/worlds/cv_por/Regions.py +++ b/worlds/cv_por/Regions.py @@ -1,8 +1,10 @@ from BaseClasses import Region, Location from typing import TYPE_CHECKING from rule_builder.rules import HasAll, HasAny, Has, OptionFilter, CanReachLocation +from rule_builder.field_resolvers import FromOption from .Locations import get_locations -from .Options import StartWithChangeCube, NestofEvil +from .Options import StartWithChangeCube, NestofEvil, DraculaPortraits, BraunerRequired + if TYPE_CHECKING: from . import PoRWorld @@ -236,4 +238,6 @@ def connect_regions(world): if world.options.goal: # Add a connection to the throne room if necessary - world.get_region("Master's Keep - Upper Quarters").connect(world.get_region("The Throne Room"), "Throne Barrier", Has("Brauner Defeated") & Has("Portrait Clear", world.options.dracula_portraits.value)) + world.get_region("Master's Keep - Upper Quarters").connect(world.get_region("The Throne Room"), "Throne Barrier", + Has("Portrait Clear", FromOption(DraculaPortraits)) & + Has("Brauner Defeated", options=[OptionFilter(BraunerRequired, 1)], filtered_resolution=True)) diff --git a/worlds/cv_por/Rules.py b/worlds/cv_por/Rules.py index 11fa009e1485..43e9a5356eb4 100644 --- a/worlds/cv_por/Rules.py +++ b/worlds/cv_por/Rules.py @@ -1,25 +1,28 @@ -from worlds.generic.Rules import set_rule, add_rule +from worlds.generic.Rules import add_rule from rule_builder.rules import HasAll, HasAny, Has, OptionFilter, CanReachLocation +from rule_builder.field_resolvers import FromOption from .Regions import small_uppies, big_uppies, can_cast_spell, has_change_cube, medium_uppies +from .Options import NestofEvil, BraunerPortraits from typing import TYPE_CHECKING if TYPE_CHECKING: from . import PoRWorld + def set_location_rules(world): set_rule = world.set_rule world.set_completion_rule(Has("Dracula Defeated")) set_rule(world.get_location("Entrance: Drawbridge Upper Item"), big_uppies | HasAll("Acrobat Cube", "Call Cube", "Stone of Flight", "Puppet Master")) - set_rule(world.get_location("Entrance: Above Metal Block Room"), big_uppies | (HasAll("Strength Glove", "Push Cube", "Call Cube") & (medium_uppies) | (HasAll("Acrobat Cube", "Puppet Master"))) | (HasAll("Puppet Master", "Stone of Flight", "Acrobat Cube", "Call Cube"))) + set_rule(world.get_location("Entrance: Above Metal Block Room"), big_uppies | (HasAll("Strength Glove", "Push Cube", "Call Cube") & medium_uppies) | (HasAll("Acrobat Cube", "Puppet Master")) | (HasAll("Puppet Master", "Stone of Flight", "Acrobat Cube", "Call Cube"))) set_rule(world.get_location("Great Stairway: Lower Grand Staircase Lower Alcove"), small_uppies | Has("Puppet Master")) - set_rule(world.get_location("Great Stairway: Lower Grand Staircase Upper Alcove"), medium_uppies | HasAny("Speed Up", "Puppet Master")) - set_rule(world.get_location("Great Stairway: Lower Grand Staircase Middle Alcove"), medium_uppies | HasAll("Speed Up", "Puppet Master")) + set_rule(world.get_location("Great Stairway: Lower Grand Staircase Upper Alcove"), medium_uppies | Has("Puppet Master") | (can_cast_spell & Has("Speed Up"))) + set_rule(world.get_location("Great Stairway: Lower Grand Staircase Middle Alcove"), medium_uppies | HasAll("Speed Up", "Puppet Master", "Call Cube")) set_rule(world.get_location("Great Stairway: Upper Grand Staircase Lower Alcove"), small_uppies | Has("Puppet Master")) - set_rule(world.get_location("Great Stairway: Upper Grand Staircase Middle Alcove"), medium_uppies | HasAll("Speed Up", "Puppet Master")) - set_rule(world.get_location("Great Stairway: Upper Grand Staircase Upper Alcove"), medium_uppies | HasAny("Speed Up", "Puppet Master")) - set_rule(world.get_location("Great Stairway: Upper Grand Staircase Top Left Item"), small_uppies | HasAny("Speed Up", "Puppet Master")) + set_rule(world.get_location("Great Stairway: Upper Grand Staircase Middle Alcove"), medium_uppies | HasAll("Speed Up", "Puppet Master", "Call Cube")) + set_rule(world.get_location("Great Stairway: Upper Grand Staircase Upper Alcove"), medium_uppies | Has("Puppet Master") | (can_cast_spell & Has("Speed Up"))) + set_rule(world.get_location("Great Stairway: Upper Grand Staircase Top Left Item"), small_uppies | Has("Puppet Master") | (can_cast_spell & Has("Speed Up"))) set_rule(world.get_location("Great Stairway: Connector Pipe Left"), can_cast_spell & HasAny("Owl Morph", "Toad Morph")) set_rule(world.get_location("Great Stairway: Connector Pipe Right"), can_cast_spell & HasAny("Owl Morph", "Toad Morph")) set_rule(world.get_location("Great Stairway: Right Loft"), small_uppies) @@ -32,9 +35,21 @@ def set_location_rules(world): set_rule(world.get_location("Tower of Death: Elevator Room Middle"), Has("Tower Elevator Active")) set_rule(world.get_location("Tower of Death: Elevator Room Top"), Has("Tower Elevator Active")) set_rule(world.get_location("Tower of Death: Above Motorcycles"), big_uppies | HasAll("Stone of Flight", "Call Cube", "Acrobat Cube")) - - - - set_rule(world.get_location("Dummy"), big_uppies | Has("Dummy")) - \ No newline at end of file + set_rule(world.get_location("City of Haze: Cart Secret Left"), big_uppies) + set_rule(world.get_location("City of Haze: Cart Secret Right"), big_uppies) + set_rule(world.get_location("City of Haze: Cart Secret Right"), big_uppies) + set_rule(world.get_location("City of Haze: Right Dance Hall Item"), small_uppies | Has("Puppet Master")) + set_rule(world.get_location("City of Haze: Tunnel Room Left"), (can_cast_spell & HasAny("Toad Morph", "Owl Morph")) | Has("Puppet Master")) + set_rule(world.get_location("City of Haze: Left Dance Hall Left Item"), small_uppies | Has("Puppet Master")) + set_rule(world.get_location("City of Haze: Left Dance Hall Right Item"), small_uppies | Has("Puppet Master")) + + set_rule(world.get_location("13th Street: Train Room Secret Left"), big_uppies) + set_rule(world.get_location("13th Street: Train Room Secret Right"), big_uppies) + set_rule(world.get_location("13th Street: Right Dance Hall Top Left"), small_uppies) + set_rule(world.get_location("13th Street: Right Dance Hall Top Right"), small_uppies) + set_rule(world.get_location("13th Street: Many Nyxes Room"), (medium_uppies | HasAll("Acrobat Cube", "Call Cube", "Puppet Master")) & (HasAll("Puppet Master", "Lizard Tail") | (can_cast_spell & HasAny("Toad Morph", "Owl Morph")))) + + set_rule(world.get_location("Lost Gallery: Studio Portrait Fight"), + Has("Portrait Clear", FromOption(BraunerPortraits)) & + CanReachLocation("Nest of Evil: Doppelganger Reward", options=[OptionFilter(NestofEvil, NestofEvil.option_required)], filtered_resolution=True)) diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py index 9a59f7a384f0..94b035605319 100644 --- a/worlds/cv_por/__init__.py +++ b/worlds/cv_por/__init__.py @@ -2,7 +2,7 @@ import typing import threading -from BaseClasses import Item, Tutorial, MultiWorld +from BaseClasses import Tutorial, MultiWorld from worlds.AutoWorld import World, WebWorld from .Items import item_table, get_item_names_per_category diff --git a/worlds/cv_por/generator_main.py b/worlds/cv_por/generator_main.py index e9690f5138d8..3a61a84fe45e 100644 --- a/worlds/cv_por/generator_main.py +++ b/worlds/cv_por/generator_main.py @@ -58,6 +58,7 @@ def create_items(world) -> None: world.multiworld.itempool += pool + def set_rules(world) -> None: from .Rules import set_location_rules set_location_rules(world) @@ -103,6 +104,7 @@ def get_filler_item_name(world) -> str: return filler_item + def create_item(world, name: str) -> CVPoRItem: data = set_classifications(world, name) return CVPoRItem(name, data.classification, data.code, world.player) From 7f8a0670284a832626e88107c20d2803d43d3931 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Tue, 28 Apr 2026 01:21:25 -0500 Subject: [PATCH 35/58] Add more rules --- worlds/cv_por/Rules.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/worlds/cv_por/Rules.py b/worlds/cv_por/Rules.py index 43e9a5356eb4..cebdd4b56418 100644 --- a/worlds/cv_por/Rules.py +++ b/worlds/cv_por/Rules.py @@ -50,6 +50,11 @@ def set_location_rules(world): set_rule(world.get_location("13th Street: Right Dance Hall Top Right"), small_uppies) set_rule(world.get_location("13th Street: Many Nyxes Room"), (medium_uppies | HasAll("Acrobat Cube", "Call Cube", "Puppet Master")) & (HasAll("Puppet Master", "Lizard Tail") | (can_cast_spell & HasAny("Toad Morph", "Owl Morph")))) + set_rule(world.get_location("Nation of Fools: Bottom Left Medium Square On Wall"), big_uppies | HasAll("Call Cube", "Acrobat Cube", "Stone of Flight") | HasAll("Stone of Flight", "Puppet Master")) + set_rule(world.get_location("Nation of Fools: Crevice Item"), Has("Puppet Master") | (can_cast_spell & HasAny("Toad Morph", "Owl Morph"))) + + set_rule(world.get_location("Burnt Paradise: Right Upper Big Corner On Wall"), big_uppies | HasAll("Call Cube", "Acrobat Cube", "Stone of Flight") | HasAll("Stone of Flight", "Puppet Master")) + set_rule(world.get_location("Lost Gallery: Studio Portrait Fight"), Has("Portrait Clear", FromOption(BraunerPortraits)) & CanReachLocation("Nest of Evil: Doppelganger Reward", options=[OptionFilter(NestofEvil, NestofEvil.option_required)], filtered_resolution=True)) From 4e0ef6c57098c9c3240953f147ba15df1839592a Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Tue, 28 Apr 2026 08:42:56 -0500 Subject: [PATCH 36/58] Finish building rules --- worlds/cv_por/Locations.py | 10 +++++----- worlds/cv_por/Regions.py | 6 +++++- worlds/cv_por/Rules.py | 31 +++++++++++++++++++++++++++++-- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/worlds/cv_por/Locations.py b/worlds/cv_por/Locations.py index d9a0ae0fc85f..4090b6133895 100644 --- a/worlds/cv_por/Locations.py +++ b/worlds/cv_por/Locations.py @@ -194,11 +194,11 @@ def get_locations(world: "PoRWorld") -> List[LocationData]: LocationData("Forgotten City - Inner", "Forgotten City: Lower Underground Square Lower Item"), LocationData("Forgotten City - Inner", "Forgotten City: Lower Underground Square Upper Item"), LocationData("Forgotten City - Inner", "Forgotten City: Upper Underground Square"), - LocationData("Forgotten City - Inner", "Forgotten City: Pyramid East 1F"), - LocationData("Forgotten City - Inner", "Forgotten City: Big Shaft Room Left"), - LocationData("Forgotten City - Inner", "Forgotten City: Big Shaft Room Right"), - LocationData("Forgotten City - Inner", "Forgotten City: Post-Boss Item"), - LocationData("Forgotten City - Inner", "Forgotten City: Boss Room", True), + LocationData("Forgotten City - Inner Upper", "Forgotten City: Pyramid East 1F"), + LocationData("Forgotten City - Inner Upper", "Forgotten City: Big Shaft Room Left"), + LocationData("Forgotten City - Inner Upper", "Forgotten City: Big Shaft Room Right"), + LocationData("Forgotten City - Inner Upper", "Forgotten City: Post-Boss Item"), + LocationData("Forgotten City - Inner Upper", "Forgotten City: Boss Room", True), LocationData("Nation of Fools - Right Lower", "Nation of Fools: Bottom Corner Room"), LocationData("Nation of Fools - Right Lower", "Nation of Fools: Bottom-Right Room Left Building"), diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py index be6189b60a34..6c7f77444e29 100644 --- a/worlds/cv_por/Regions.py +++ b/worlds/cv_por/Regions.py @@ -60,6 +60,7 @@ class PoRLocation(Location): "Forgotten City", "Forgotten City - Inner", + "Forgotten City - Inner Upper", "Nation of Fools", "Nation of Fools - Right Lower", @@ -203,7 +204,7 @@ def connect_regions(world): {"City of Haze - East": Has("Puppet Master") | (has_change_cube & Has("Call Cube"))}) world.get_region("Sandy Grave").add_exits(["Sandy Grave - Upper Pyramid"], - {"Sandy Grave - Upper Pyramid": small_uppies}) + {"Sandy Grave - Upper Pyramid": (small_uppies & Has("Puppet Master")) | medium_uppies}) world.get_region("Nation of Fools").add_exits(["Nation of Fools - Right Lower", "Nation of Fools - Main"], {"Nation of Fools - Right Lower": small_uppies | Has("Puppet Master"), @@ -227,6 +228,9 @@ def connect_regions(world): world.get_region("Forgotten City").add_exits(["Forgotten City - Inner"], {"Forgotten City - Inner": medium_uppies | Has("Puppet Master")}) + world.get_region("Forgotten City - Inner").add_exits(["Forgotten City - Inner Upper"], + {"Forgotten City - Inner Upper": medium_uppies | HasAll("Puppet Master", "Call Cube", "Acrobat Cube")}) + world.get_region("13th Street").add_exits(["13th Street - Main"], {"13th Street - Main": HasAll("Strength Glove", "Push Cube", "Call Cube")}) diff --git a/worlds/cv_por/Rules.py b/worlds/cv_por/Rules.py index cebdd4b56418..95ec04223dcd 100644 --- a/worlds/cv_por/Rules.py +++ b/worlds/cv_por/Rules.py @@ -1,7 +1,6 @@ -from worlds.generic.Rules import add_rule from rule_builder.rules import HasAll, HasAny, Has, OptionFilter, CanReachLocation from rule_builder.field_resolvers import FromOption -from .Regions import small_uppies, big_uppies, can_cast_spell, has_change_cube, medium_uppies +from .Regions import small_uppies, big_uppies, can_cast_spell, medium_uppies from .Options import NestofEvil, BraunerPortraits from typing import TYPE_CHECKING @@ -50,11 +49,39 @@ def set_location_rules(world): set_rule(world.get_location("13th Street: Right Dance Hall Top Right"), small_uppies) set_rule(world.get_location("13th Street: Many Nyxes Room"), (medium_uppies | HasAll("Acrobat Cube", "Call Cube", "Puppet Master")) & (HasAll("Puppet Master", "Lizard Tail") | (can_cast_spell & HasAny("Toad Morph", "Owl Morph")))) + set_rule(world.get_location("Sandy Grave: Behind Bricks"), big_uppies) + set_rule(world.get_location("Sandy Grave: Boulder Room Tunnel"), can_cast_spell & HasAny("Toad Morph", "Owl Morph")) + set_rule(world.get_location("Sandy Grave: Boulder Room Corner Alcove"), big_uppies) + set_rule(world.get_location("Sandy Grave: Lonely Mimic Alcove"), medium_uppies | HasAll("Acrobat Cube", "Call Cube", "Puppet Master")) + set_rule(world.get_location("Sandy Grave: Lower Big Underground Room Top"), medium_uppies) + set_rule(world.get_location("Sandy Grave: Upper Big Underground Top Left"), small_uppies | Has("Puppet Master")) + set_rule(world.get_location("Sandy Grave: Upper Big Underground Top Right"), small_uppies | Has("Puppet Master")) + set_rule(world.get_location("Sandy Grave: Eastmost Item"), small_uppies) + set_rule(world.get_location("Sandy Grave: Pyramid East 1F"), big_uppies | HasAll("Stone of Flight", "Acrobat Cube", "Call Cube")) + + set_rule(world.get_location("Forgotten City: Pyramid East 5F"), small_uppies | Has("Puppet Master")) + set_rule(world.get_location("Forgotten City: Pyramid 1F Bricks"), big_uppies) + set_rule(world.get_location("Forgotten City: Lower Boulder Room Upper Alcove"), big_uppies) + set_rule(world.get_location("Forgotten City: Lower Boulder Room Tunnel Alcove"), can_cast_spell & HasAny("Toad Morph", "Owl Morph")) + set_rule(world.get_location("Forgotten City: Lower Underground Square Upper Item"), medium_uppies | HasAll("Call Cube", "Acrobat Cube", "Puppet Master")) + set_rule(world.get_location("Forgotten City: Pyramid East 1F"), big_uppies |HasAll("Call Cube", "Acrobat Cube", "Stone of Flight")) + set_rule(world.get_location("Forgotten City: Pyramid East 1F"), big_uppies |HasAll("Call Cube", "Acrobat Cube", "Stone of Flight")) + set_rule(world.get_location("Forgotten City: Big Shaft Room Left"), big_uppies) + set_rule(world.get_location("Forgotten City: Big Shaft Room Right"), big_uppies) + set_rule(world.get_location("Forgotten City: Pyramid East 3F"), medium_uppies | HasAll("Call Cube", "Acrobat Cube", "Puppet Master" | (can_cast_spell & HasAll("Speed Up", "Puppet Master")))) + set_rule(world.get_location("Nation of Fools: Bottom Left Medium Square On Wall"), big_uppies | HasAll("Call Cube", "Acrobat Cube", "Stone of Flight") | HasAll("Stone of Flight", "Puppet Master")) set_rule(world.get_location("Nation of Fools: Crevice Item"), Has("Puppet Master") | (can_cast_spell & HasAny("Toad Morph", "Owl Morph"))) set_rule(world.get_location("Burnt Paradise: Right Upper Big Corner On Wall"), big_uppies | HasAll("Call Cube", "Acrobat Cube", "Stone of Flight") | HasAll("Stone of Flight", "Puppet Master")) + set_rule(world.get_location("Forest of Doom: Secret Cave Room"), big_uppies) + + + # Todo. Rules for City and Forest + + + set_rule(world.get_location("Lost Gallery: Studio Portrait Fight"), Has("Portrait Clear", FromOption(BraunerPortraits)) & CanReachLocation("Nest of Evil: Doppelganger Reward", options=[OptionFilter(NestofEvil, NestofEvil.option_required)], filtered_resolution=True)) From d6f3aa494d2b343a5c9e4d4118432da0398bf527 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Tue, 28 Apr 2026 08:44:13 -0500 Subject: [PATCH 37/58] ort --- worlds/cv_por/Rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/cv_por/Rules.py b/worlds/cv_por/Rules.py index 95ec04223dcd..b01ee7a815f7 100644 --- a/worlds/cv_por/Rules.py +++ b/worlds/cv_por/Rules.py @@ -68,7 +68,7 @@ def set_location_rules(world): set_rule(world.get_location("Forgotten City: Pyramid East 1F"), big_uppies |HasAll("Call Cube", "Acrobat Cube", "Stone of Flight")) set_rule(world.get_location("Forgotten City: Big Shaft Room Left"), big_uppies) set_rule(world.get_location("Forgotten City: Big Shaft Room Right"), big_uppies) - set_rule(world.get_location("Forgotten City: Pyramid East 3F"), medium_uppies | HasAll("Call Cube", "Acrobat Cube", "Puppet Master" | (can_cast_spell & HasAll("Speed Up", "Puppet Master")))) + set_rule(world.get_location("Forgotten City: Pyramid East 3F"), medium_uppies | HasAll("Call Cube", "Acrobat Cube", "Puppet Master") | (can_cast_spell & HasAll("Speed Up", "Puppet Master"))) set_rule(world.get_location("Nation of Fools: Bottom Left Medium Square On Wall"), big_uppies | HasAll("Call Cube", "Acrobat Cube", "Stone of Flight") | HasAll("Stone of Flight", "Puppet Master")) set_rule(world.get_location("Nation of Fools: Crevice Item"), Has("Puppet Master") | (can_cast_spell & HasAny("Toad Morph", "Owl Morph"))) From 6acb8e96c80507435c882a8218a09c6b631f4d1a Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Tue, 28 Apr 2026 21:03:34 -0500 Subject: [PATCH 38/58] add basepatch --- worlds/cv_por/Items.py | 6 +- worlds/cv_por/Regions.py | 3 +- worlds/cv_por/Rules.py | 15 ++- worlds/cv_por/generator_main.py | 3 + worlds/cv_por/src/overlay_119.bin | Bin 0 -> 1968 bytes worlds/cv_por/src/por_ap_main.asm | 169 ++++++++++++++++++++--------- worlds/cv_por/src/por_base.bsdiff4 | Bin 0 -> 5113 bytes 7 files changed, 133 insertions(+), 63 deletions(-) create mode 100644 worlds/cv_por/src/overlay_119.bin create mode 100644 worlds/cv_por/src/por_base.bsdiff4 diff --git a/worlds/cv_por/Items.py b/worlds/cv_por/Items.py index d7042fed6407..a2f68e169559 100644 --- a/worlds/cv_por/Items.py +++ b/worlds/cv_por/Items.py @@ -100,8 +100,8 @@ class ItemData(NamedTuple): # "AP Item": ItemData("Consumables", 0x024F, ItemClassification.filler, 0), # "AP Item": ItemData("Consumables", 0x0250, ItemClassification.filler, 0), These are reserved for offworld items # "Vic Viper": ItemData("Consumables", 0x0251, ItemClassification.filler, 0), - "Tome of Arms p1": ItemData("Key Items", 0x0252, ItemClassification.useful), - "Tome of Arms p2": ItemData("Key Items", 0x0253, ItemClassification.useful), + "Tome of Arms p1": ItemData("Key Items", 0x0252, ItemClassification.useful, 0), + "Tome of Arms p2": ItemData("Key Items", 0x0253, ItemClassification.useful, 0), "Spade": ItemData("Key Items", 0x0254, ItemClassification.filler, 0), "Diamond": ItemData("Key Items", 0x0255, ItemClassification.filler, 0), "Heart": ItemData("Key Items", 0x0256, ItemClassification.filler, 0), @@ -448,7 +448,7 @@ class ItemData(NamedTuple): "Dark Gate": ItemData("Dual Crashes", 0x085A, ItemClassification.useful), "Greatest Five": ItemData("Dual Crashes", 0x085B, ItemClassification.useful, 0), - "Change Cube": ItemData("Relics", 0x085C, ItemClassification.progression), + "Change Cube": ItemData("Relics", 0x085C, ItemClassification.progression, 0), "Call Cube": ItemData("Relics", 0x085D, ItemClassification.progression), "Skill Cube": ItemData("Relics", 0x085E, ItemClassification.useful), "Wait Cube": ItemData("Relics", 0x085F, ItemClassification.progression), diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py index 6c7f77444e29..5c9b2088a343 100644 --- a/worlds/cv_por/Regions.py +++ b/worlds/cv_por/Regions.py @@ -244,4 +244,5 @@ def connect_regions(world): # Add a connection to the throne room if necessary world.get_region("Master's Keep - Upper Quarters").connect(world.get_region("The Throne Room"), "Throne Barrier", Has("Portrait Clear", FromOption(DraculaPortraits)) & - Has("Brauner Defeated", options=[OptionFilter(BraunerRequired, 1)], filtered_resolution=True)) + Has("Brauner Defeated", options=[OptionFilter(BraunerRequired, 1)], filtered_resolution=True) & + CanReachLocation("Nest of Evil: Doppelganger Reward", options=[OptionFilter(NestofEvil, NestofEvil.option_required)], filtered_resolution=True)) diff --git a/worlds/cv_por/Rules.py b/worlds/cv_por/Rules.py index b01ee7a815f7..6690b4a147b6 100644 --- a/worlds/cv_por/Rules.py +++ b/worlds/cv_por/Rules.py @@ -77,11 +77,14 @@ def set_location_rules(world): set_rule(world.get_location("Forest of Doom: Secret Cave Room"), big_uppies) - - # Todo. Rules for City and Forest - - - set_rule(world.get_location("Lost Gallery: Studio Portrait Fight"), Has("Portrait Clear", FromOption(BraunerPortraits)) & - CanReachLocation("Nest of Evil: Doppelganger Reward", options=[OptionFilter(NestofEvil, NestofEvil.option_required)], filtered_resolution=True)) + CanReachLocation("Nest of Evil: Doppelganger Reward", options=[OptionFilter(NestofEvil, NestofEvil.option_required), OptionFilter(Goal, True)], filtered_resolution=True)) + + if world.options.goal: + set_rule(world.get_location("The Throne Room: Great Stairs Under Stairs"), medium_uppies | HasAll("Acrobat Cube", "Call Cube", "Puppet Master")) + set_rule(world.get_location("The Throne Room: Great Stairs Hidden"), big_uppies) + set_rule(world.get_location("The Throne Room: Above Throne Left"), big_uppies) + set_rule(world.get_location("The Throne Room: Above Throne Right"), big_uppies) + set_rule(world.get_location("The Throne Room: Great Stairs Center"), big_uppies) + set_rule(world.get_location("The Throne Room: Great Stairs Left"), big_uppies) \ No newline at end of file diff --git a/worlds/cv_por/generator_main.py b/worlds/cv_por/generator_main.py index 3a61a84fe45e..509d271ea0a8 100644 --- a/worlds/cv_por/generator_main.py +++ b/worlds/cv_por/generator_main.py @@ -44,6 +44,9 @@ def create_items(world) -> None: if not world.options.exclude_owl_morph: pool.append(set_classifications(world, "Owl Morph")) + if not world.options.start_with_change_cube: + pool.append(set_classifications(world, "Change Cube")) + if world.options.nest_of_evil_state: pool.extend([ set_classifications(world, "Greatest Five"), diff --git a/worlds/cv_por/src/overlay_119.bin b/worlds/cv_por/src/overlay_119.bin new file mode 100644 index 0000000000000000000000000000000000000000..23b90c8d9edcef9fe0020667744adc3926a69e41 GIT binary patch literal 1968 zcmZ`)Uuaup6hGgWv`KHna?`Ap#o|)`fVy_OD?W%sNCgE^wm2R7P_T%?bkkL>e;!Jg zDZWV2PRmdjk)rSg2l3eea+`ES%H)}Q1>+js$v>m#@$GPXv z_dDnNo%4OE(E*}hH$~}rDpOieFidG8Qb`je@1qF5-6QT1B|w1?g@YsnEJV@tP<^d4 zXA+gLGDe&zae0#-g=&e01pJiLjra@AbsyUqoA(Z4Zz~r0 zS>JWd)_~OoECGyB)WZ2kDP3j%HtpIv&QY9sxg%$urkRQW-e1v-0VeZ1uphNrgOICg z3y1x&gG~?fW8`et8HJtColFHlrbzo%6*BgG_ju#t6Ee~IEtQ*B=5Py7A zY~PAw1yDQsqe#dO^l2|Rn7T9%+u>Pi2D_B>LyH%<^FUdem2N+RKi?&btO0-sRh%^g+1Pk zAWou|2jN3r<8N?E&@)?eCUf}66IbmFeiEV!EJX>!h8kT~*uR(tG zb;+B(1;j+Cw8C5hS2-DHobx8fGcI%a<@TmMeOu0)fltyR{1?Ctq`GMG9qGhA&2{Yw zFCTbQkFN8@7+g#7UB{bm$8|maLZ~Bv+P3)pY0u#*aQJ*d+>LLiT}xtb(#MVXxDvH} z0iUz8ggMz~e1FmTZpTH(N-KJW&jFr4zj7>1?I`99{oe3(JT<#(ff4$;vW3U?{pQ>cI3+jk7S zwPs9=>BiVFxXk>#(zhRPL#DTkyVeuJSw!-!o;A$ol+Qok*Lx-Rq-1sjzX#m#dvK?3 zagZlKXsWAZISc{@dd-|G0Kh zwD7Z=di!I%iMu^i*L`0*H_y(89`;Z@%Rc`OAXj+Lq^mHW3+OX*HI963PT^m8Xwp49 z=I_C~?!Didr_Y$OORX}-{lG~1G4i^Yl9`0gG4~(_vbJ-wHE$>Ue34E0@Z6(+Bjq4C z+=_3$n=!nlLLI55Vnwq#->?eQs-JmYoo^^bU+28s`#)ft_idcJZ_-77bR#8-rj7kk z|GS3oSwF6Q*Eo*nhWX%kt?SQ?|Ay&jqVl6+R6crBOpcJ6Exo77-;Zf>av&yOg`Akx zdNZS}gh>WeI(%8l> zNg^#GLMjYWv?og;MWn?~pYQYhKIcC7ob#M>pL5Us=XIWI2S_5a3@)68oRs z)Zo9Kl@HSlZ|RM7^Il04y9J10HvW^pBG$ttrg2P`bhNH2<{0pZAs1xc)mn8+64FI$hSNJCSzEGVfX4_4Gl z=I|(#Se0>c^^^r{E}=wew{9K;Us^o6t;V?MVlVfbeK~8+&~1d3Mj(++3sXPx2Ei)oga8y#t;y0(fRaV4MuhvtwT>+65^_ zgWGh)z#(U$qO6=GtrgOtbJ0s@pT-?iP-Atej%4X&ri^M$n4f?s71T?(MQfHS4Rp$( zi)$a&SAKeLJT#3b2aYNLB~R?=Y3u-9UC`9qWorEe)$2PVuJE~e3Xe=eZpWgEFr|h^ z<4%;_@d!VglIOZT5me09zAmA4{mZ2$pD{6Wsz+j&edgL5d`9!jA`2XAjum|PYh3ds z;c$UIwlPv-jV4=K+K~zrys(**IVEir0CRubhaptEphfTM)|N%ajS^X6#3G%{!F|;V z;ctm!&F9Ac0Kb>x&-THjj>0&6nbs)G6{ao_&W)Q!b zIrzC2@luz^D_Hon_%<5DYM49>+5P*taTM+s_}Ouv>JJjzJ+CO}7$~>BN5~qD8jpWC z{z<>HtMqo%jBOi(G&#{do3?e}Iysn!y2iV}L8d{_5H!4&#`Z>xh9w7SyxFI&hPOPU zpM4Qx<9|XW#Qyx5+O_2IDzh$8u=$hXYlcAjP{+p zY(4zt$w}u@@jnQoK^2*W@RF(E=+dJ;55(*ZxG=4OfvCJ)2VhBJqXRROW0-i|WAr&E z*Sl1K*3NSkNtx(^JG6&5pC{3u83`p@GOorkK8i8;zw&Ord_B9mUVNjbJKJiW?bCu* z!SxvgbZb~`O>zm6w}%IJ?c!F%o=|sHo`{&TuYwL(cK_&ehM*#u=p&8cGO(E*YM|ps zaRtwTKm>Hv@za>kF*REDWfwiFExU>KE}bCP?b2}zwCxgD6iW*9)%hp$=keugQlYh< zO>a=`QogNen*tTH&KrxKKaVYLY@FKoy}36wPQF^()T^fkp@?T}z{FpXNU!miCl+C8)p?pU z+j0w7Uca;mPpyPc1X<4EiOj@QIiipRqG+m@@R6mmE@jpvA-0HsH0BNx)%ZdRmXPfn z#xioUw%IKh`C43AJ(ox)BxXq~yWD^#%@S9L1m=s>5de^N4hC(OU?vvx)i&2Aj0XWE zev`palC+au8qHonhoXg~zgH#OmV+os{7qUzlS!M9W-@`lu!a_aHhIiuveHPX&A?xv z5Aikwn-&SO6rfR?&Zhj2*hD}S;~-rFo1qmt*>h0ALKv^t!Wv8@B9b?s!bhTr(!|Y@ zG&(esa++_gNVB#kZMGzmlSag7Yy8&KHY#Ao3uoYa@);1enbG$S+bS?pb1hCd%oDdo>FB)7>;(X*EDIS7(TV4uzs8!eY5^c9>#ljtq+Ei;tP-woe5(SB|$|9mvB-(r`mIB1+^^p^k_I)y&EbZf+-XGy|F0Euz7yFrfO? zb0Ccw;|~v_T9J>tMfcWh`C1i@uDqh~5^BkX=V%2h@7K}@ z^Yn)=TYc>GVUpV>mC}D6bO_+dbat`dCSp%I5lP|+Esh=BJBVR5E6f)^L=!kHt|y7n z@khOn5{QMu)HlqUem;wCBzW$3GoxIQXUKbB2WZmUi^m0-8mPG^D^|i`!bm*BQ zlOIz+b}E_~mN_C;PP*Auv7dGC+>JA|zm|Cq^T~ZRrR~@+baNBsyqgCF4h8}##$o-> zNr%doCufB!e_nLHyydlKa^wK|2Y<+=2zV@i)Wn%eiTl?4`OZowAm_D~ zbMu8tlrQ`6t zw_UNeVdU1kbz2{!St0nPc(AI-*&_MDvC~vrR5}TRDFw@N69g?gs&sy)N|XJwJ-j=O zy*NuhCN7iBt6++3HCN}4xe}_@q|P{nixJL!V@3pTU+0tWRjT7{K?E-YL3|j8U%534 z5t~7m z?gVC>K^Tl=VZa|rkf)AfO}~iW$**VOoRWa}qxK-Xk|MtgcH{DGi7R2^D zd8Ul17E9fly}ayZJm>hL(q-+)iNpIwr-vqwq?{+}jHw}gT5tDP!-*O&r|xvGx=E?OaBl+_&7ANX(!qW|}Zj)xZ?X4wUv(m#P?iO75EFn=X!PjHlRWrtCFvS7C zpZAR)w!?7pl=Ag!nsNjEsvYatCciK1cjR$5=j{Vu) zoZURgd8I6SV|I^5@%5jV%~w9{VL6DZ+`oP!O$yoMgej>$aLAo<=az*M6RueO8Z-WF zBgC`S^W_G{1UK>ipObj*H0pX%cWc@sv^aTS4sxhBH;HCBnx3%0YFw`6*L#?0jBG47js+k)%*ZWCYc{zLU{A0Kh~3zVDnpEP?{Z=U+TIR2*P zxLA~bd&`Hpwhf7I%?0s4^R9NSWpj>ksBMV8N0-V%{0}V7qORQT*|^WXe`>(l!#;m3 z^g)FRNoROa&fxDiNK_mU*QG;)bQQSc)e79DcrCXSnAATev+Vx{aPrYpdjZk1&4D|y zaG_dHEp1`rPiYLo;rUWuWOh&7v(Ze zWdz^?RIxY9LcVOIQyK|P5?IrvE>zEiNh6Y3(CTIy%_>kK5ygU{d;0`oQmL7MwK^9B z%gLi;rWW(0=+Gjt6*MElZN<0*It+!Svy-i{$!LJs8Gxl+kYWQ7ase=IM_Pxg(6DCoq+7VmE7G0pKV(VKkp!ZUUd?~dSEfmV(m`|(7LAw6w5@v* z{Mo6~LFV>Sx%cBEd1Y(M@9WkgaK~O8FZsTW%SA#~`db(Bc@N~?QbJ-Ln4<$qFa^+8 zeJ7}EGTV-;TB;m4B6oKo055fzjF#3$&t%PU26T;$^(74gdwo@va{7pU&DdZE=M?oW z_LMk8!ywjZ?|W9GVN8Yp;73Pito=Jh_Hqxm}bP2Uwchja{G z2Sx0PKp#pa_*&|CzzufS>njzyvK+1ixqt9&^|e&mrj;rijf{Mpb^;#rA;?S|es##y z^r`!w(dO9qJG=wjB#!2PF`osa4y@R1$Eb@kz(>>G3~Y@$^zg|%j-;d|mM|uHDP~so zzQny90;b(r!PGaed6IrS?rkLb(|(2!Y1;bpSUUi4q%VPqr}nhkxI!TNb)& zip0iMIJ#6V)x!Cw$TL4jJSCfgN^KtQohE#qLmoDC4-|y)9T1}7Wl8yGF2P_ zAt`wVm)cX3CHX_LyzvE9Ysw3q7OKH*W_s0DC2VE)V^KE(aGM3c^x z0z9Iw&4O;}3*-Fw=ULW4nYpL1)$&IBd(UrOqpf;S z+Xz;dBYuS_mV~E>yg`9XlO7}x%j~EsE!SMiReastSI!T4Hx8Oo?-cR-c#}^|a1I|6 zY7JK%=bD4X2$aO7*ZBNGTr#zr>b5NkJ82g4je90{|`uh>3VKO+A4jm#dv(= zrNSW4z01dCr~I|5smQuBI{RITJmZn6RKnf2;<`E3Ind+bYkOP2wt(h;-E16ytB`fn zd(GW4e2n-y{cpOL^6QEbnyQH^W@D_~xgy;d*K-(T(VvsUS#$T)uwkL0jF0op_az$Y z@n4G0Cv}1MRYQwg5cyj5ckVX~mr0`byL6~~ak8GYaTK0n$ZhHjkmf)Rm9^^uR#N7> zA6OepPr>H}xjpW_*mi%cGtuaNw$)?*;~cA(ZLhrb%A`Mc!VdT&FN^QSTHiC`KvI0( z;^ZYJiqwJfRAb-Dz?qg8jy*RE8^_WIcmLd?SCy{=aWG38Pe|Y<)O~&@h|0rx%_oPd zXlIS0ZHDB!o*w%ChpU(!g=PgOgKh_;#LCGAdGztW^BSV8YMi;jEx(G43*rUoa_4fi zc}lA19hpt)DFHj;0-0~8uua%q>fDc-sYb)*lJzLvS)>v&3`3 z;|)`f!&mJxUe-VOw4P(;6jNjRW9n9$&u@G*QUci3z9N44^|WtJ@h_E_f%Yk7kuATH zY%?R~RQrLtx^@8Z-n2PA0JF!!UXY1DYiw;U8 zJA8i9(W86ak@$V@MW8^PvSfr{$ttl-CfFgt; jAN#*9XvQl&_s5Q1lj`p*xWp*tEZ9{_D9A Date: Tue, 28 Apr 2026 22:41:09 -0500 Subject: [PATCH 39/58] Setup a file writing system --- worlds/cv_por/Options.py | 4 +- worlds/cv_por/Rom.py | 81 +++++++++++++++++++++++++++++++++ worlds/cv_por/Rules.py | 2 +- worlds/cv_por/__init__.py | 7 +++ worlds/cv_por/generator_main.py | 31 +++++++++++++ 5 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 worlds/cv_por/Rom.py diff --git a/worlds/cv_por/Options.py b/worlds/cv_por/Options.py index 8e37ddc75daf..def9e41909bd 100644 --- a/worlds/cv_por/Options.py +++ b/worlds/cv_por/Options.py @@ -17,7 +17,7 @@ class RevealMap(DefaultOnToggle): # TODO!!! IMPLEMENT display_name = "Reveal Map" -class RevealBreakableWalls(Toggle): # TODO!!! IMPLEMENT +class RevealBreakableWalls(Toggle): """Automatically reveals all breakable walls/objects.""" display_name = "Reveal Hidden Walls" @@ -95,7 +95,7 @@ class PoRExcludeLocations(ExcludeLocations): "The Throne Room: Great Stairs Center", "The Throne Room: Great Stairs Under Stairs", "The Throne Room: Great Stairs Hidden"}) -class StartWithChangeCube(Toggle): # TODO!!! Implement +class StartWithChangeCube(Toggle): """If enabled, you'll start with the Change Cube by default""" display_name = "Start with Change Cube" diff --git a/worlds/cv_por/Rom.py b/worlds/cv_por/Rom.py new file mode 100644 index 000000000000..bf7f3100e290 --- /dev/null +++ b/worlds/cv_por/Rom.py @@ -0,0 +1,81 @@ +import typing +import struct +from worlds.Files import APProcedurePatch, APTokenMixin, APTokenTypes, APPatchExtension +from typing import Sequence, NamedTuple +from . import world_version + +hash_us = "2edd57540cae45842fbd19c45a4214f9" + +class FilePointer(NamedTuple): + rom_address: int + base_address: int + file_size: int + +file_pointers = { + "overlay_119": FilePointer(0x2CF0800, 0x02308EC0, 0x1F000), +} + + +class LocalRom(object): + + def __init__(self, file: bytes, name: str | None = None) -> None: + self.file = bytearray(file) + self.name = name + + def read_byte(self, offset: int) -> int: + return self.file[offset] + + def read_bytes(self, offset: int, length: int) -> bytes: + return self.file[offset:offset + length] + + def write_bytes(self, offset: int, values: Sequence[int]) -> None: + self.file[offset:offset + len(values)] = values + + def get_bytes(self) -> bytes: + return bytes(self.file) + + +def patch_rom(world, rom, player: int, code_patch): + rom.name = f"{world.player}_{world.auth_id}" + patch_name = bytearray(rom.name, "utf8")[:0x14] + patch_name.append(0) # Add a terminator here + rom.write_to_file(0x02309140, "overlay_119", code_patch) # Apply the basepatch's data + rom.write_to_file(0x02308F20, "overlay_119", patch_name) # Write in the player's name + + # Options handling + rom.write_to_file(0x0230916E, "overlay_119", world.options.nest_portraits.value) # Portraits for nest of evil + rom.write_to_file(0x0230916F, "overlay_119", world.options.brauner_portraits.value) # Portraits for Brauner + rom.write_to_file(0x02309170, "overlay_119", world.options.dracula_portraits.value) # Portraits for Dracula + rom.write_to_file(0x02309173, "overlay_119", world.options.reveal_hidden_walls.value) # Reveal hidden walls + rom.write_to_file(0x02309174, "overlay_119", world.options.start_with_change_cube.value) # Start with Change Cube + + rom.write_file("token_patch.bin", rom.get_token_binary()) + + +class PoRProcPatch(APProcedurePatch, APTokenMixin): + hash = hash_us + game = "Castlevania: Portrait of Ruin" + patch_file_ending = ".apcvpor" + result_file_ending = ".nds" + name: bytearray + procedure = [ + ("apply_bsdiff4", ["por_base.bsdiff4"]), + ("apply_tokens", ["token_patch.bin"]), + ("adjust_item_positions", []), + ("apply_modifiers", []) + ] + + @classmethod + def get_source_data(cls) -> bytes: + return get_base_rom_bytes() + + def write_to_file(self, offset: int, file_name: str, value: typing.Iterable[int]) -> None: + file = file_pointers[file_name] + address = offset - file.base_address + if address < 0 or address > file.file_size: + raise ValueError(f"Out of Range: Tried to write {value} at {hex(offset)} in {file_name}") + address = file.rom_address + address + self.write_token(APTokenTypes.WRITE, address, bytes(value)) + + def copy_bytes(self, source: int, amount: int, destination: int) -> None: + self.write_token(APTokenTypes.COPY, destination, (amount, source)) \ No newline at end of file diff --git a/worlds/cv_por/Rules.py b/worlds/cv_por/Rules.py index 6690b4a147b6..b1d0fc60ba09 100644 --- a/worlds/cv_por/Rules.py +++ b/worlds/cv_por/Rules.py @@ -1,7 +1,7 @@ from rule_builder.rules import HasAll, HasAny, Has, OptionFilter, CanReachLocation from rule_builder.field_resolvers import FromOption from .Regions import small_uppies, big_uppies, can_cast_spell, medium_uppies -from .Options import NestofEvil, BraunerPortraits +from .Options import NestofEvil, BraunerPortraits, Goal from typing import TYPE_CHECKING if TYPE_CHECKING: diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py index 94b035605319..f244ae6f9125 100644 --- a/worlds/cv_por/__init__.py +++ b/worlds/cv_por/__init__.py @@ -10,6 +10,8 @@ from .static_location_data import location_ids from .generator_main import CVPoRItem +world_version = "1.0" + class PoRWeb(WebWorld): theme = "ocean" @@ -67,6 +69,7 @@ def __init__(self, multiworld: MultiWorld, player: int): self.locked_locations = [] self.location_cache = [] self.has_tried_magus_ring = False + self.has_generated_output = False self.subweapon_filler_table = [ "Axe Subweapon", @@ -325,6 +328,10 @@ def set_rules(self): from .generator_main import set_rules set_rules(self) + def generate_output(self, output_directory: str) -> None: + from .generator_main import generate_output + generate_output(self, output_directory) + def get_filler_item_name(self): from .generator_main import get_filler_item_name get_filler_item_name(self) diff --git a/worlds/cv_por/generator_main.py b/worlds/cv_por/generator_main.py index 509d271ea0a8..6dfb30f2f0f7 100644 --- a/worlds/cv_por/generator_main.py +++ b/worlds/cv_por/generator_main.py @@ -1,3 +1,6 @@ +import os +import pkgutil + from BaseClasses import Item from .Items import item_table @@ -8,6 +11,16 @@ class CVPoRItem(Item): def generate_early(world) -> None: from .setup_game import setup_game + if hasattr(world.multiworld, "re_gen_passthrough"): # If UT + if "Castlevania: Portrait of Ruin" not in world.multiworld.re_gen_passthrough: + return + passthrough = world.multiworld.re_gen_passthrough["Castlevania: Portrait of Ruin"] + world.options.goal = passthrough["goal"] + world.options.brauner_portraits = passthrough["brauner_portraits"] + world.options.dracula_portraits = passthrough["dracula_portraits"] + world.options.nest_portraits = passthrough["nest_portraits"] + world.options.nest_of_evil_state = passthrough["nest_of_evil"] + world.options.brauner_required = passthrough["brauner_required"] setup_game(world) world.auth_id = world.random.getrandbits(32) @@ -74,6 +87,24 @@ def set_classifications(world, name) -> CVPoRItem: return item +def generate_output(world, output_directory: str) -> None: + from .Rom import PoRProcPatch, patch_rom + try: + code_patch = pkgutil.get_data(__name__, "src/overlay_119.bin") + patch = PoRProcPatch(player=world.player, player_name=world.multiworld.player_name[world.player]) + patch.write_file("por_base.bsdiff4", pkgutil.get_data(__name__, "src/por_base.bsdiff4")) + patch_rom(world, patch, world.player, code_patch) + + world.rom_name = patch.name + + patch.write(os.path.join(output_directory, + f"{world.multiworld.get_out_file_name_base(world.player)}{patch.patch_file_ending}")) + except Exception: + raise + finally: + world.rom_name_available_event.set() # make sure threading continues and errors are collected + + def get_filler_item_name(world) -> str: from .Items import money_table, good_food_table, consumable_table weights = {"subweapon": 5, "good_weapon": 7, "accessory": 8, "good_food": 10, "good_armor": 15, "money": 20, From 8ca8bc119f4d5c01a7d297a7e18cfe29a00582a6 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Tue, 28 Apr 2026 23:56:20 -0500 Subject: [PATCH 40/58] add options handling to ROM --- worlds/cv_por/Options.py | 10 +++---- worlds/cv_por/Regions.py | 3 --- worlds/cv_por/Rom.py | 41 ++++++++++++++++++++++++----- worlds/cv_por/src/overlay_119.bin | Bin 1968 -> 2004 bytes worlds/cv_por/src/por_ap_main.asm | 18 +++++++++++-- worlds/cv_por/src/por_base.bsdiff4 | Bin 5113 -> 5103 bytes 6 files changed, 56 insertions(+), 16 deletions(-) diff --git a/worlds/cv_por/Options.py b/worlds/cv_por/Options.py index def9e41909bd..688e9cdd520a 100644 --- a/worlds/cv_por/Options.py +++ b/worlds/cv_por/Options.py @@ -2,7 +2,7 @@ from Options import PerGameCommonOptions, Choice, DefaultOnToggle, Range, NamedRange, Toggle, ExcludeLocations, OptionGroup -class Goal(Choice): # TODO!!!! IMPLEMENT +class Goal(Choice): """The goal for your game. Brauner: Defeat Brauner in the studio portrait. Dracula: Defeat Dracula in the throne room.""" @@ -12,7 +12,7 @@ class Goal(Choice): # TODO!!!! IMPLEMENT default = 1 -class RevealMap(DefaultOnToggle): # TODO!!! IMPLEMENT +class RevealMap(DefaultOnToggle): """Start with the entire map visible.""" display_name = "Reveal Map" @@ -42,7 +42,7 @@ class BraunerRequired(DefaultOnToggle): # TODO!!! IMPLEMENT display_name = "Brauner Required" -class NestPortraits(Range): # TODO!!! Implement +class NestPortraits(Range): """How many Portraits are required to be cleared to access the underground passage at the castle entrance.""" display_name = "Passage Portraits" @@ -51,7 +51,7 @@ class NestPortraits(Range): # TODO!!! Implement default = 8 -class BraunerPortraits(Range): #TODO!!! IMPLEMENT +class BraunerPortraits(Range): """How many of ANY portraits are required to be cleared to access Brauner's portrait.""" display_name = "Brauner Portraits" range_start = 0 @@ -59,7 +59,7 @@ class BraunerPortraits(Range): #TODO!!! IMPLEMENT default = 4 -class DraculaPortraits(Range): # TODO!!! IMPLEMENT +class DraculaPortraits(Range): """How many portraits are required to be cleared to access the Throne Room.""" display_name = "Throne Portraits" range_start = 0 diff --git a/worlds/cv_por/Regions.py b/worlds/cv_por/Regions.py index 5c9b2088a343..cd8d7936ae47 100644 --- a/worlds/cv_por/Regions.py +++ b/worlds/cv_por/Regions.py @@ -96,9 +96,6 @@ def init_areas(world: "PoRWorld") -> None: if not world.options.goal: active_regions.remove("The Throne Room") - if world.options.nest_of_evil_state == NestofEvil.option_removed: - active_regions.remove("Nest of Evil") - for area in active_regions: regions.append(Region(area, world.player, world.multiworld)) diff --git a/worlds/cv_por/Rom.py b/worlds/cv_por/Rom.py index bf7f3100e290..92838973684e 100644 --- a/worlds/cv_por/Rom.py +++ b/worlds/cv_por/Rom.py @@ -3,6 +3,7 @@ from worlds.Files import APProcedurePatch, APTokenMixin, APTokenTypes, APPatchExtension from typing import Sequence, NamedTuple from . import world_version +from .Options import NestofEvil hash_us = "2edd57540cae45842fbd19c45a4214f9" @@ -12,6 +13,8 @@ class FilePointer(NamedTuple): file_size: int file_pointers = { + "arm9": FilePointer(0x4000, 0x02000000, 0xFDBB8), + "overlay_113": FilePointer(0x636600, 0x022E8820, 0x6220), "overlay_119": FilePointer(0x2CF0800, 0x02308EC0, 0x1F000), } @@ -41,13 +44,39 @@ def patch_rom(world, rom, player: int, code_patch): patch_name.append(0) # Add a terminator here rom.write_to_file(0x02309140, "overlay_119", code_patch) # Apply the basepatch's data rom.write_to_file(0x02308F20, "overlay_119", patch_name) # Write in the player's name + rom.write_to_file(0x02308F35, "overlay_119", world_version.encode("ascii")) # Write the patch version + + # Options handling # + rom.write_to_file(0x0230916E, "overlay_119", bytearray([world.options.nest_portraits.value])) # Portraits for nest of evil + rom.write_to_file(0x0230916F, "overlay_119", bytearray([world.options.brauner_portraits.value])) # Portraits for Brauner + rom.write_to_file(0x02309170, "overlay_119", bytearray([world.options.dracula_portraits.value])) # Portraits for Dracula + rom.write_to_file(0x02309172, "overlay_119", bytearray([world.options.goal.value])) # Goal mode, 1 if Dracula + rom.write_to_file(0x02309173, "overlay_119", bytearray([world.options.reveal_hidden_walls.value])) # Reveal hidden walls + rom.write_to_file(0x02309174, "overlay_119", bytearray([world.options.start_with_change_cube.value])) # Start with Change Cube + rom.write_to_file(0x02309175, "overlay_119", bytearray([world.options.reveal_map.value])) # Reveal map + + if world.options.reveal_map: + rom.write_to_file(0x0202F3B0, "arm9", bytearray([0x00, 0x00, 0xA0, 0xE1])) # Nop out the instruction that hides room borders + + goal_requirements = 0 + + if world.options.brauner_required: + goal_requirements |= 1 # Brauner Flag + + if world.options.nest_of_evil_state == NestofEvil.option_required: + goal_requirements |= 2 # Nest flag + elif world.options.nest_of_evil_state == NestofEvil.option_removed: + # Block off the Nest of Evil entrance with bricks + rom.write_to_file(0x022EB0CA, "overlay_113", struct.pack("H", 0x4027)) + rom.write_to_file(0x022EB0EA, "overlay_113", struct.pack("H", 0x4027)) + rom.write_to_file(0x022EB10A, "overlay_113", struct.pack("H", 0x4027)) + rom.write_to_file(0x022EB12A, "overlay_113", struct.pack("H", 0x4027)) + + # This is the singular Goal Requirements flag. + # Brauner will never check if brauner is required... + # If Nest of Evil is required, your goal (either brauner OR drac) will check it. + rom.write_to_file(0x02309171, "overlay_119", bytearray([goal_requirements])) - # Options handling - rom.write_to_file(0x0230916E, "overlay_119", world.options.nest_portraits.value) # Portraits for nest of evil - rom.write_to_file(0x0230916F, "overlay_119", world.options.brauner_portraits.value) # Portraits for Brauner - rom.write_to_file(0x02309170, "overlay_119", world.options.dracula_portraits.value) # Portraits for Dracula - rom.write_to_file(0x02309173, "overlay_119", world.options.reveal_hidden_walls.value) # Reveal hidden walls - rom.write_to_file(0x02309174, "overlay_119", world.options.start_with_change_cube.value) # Start with Change Cube rom.write_file("token_patch.bin", rom.get_token_binary()) diff --git a/worlds/cv_por/src/overlay_119.bin b/worlds/cv_por/src/overlay_119.bin index 23b90c8d9edcef9fe0020667744adc3926a69e41..34daf9e1102b7bce0adef3a57dc7bac17821ee66 100644 GIT binary patch delta 873 zcmY*XO=uHQ5Pt9NrW@0iY?@TDRZ~f&l@J@jKQLepf`XS?3hGU7J*1)sp@&E!B6!Fl z;Z^Vu*cB0as93azDh zrp`tbO%P(hGj4?m8SwuT2O<8|g?Lfy9Ml9Qsu_UXcIkeU8l}=E-Qm;p1}*bXbc|+r zhpy5E{+?az%|tXYKBQaMCV#XtoJu1{uFCs!7$Jca#;R!%H81$6bS6h8HF3-ttLG;k zn|QXR{RSU2NF>oTF{kR#R234bH0}*_>;TcNyKpYA8g;CxLQM}bzTrX?`sTyP^UzostP_t=9fLTx;9ufL?dGi2iPeKa>ai%@s|Q zt5vh=-$!cvwH%}Kd_z9pKhuZXmOveB3}({Xx;4d*`r;?jY|oo+(AEcfD9lWHISM&H z>^;n?RAUPHxyCF=wTSib}oqMb7^5Ql(Vu&J_iDeHWg z+H1o9Y)gg}F1T?~umkEAa-0K>%U%upuf`BV5MxaOYT#^)WF4ENyA#c_H!#AVb;Yl@ z7dv*1xY}^EwY}I0ta~w1qTCkbmc1O(2D1)0tXnC%CcZ*{(EF~#cbSKjC}sS#67PLb XN;2bch!tm(tibOmF?y3fQR069d?a*s delta 793 zcmYjP&ubGw6rR~1-DI1Z*w})Nm`D(nkT!>kiYY`8FJf7%Al~v12oWk?0!GAx(nG@J zA{2HH6{;5%gQj9&u@S2t+&~Wjdq}zrCc7<&Y_t$?eUmKI!}9ih@4fGv_swhuUj~cm z(|aQ-MaY0pF~bN=?fjPqA#V-xBHfx)DMq1|4A^Grb`)1irSEu)+`>0-fqcX>SRp;U zj>}|?J3pozQmGQt%*)q*+X}(_338F2y}5TyrAOVFc46T`i^Uy&Ut2^-=25cSWLQWu zQAnpes&(#8^(+}_<{gOd*NryosX|Wx89&&WG0d%y1|RmnjrQ%OxJrv;myd`k*R>bP zm=HN!f9%+d$#Go^Ws+S9*mzJlKrRT8xlc>3#Q|q*pli@T=>9;PgNZj#bEb_pMhoWG z4d+W@!?7~5)@Hg0X|I2hCqiVR`9Y=2y57=#`xTvh7UFn?>K)s z;3oo(+(E|MfVZH4FSY8Wy!$Te&A|KE77&`3Qv;w#Ie%!Ik<0-}+H2y5OdW013+ zS?Gb-na&TdY1tDDkXIv#tNlvP($Vl}%$h%yo?Q2G#>lSZ7hn|+W2}>T7cAAxG_F${ h=7ZjU2mZ?>DMhiLT#*vuVmZa-$U`ZP)8v_y_y_bfS2X|t diff --git a/worlds/cv_por/src/por_ap_main.asm b/worlds/cv_por/src/por_ap_main.asm index 7e4d8fba92a0..65a18503899c 100644 --- a/worlds/cv_por/src/por_ap_main.asm +++ b/worlds/cv_por/src/por_ap_main.asm @@ -276,6 +276,7 @@ ;;;;;;;;;;;;;;;;;;;;;;; .open "ftc/overlay9_119", @Overlay119Start + .org @FreeSpace .area 0x1F000 ; Maximum overlay space, failsafe if too big ;;;;;;;;;;;;;;;;; @@ -351,6 +352,12 @@ @OptionFlag_RevealHiddenWalls: ;02309174 .db 0x00 + @OptionFlag_RevealMap: ;02309175 + .db 0x00 + + @OptionFlag_EXPMult: ;02309176 + .db 0x00 + .align 4 ;;;;;;;;;;;;;;;;;;; ; Gets an arbitrary item and gives it to the player. Used for Archipelago server items @@ -585,14 +592,21 @@ ldr r0, =@OptionFlag_StartWithChangeCube ldrb r0, [r0] cmp r0, 0 - beq @@End + beq @@EndCubeCheck mov r0, 0 mov r1, 1 bl 0x02215308 ; Activate relic mov r0, 0x5C mov r1, 1 bl 0x02215B2C ; Give the change cube - +@@EndCubeCheck: + ldr r0, = @OptionFlag_RevealMap + ldrb r0,[r0] + cmp r0, 0 + beq @@End + ldr r0, =0x021119D0 + ldr r1, = 0xFFFFFFFF + str r1,[r0] ; Set all of the Map bits, revealing the full map @@End: pop lr bx lr diff --git a/worlds/cv_por/src/por_base.bsdiff4 b/worlds/cv_por/src/por_base.bsdiff4 index 96924e6b4551db486fda2b87427723801a946d0e..040aab1d9709cd5d5f3bc927b3d2fe4b9f5ce2ba 100644 GIT binary patch delta 3621 zcmV+=4%+egC+{Z_LQ_OZMn*I+o&^8^00000d$AF>1q5K_=u(r+1t5R-|NrNI&;}US zfB-hDtj!T>UWTdZG?Qw2QH05e$Z51q7>xm-0MN({449b#wFjuv(rC~IjD|*rBTP(~ znq&q*8f4LvN2v8ZCYmyOngb?=L)2(BG%2G^1vaLc8%+_WCXEbDG{hT7^)ximglIAv z0g%z4Xa}eb27oj&004gg2AVWz14pT#8VvwwXlNRqkdUXMJw}*@KpJVF8UO=NP%;`C z02%-p0BFPj00003KmY&$$j|@)WCKB_K}vuC0iXa40000000000001;J0000000000 z000000j7Zxr8Lb_JwrxA8VK}_149!{88S3_jR0iO(9mh7hp2zZ0}#z4O&S2i8f4Hh z89hMI&@l}IMvOtGfB?`jE#k5IhE8Ttqrjg)q5Gl!mH_P6+yNj^2r>h=QSTTSq#|`_ zg})Dbk*y6_M2Ag{BOv7%|6E-A`jgmVugk|!=)?Y^En<@GYY&P-lrASlH?%AZY`l~k zV5lgxeiadGa;+XRiU@7cKnk!BP;k*z z5TXjIrC6dOm`EZCZ4?yjA~hlawva#+uYe)ez!gG>D2-?+B3qy+3q%2IU;uyy^AQCT zJp@4(q(wnc+#o8iF%T3}nt}o%e$iA=P1YbF157|rETDh@pk5+?peTn01W`a#;Ov20 zzvxgP)`EYbT?M<&5;epYlq8HKgb5)SZxIy%6-2yhC=qMQ#T1c8Hw4#2s6bae1LBG% z$d&Oig(U@ICqzUy5&^l&2_zvP!a@NiIu`hC@kId@M55m)0SZe3gr+EVumgUI7Phnm ziRa2-mkCw~7ZNLN9h7Q7@V}}A;42p*xOkW;q=|pK!H{7EBuN0R&YOm+EXw zZhq48SeN@efReLHv>0LmUoph`?x1~3ah8do2?OO^-o+}1YoQVhlN7Uf^rb@kZwM-- zLHz$xO_3sP2W~(jm93IRNR@8asG!lqxx>mzty^E-rWrE0hg3$vDNh(YTlQ-}BMO0G zQF4EYl3gYU1GxloK2CvD#|8lcH|z?>^4`@jM~9(d&~&`}KL?l}2wMv$X#p>0(nlTz zFTy!%AdTc?ZHMV=@K6v%t*jWUnd3(&t1)PT1uvNMsR%^$dO~0`Q15{&bfgWGAvNgWAo*btR&3%wTqIy%&z|=>CA!F`6(egw zH4L3AvNn{Gqzh40$yEWDAVv_bc>+HyirE;biPE{_P9kSZ)>yio(PPN6cRCNt41<3q zvd=*DyET~R^qDnIHaRPa$vBhlYoC!~zQ)Y%@rHjX6G1~;K8rxvn;~9KZ-%TT6cIWO z%xf*fD=Q@sXG0fgjf;(1<1r}Wlv1!wYJ=E|0~<0ScZ&$MhaaZI2^;F+t@uNSmo`;} z4|SVjmq)NCa_2Q1`Gfg1HA!ypj1hkU)m`ZtRq7+trpnK&G2=eb&C&`X^!#1i)ZQ#| zR{=0+Ay(qeQ(*9uoH(8`8e%|!^$X|-VXhm%Ye>Q9?AVqT2sV$ZteB_4uA-b&*Yi~1 zv!83P?x!aIZml_$OGU_Z4aNZJ$U*0_&tc#$;ff~H0L>?T8d#O$3bIiO*9w0~Gd5Ov zz-r=#o4NU+ZO(H*?fhQ&7agY6mJP$(`mW-UUm$&)2ie!wLKjHe78vnt^NbikP_K$h z;Sj1$ngm}QXSP;Z+9gi(dV-X!t9bpFsoXcJUd?7~juk6((}5ylj6s2>$p!bEIEEl^ z*%;K>2?+F=a3BPIm{Z=9fgyiltzBwfVnQH7%A+XJM|pIGv&7<{cMw$Yk0I3m7g3o} zJ{kxhV_>F)8!gmW1+^`@T3C$^3vRhTSCsXc>BBI&;|K^&mA%cZP=HG==xU1!WJjZ7 z?A+>SkYPNt$--@~uxl*+`%}67PIg}Lr7BiszTMtY4w>Uj~T>WqWb1_2mNwNF#j{Y1#q#3uD1)i#NVh)hD8Qw>Z*L6LzyA%b9l zX^30B8UW0QXts z&iwiWp!(;RBE;9`ogbyHKBvlQX5-VE9_jJOL#XNhIHAsJq_LDB;6)s=l8GQym`4{C zX|aC+!|#KrBmQS&3+z6#e-Ot+LIBco4v5~SIRQzou2@-vBBWxXiBxUhqj9>_s=biP z6bdxKtFFuG>!w2T;O*Qy+y01VhH2W>S%h@jEbe$$)b#=7`c3{@%6){qvB4eKrp{XL>e>W?Rq?oZ54{ZXK z`X>h&>Yu8dr#>?>uAWDFbF&r6!>F?xx+!crdq=4@lwR4VL z5xhdDy6?aeBd71aYaRK4i{;6g{L49mc&{mc-VT zw>0$a`LB9^2*wCKf4#n32$zlBbxmWZb!>us3KlBLg84*1DfZRGID`l$jX*>W{UH14 zY%?-XD1F?E@XZx3ft2;@-*gqRb+F=(46%tU8fG?AMRbzm`%gY0UeUVx-1N{uiWsD= zXZP(Z^Fk_?G}I_EjkIzIbz&q+z2tecB)B+A0=&ds^ET}aria*6 zNY(FciV2)xe<8-I)ndG%O04#4m76R_mkckf&cDcxNk1!sr+Ih%|CuoDczffm%;)xJ z{;Rb&p`yykK_^@&Ze>3LZjmk>ac*|$9gOENOZ*K+Lj|TiMlL&rwejga?m8Jfc_XCZ zOH;q0QF<`78=g^8&Sw*^8@Y;-ol>Bg!JO+~ii*_ue_rNAM9<)`&lO*BrNu-D-8&2p zuQq*?1w_xZH>>dM=wgXMuScTZT!eGiS<>U!_3SU!CYIVj;a_bXq2ChB3VF^-BB&l|igmO^R!C9NxV5k| z;**wee{VS!=s`;xdNoeVQI2H+rMv2)=@5ieO$6>u|A}I>SDP2${%B_b~KpSI2=fMvnZII#_95~R`tq-7t zfG-T<%JHs?sAN(se-JcZ(hM$K1R#9s8bk%z)ue1QWHK|x z+@!%wC^C@1risfl)mOX*6wV^qN__4KVmR*bbTU@bETe{#DYR22tUYcO8{B^VEFA+|=_j>gVz|avs&O=GeP)dpMA|&U*W$SYgMQK`hg=GqDFm z$areFK(Z>hOB_P2?GS)cLmMluomN&8 zHFI>u&aiM}lJh$x2)-g1cFrFYw}NwVe{&_>jP=7l z%}YEJM2JgBlUNlUh0M`+A%$Qrby8^mSthC`Iw6GSBZf87AwnX7hUZ;;Rea0>go_Oc zI}B(Z^owB{QS<24u=`M=CTriw@NXVA&P>o_U5eyeTUXwaESLZ?HOL_kZ{HJpe|FVe zVyc?NAE;KzL|)<>@c!4fm?HxR4;MFYZ>GRKSoTwNl|MhGNp_Lg!hY?;kA&k8cMq&` zwK{|7+bs<0Xp@jexr1^WPSdQf0;Luc8A!`Wb+so7;~88IROf$abF*G$0btcy3QK?! rXo!Jmd|E;go3oYIUPl@&rujH_6NqO4g)KR0KlrmZW1t5R_|Nr-Y&;}US zfB-jL6Esb#T@f^zCQPZ5BTPr6JqeS_4FQlIp`bJaLFzFyXnKYaG}Ax>Mk67ipc$eM zQKLgZ(W|CO)`3o4I5Fl27&1`^)xbQ z+J@8s01r@P10kWHXnKF1pdO$epa1~S&;S`R00w{tKn+hwh)hYQO$H#-K+`}2MuSJB z14e2ATsv00w|)X^21?02%-^0002dkN^gN0000P13{)B z8UO$Q00006jQ{`u05kvsB}xsd3Fu6iPf3CrWYY@G&Fx0gGM7Epcxu84KxOT z224!`j7Eo`LlaCxLqGt~&;gN%G$kh@{Uawem1uX<=oCMcR0V)JHTJ+qs00}S%Zr<) z1}O;nEfBTu_cw27G_Z*blF&H@=*EGy|6f#H!j(@taSV(<=jE(Y9Iavir6{3oIqAHj zU|U(7WE)UaFZ6$Thvlufr)aL?V_Q>_E2;S2@1hqv#F zhJcL1lvnYQAz6x0=2VdHdJrpYSg@wbiBls|OsW91geW>%7?J|Ys7$c{EU0bByT^}; zStDnPwXsknNdR0dQvZ0MOY}ei!o&bLC@M7~DB&Wa4M=~277Xz3LvVgR>ct%u!10#F+onUBUcaw=-_}USpY+-fGUL$Q5uj?M6y6o7Kj4Z zzySaYtV9$~ZU}-ct|FkQS3p%`t|+@AVZoF)^d4;joa4~PYo2N^hyfM|xn`<$pi9{+1^>L-w(aDZc&uWh7| zf&(0=6d(kF3v){xXb`F>R5t>UlTpPaSdp324kcD0*O9b1kCziAY>9`mlm>dANzkQ+ zryPIpk`;U!IU7u>a=?H*m?MYe@E2N0D+vo9+!cxYYf4~{4#LBL;d&;1Pc%RXc(zW! zguNKj9zrGXTo|$hYIVkeRJQOF1QLy5VHB7C9l14?fYvY+&R@!#f+&;hNm!d^5{H5nO2Xba5eUE=; zBJ~PJud>TP-6;ylM{gnxTr1P-53}a);tepkwQ=?S<1Et^c*z!$5*@Peu>@j-ID+v4 zUl^#EL}#d@V;afUSXd}Z*~q&StFF|#;`&EgHR;m?-3VkI0gjpx(AI}bM;eJhB_*N+ z4}^H8wl1hB`-TlfU5{2-!Zuif@%ew-okTmR2@`-GQc&iS3LQg0!gUgxZBWV9H8NRS zP=4basdq;MGFULCcWQ#5rx%SFbBaw6kU-Hf{SgJ$cTfi%J(KaPYQ=OID+?qsp+Ne{ zpDhO#&Rvvd%8ecF1M;aEq#uINF97IJ10^L3m|*@316L>$s>%Uee)5hKf}ww4cX>%= zNY}=hip*Q36HQTTR!bYIoy(cE^0mozBW z9vND=yvnH*G$IL4#fey~+y*oM6^E>zjHUk6luGnRBV`_4FU)lxGAw)WOMt)w2!pVg5Dbswdp@1p0oR%JN4^7X-Wbd!Lzl93>mUd zC84Qia8*XVx;+(W6cef6CD`--WmmXY-Tx&wEZgHgeldjS8nc1LxQKttAP7Xv>IHDf zl7UP}n~xPYZI1zua`Yl7A|jm?kO1ZcLP6R9VnMcdSQ-IsK1*4< z*wj%#MNuxdt?MJm_qTt4_V3u>wtNR~UAwmA>bd_4^R$(B`;)K3_>Y2nf=js=$;+Ih zWnE_P=^CHB#{a>9!GFw9R!NlBXT+r(Kq%7C5HJ7)0006A00=2f24g7_gxIMPgzg)WQuJ27#tSO$?c-=xPlZgJ}k-v`kDw zViI~9X$Bz3$);)yhKvA1A%p`7k&{M@hMtfNO$M3-LI#R$Q}s5X2GRxziGYJcCW9aV zG{RseMwtwN0BsOp350*Zm`o;^CL;h$0U8-F34j0q5rCKg01_fYRK|%tQSB2=Pf6sB z3{4p_G}BF~pv1|eBR~dC15E=aATbP#N370d>`%f<4!rTCS|WpczjAYh>-l?L9)eM|TbAAB7_9AAPpxV z=#A=v%qccDD+@4U$Xgv1k;yd4v?gRqP=z7GfaM4UA~16`UgK%7GjgR}@AI&`3`<%h zA?ZsKwD!}Lr~^&6i{D=Wz_hc3;K`p5N{J;+C>k`wZ6I)lGFw3?RunKitt%CnQfW$` zV^!o^xLZLLA0&T<4{;f8&4_&za^zz<2o$f;I5^K${?)W|<1SfXQwn*M@H^=Q8fNuK zEhQlWR0Mg5;LI0qaL3N=EI<_`2YA*CoO0<8!3w`Frf5ixrseZk?LaGcEa7fM>tR&W z892E6*JI4&dU7+g^fkX%vW$K(?O}VM{WaU3jrR>6PeXshLC$_|JA_nDhAI{b-q1aK>o=;s?&+rD&u%zrUr1ljn7G)+mWm{IIXR1LM zlF6oHWi(f5D*uV+#3{6IzPIJef+sS9C8+fdO$=yWh!`0-&eVk?TLVaCbEsXEYhq;h zPJ8OqqNRUK;}FC+QG}xcG_Fj}7F5d8{kN?2(^Vq@b9JRoQK<2&6rg`Z==;=H3$kOP zMaN#}GVJOZbTd_ArfQ%{R2VHPEHEgx6#Sy29V{{!3k!=ZhQ^_d3;7GAu-Z!`3qtFE?Q zO)N*Bf7OO8eo4L6?v+fz9@v~GcJ7=orKzKM5-*SKPY z8>0PAxjjumTM2^sKG%(F4(iVd_oT2%zuCI6O!rnxxiUhXZTw{up=^Z#;2Hxsfy8(; zqey>C9JaAhq#RpQRrQzAS+LMJOTC()$&lR6H!Mo#c&jt_PU^H|2Qo%_@!v6&m@I=! z#Y(*-4f6M#X4G1FA=e*|sb<%Tl8;Rq>_Ly-o2Z1`Td^0ou*{I{^o6dXh-A;>#Zidr?GD9Yf)@szBd7$Y5e z968o$Jdy#$B~q3p8Jhln(v9lB9wusMXH@3xw809ltugSLmx03sU#SM3wiuKot-Zmn z(xVF+VNt*#Sb~ktxq=eUB}BO)R0Dq_%#llJI>aUm4)$CGCnimWF_U9c&BdF!!Pdfh zHNlTU&0TzgLfyIESt_Fq?7|6VT^XGSJSIcGHG&0&TH!1a3fr0?0HlUCTc={^w%Sg{ zx0tZjiO@Xz7AmNeCXB;Pu)gVRD5W~-K_Wpl_6OV5o zX=H-~ykLcM5D#Nk$)NU-u5UuBP=sW*{F*kEcczIDmXRlr6&;sOXtE)NU@moHXyN51 zvL-qqgytiLHPRtMF_2N=>Zbg^%7BjgJ01OHEDodTG?rx8uI0^a_ehG}t;Fm4kvUIt zsyO-Oy182YF>x6R06M$%2%Uf5=Ub7U{2X%3yX6}J7W{!njBdNNM=?R*G&s2W*}1wZ z1CqTm+cT!5@Bd(hfHJuum2vcPrE$i+bLAM<`a{8*YYKx(?9RZ(DYH&A_FkI+l_5s) zH5h3R6=^ryfMw?nGC$Nh`0#+QYQ}%fbJPecF! From ec651814d19788f230c5fa5fa65f08c74d1e0109 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 29 Apr 2026 00:37:50 -0500 Subject: [PATCH 41/58] start logging check data --- worlds/cv_por/Rom.py | 11 +- worlds/cv_por/static_location_data.py | 264 +++++++++++++++++++++++++- 2 files changed, 269 insertions(+), 6 deletions(-) diff --git a/worlds/cv_por/Rom.py b/worlds/cv_por/Rom.py index 92838973684e..4e26f2a52a73 100644 --- a/worlds/cv_por/Rom.py +++ b/worlds/cv_por/Rom.py @@ -3,6 +3,7 @@ from worlds.Files import APProcedurePatch, APTokenMixin, APTokenTypes, APPatchExtension from typing import Sequence, NamedTuple from . import world_version +from .static_location_data import location_data_table from .Options import NestofEvil hash_us = "2edd57540cae45842fbd19c45a4214f9" @@ -46,7 +47,7 @@ def patch_rom(world, rom, player: int, code_patch): rom.write_to_file(0x02308F20, "overlay_119", patch_name) # Write in the player's name rom.write_to_file(0x02308F35, "overlay_119", world_version.encode("ascii")) # Write the patch version - # Options handling # + # Options handling ################### rom.write_to_file(0x0230916E, "overlay_119", bytearray([world.options.nest_portraits.value])) # Portraits for nest of evil rom.write_to_file(0x0230916F, "overlay_119", bytearray([world.options.brauner_portraits.value])) # Portraits for Brauner rom.write_to_file(0x02309170, "overlay_119", bytearray([world.options.dracula_portraits.value])) # Portraits for Dracula @@ -76,8 +77,14 @@ def patch_rom(world, rom, player: int, code_patch): # Brauner will never check if brauner is required... # If Nest of Evil is required, your goal (either brauner OR drac) will check it. rom.write_to_file(0x02309171, "overlay_119", bytearray([goal_requirements])) + #################################### + for location in world.get_locations(): + if not location.address: # Filter all events out of this + continue + + data = location_data_table[location.name] + - rom.write_file("token_patch.bin", rom.get_token_binary()) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index 5deab6ea1bb2..a14e9520258a 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -1,7 +1,14 @@ +from typing import NamedTuple # IDs are matched to which internal Flag number the location uses # Because of this, they are asynchronous and out of order from what is expected # They are listed here in numerical order for clarity +class LocationInformation(NamedTuple): # In-game information used to define each Location in the ROM. + pointer: int + file: str + is_grounded: bool = False + location_type: str = "Normal" + location_ids = { "Entrance: Drawbridge Pit Item": 0x01, "Entrance: Drawbridge Switch Item": 0x02, @@ -66,7 +73,7 @@ "Nation of Fools: Bottom-Left Room Building": 0x48, "Sandy Grave: Pyramid 3F Lower": 0x49, "Tower of Death: Rampart Room": 0x4A, - "Entrance: Beyond Great Armor Hallway": 0x4B, + "Entrance: Beyond Great Armor Hallway": LocationInformation(0x02305020, "overlay_79"), "Tower of Death: Elevator Room Lower": 0x4C, "Forest of Doom: Right Atrium East Square Room Right": 0x4D, "Forgotten City: Outside Left": 0x4E, @@ -288,7 +295,256 @@ "Quest: Kill Gergoth": 0x0224 } -game_events = { - "Tower of Death: Stella Item", - "Master's Keep: Whip Trial" +location_data_table = { + "Entrance: Drawbridge Pit Item": LocationInformation(0x022F9110, "overlay_78"), + "Entrance: Drawbridge Switch Item": LocationInformation(0x022F911C, "overlay_78"), + "City of Haze: Church Hallway": LocationInformation(0x022F6E7C, "overlay_94"), + "Entrance: Statue Room Above Statue": LocationInformation(0x02304E64, "overlay_79"), + "City of Haze: Post-Boss Room": LocationInformation(0x02302A18, "overlay_95"), + + "City of Haze: Heavy Cart Item": 0x06, + "Sandy Grave: Pyramid 5F Nook": 0x07, + "Great Stairway: After Keremet Lower": 0x09, + "Forgotten City: Pyramid 1F Bricks": 0x0A, + "Nation of Fools: Central Top Shaft": 0x0D, + "Entrance: Above Hub": LocationInformation(0x02304A8C, "overlay_79"), + "Entrance: Double Room Upper": LocationInformation(0x02309140, "overlay_119"), + "Nation of Fools: Legion Arena": 0x10, + "Forest of Doom: Post-Boss Room": 0x11, + "Dark Academy: Right Building Center Room": 0x12, + "The Throne Room: Above Throne Left": 0x13, + "Sandy Grave: Boulder Room Tunnel": 0x14, + "City of Haze: Before Cart Room": 0x16, + "City of Haze: Underground Square Right": 0x17, + "Sandy Grave: Upper Big Underground Top Right": 0x18, + "Nation of Fools: Bottom Corner Room": 0x19, + "Nation of Fools: Mid-Right Corner Room": 0x1A, + "Sandy Grave: Lower Big Underground Room Bottom": 0x1B, + "Sandy Grave: Left Trap": 0x1C, + "Buried Chamber: Central Secret": 0x20, + "Sandy Grave: Sandworm Stomach": 0x21, + "Tower of Death: Second Gears Room Left": 0x22, + "Great Stairway: Lower Grand Staircase Corner Nook": 0x23, + "Master's Keep: Entrance": 0x24, + "Forest of Doom: Left Atrium Big Room": 0x25, + "Forgotten City: Big Shaft Room Left": 0x26, + "Nation of Fools: Left Upper Corner Room": 0x27, + "Dark Academy: Main Building Ghoul Room": 0x28, + "13th Street: Triple Moth Room": 0x29, + "Tower of Death: Secret Room": 0x2A, + "Nation of Fools: Right Horizontal Room": 0x2B, + "Burnt Paradise: Left Lower Isolated Room": 0x2C, + "Burnt Paradise: Right Lower Isolated Room": 0x2D, + "The Throne Room: Great Stairs Under Stairs": 0x2E, + "13th Street: Behind Final Guard": 0x2F, + "Buried Chamber: Right Secret": 0x30, + "Sandy Grave: Bottom Right Room": 0x31, + "Tower of Death: Passage Hidden Item": 0x32, + "Great Stairway: Left Loft Lower": 0x33, + "Entrance: Center Hidden Room": LocationInformation(0x02304F0C, "overlay_79"), + "Forgotten City: Big Shaft Room Right": 0x36, + "Nation of Fools: Right Bottom Corner Room": 0x37, + "Master's Keep: Hidden Room": 0x38, + "13th Street: Three Amalaric Snipers": 0x39, + "Dark Academy: Main Building Three Toads": 0x3A, + "Nation of Fools: Top Corner Room": 0x3B, + "Burnt Paradise: Left Upper Isolated Room": 0x3C, + "Burnt Paradise: Right Upper Isolated Room": 0x3D, + "The Throne Room: Great Stairs Center": 0x3E, + "Forgotten City: Upper Underground Square": 0x3F, + "Entrance: Double Room Lower": LocationInformation(0x0230914C, "overlay_119"), + "Buried Chamber: Westmost Room": 0x41, + "Sandy Grave: Pyramid East 1F": 0x42, + "City of Haze: Register Room": 0x46, + "City of Haze: Bar Left": 0x47, + "Nation of Fools: Bottom-Left Room Building": 0x48, + "Sandy Grave: Pyramid 3F Lower": 0x49, + "Tower of Death: Rampart Room": 0x4A, + "Entrance: Beyond Great Armor Hallway": 0x4B, + "Tower of Death: Elevator Room Lower": 0x4C, + "Forest of Doom: Right Atrium East Square Room Right": 0x4D, + "Forgotten City: Outside Left": 0x4E, + "Sandy Grave: Upper Big Underground Top Left": 0x4F, + "Burnt Paradise: Left Upper Big Corner": 0x51, + "Great Stairway: Upper Grand Staircase Top Left Item": 0x53, + "City of Haze: Axe Courtyard": 0x54, + "Tower of Death: Top of the Tower": 0x55, + "Great Stairway: Upper Underground Room": 0x56, + "Entrance: Drawbridge Upper Item": LocationInformation(0x022F9128, "overlay_78"), + "Entrance: Statue Room Upper": LocationInformation(0x02304E70, "overlay_79"), + "Tower of Death: Second Gears Room Right": 0x59, + "Tower of Death: Lower Gears Room Spike Shaft": 0x5A, + "Master's Keep: Right Square Room": 0x5D, + "City of Haze: Central Hallway Stairs": 0x60, + "Buried Chamber: Right Shade Hall": 0x61, + "City of Haze: Right Corner Room": 0x64, + "City of Haze: Left Plaza": 0x65, + "Great Stairway: Underground Hidden Room": 0x66, + "Sandy Grave: Lower Big Underground Room Top": 0x67, + "Nation of Fools: Upper Upside Down": 0x68, + "Forgotten City: Lower Boulder Room Tunnel Alcove": 0x69, + "Dark Academy: Main Building Secret Ceiling": 0x6B, + "Forest of Doom: Secret Cave Room": 0x6C, + "Nest of Evil: Doppelganger Reward": 0x6D, + "City of Haze: Center Room Lower": 0x6E, + "Sandy Grave: On Statue": 0x6F, + "Tower of Death: Third Gears Room": 0x70, + "Entrance: Above Metal Block Room": LocationInformation(0x02304F3C, "overlay_79"), + "Dark Academy: Main Building Entrance": 0x72, + "Burnt Paradise: Bottom Corner": 0x73, + "Forest of Doom: Left Atrium Top": 0x75, + "Great Stairway: Before Keremet Room": 0x76, + "Forgotten City: Lower Boulder Room Upper Alcove": 0x77, + "Entrance: First Passage Upper Item": LocationInformation(0x02304AEC, "overlay_79"), + "Great Stairway: Upper Grand Staircase Corner Nook": 0x79, + "Burnt Paradise: Bottom Right Big Room": 0x7A, + "Entrance: Right Hidden Room": LocationInformation(0x022F92C0, "overlay_78"), + "Entrance: First Passage Lower Item": LocationInformation(0x02304AE0, "overlay_79"), + "City of Haze: Left Dance Hall Right Item": 0x7E, + "City of Haze: Tunnel Room Left": 0x7F, + "Burnt Paradise: Upper Left Big Room Middle Building": 0x82, + "City of Haze: Chunky Room": 0x84, + "Entrance: Pre-Behemoth Boss Room": LocationInformation(0x022F91E8, "overlay_78"), + "Great Stairway: After Keremet Upper": 0x89, + "Tower of Death: Gear Belt Room": 0x8A, + "City of Haze: Dance Halls Basement": 0x8B, + "City of Haze: Central Hallway Lower": 0x8C, + "Burnt Paradise: Upper Right Big Room Right Side": 0x8F, + "Buried Chamber: Central Hall": 0x92, + "Great Stairway: Central Nook": 0x93, + "Sandy Grave: Eastmost Item": 0x94, + "Sandy Grave: Behind Bricks": 0x95, + "City of Haze: Underground Square Left": 0xA0, + "Great Stairway: Upper Grand Staircase Upper Alcove": 0xA1, + "City of Haze: Right Dance Hall Secret Room": 0xA7, + "Nation of Fools: Bottom Left Medium Square On Wall": 0xA8, + "City of Haze: First Big Room": 0xA9, + "Buried Chamber: Final Shaft Item": 0xAA, + "Great Stairway: Central Upper": 0xAB, + "Forest of Doom: Right Atrium East Top Room": 0xAC, + "Dark Academy: Right Building Top Floor": 0xAD, + "Sandy Grave: Boulder Room Corner Alcove": 0xAE, + "Forgotten City: Post-Boss Item": 0xAF, + "City of Haze: Cart Secret Left": 0xB0, + "City of Haze: Winding Rooms": 0xB3, + "Great Stairway: Lower Grand Staircase Lower Alcove": 0xB4, + "Forest of Doom: Right Atrium East Square Room Left": 0xB5, + "Forgotten City: Bottom Chamber": 0xB9, + "City of Haze: Meat Room": 0xBB, + "Great Stairway: Big Underground Room Hidden Item": 0xBF, + "Great Stairway: Right Loft": 0xC0, + "Great Stairway: Left Loft": 0xC1, + "Nation of Fools: Bottom-Right Room Left Building": 0xC2, + "13th Street: Many Nyxes Room": 0xC4, + "The Throne Room: Above Throne Right": 0xC5, + "City of Haze: Tunnel Room Right": 0xC8, + "Great Stairway: Lower Grand Staircase Upper Alcove": 0xCA, + "Great Stairway: Underground East Room": 0xCB, + "Tower of Death: Elevator Room Top": 0xCC, + "Entrance: Hub Room": LocationInformation(0x02304AB0, "overlay_79"), + "Buried Chamber: Eastmost Room": 0xCE, + "Tower of Death: Hidden Room": 0xD1, + "Nation of Fools: Bottom-Right Room Middle Building": 0xD4, + "Forgotten City: Pyramid 5F": 0xD5, + "13th Street: Train Room Secret Right": 0xD6, + "City of Haze: Left Dance Hall Left Item": 0xD8, + "Great Stairway: Big Underground Room Lower": 0xDA, + "Tower of Death: Elevator Room Middle": 0xDB, + "Nation of Fools: Crevice Item": 0xDC, + "City of Haze: First Passage": 0xDD, + "Forgotten City: Gorgon Room": 0xDE, + "City of Haze: Axe Armor Item": 0xE0, + "Sandy Grave: Giant Shaft Upper Item": 0xE1, + "City of Haze: Dance Halls Vertical Hallway": 0xE2, + "Tower of Death: Above Motorcycles": 0xE3, + "Nation of Fools: Top-Right Room Axe Item": 0xE4, + "Dark Academy: Main Building Secret Malphas Room": 0xE5, + "13th Street: Underground Secret Room": 0xE6, + "Great Stairway: Big Underground Room Upper": 0xE7, + "Forest of Doom: Right Atrium West Top Room": 0xE8, + "Burnt Paradise: Upper Left Big Room Left Building": 0xE9, + "13th Street: Underground Demon Room": 0xEA, + "Sandy Grave: Mega Ghost Room Lower": 0xEB, + "13th Street: Left Big Room": 0xEC, + "City of Haze: Bread Shelf": 0xED, + "Burnt Paradise: Upper Left Lower Corner Upright Building": 0xEE, + "City of Haze: Underground Secret Room": 0xEF, + "Sandy Grave: Mega Ghost Room Upper": 0xF0, + "Burnt Paradise: Lower Left Big Room": 0xF1, + "13th Street: Secret Floor Room": 0xF3, + "City of Haze: Right Dance Hall Item": 0xF7, + "Tower of Death: Top Gears Room": 0xF9, + "13th Street: East Courtyard": 0xFA, + "City of Haze: Corner Room": 0xFC, + "Sandy Grave: Giant Shaft Middle Item": 0xFD, + "Entrance: Lilith Room": LocationInformation(0x02304E28, "overlay_79"), + "Sandy Grave: Right Trap": 0x103, + "Nation of Fools: Left Bottom Corner Room": 0x104, + "Forgotten City: Lower Underground Square Upper Item": 0x105, + "City of Haze: Center Room Top": 0x10E, + "Great Stairway: Upper Grand Staircase Middle Alcove": 0x10F, + "Buried Chamber: Right Shaft Top": 0x110, + "Sandy Grave: Pyramid 3F Upper": 0x113, + "Nation of Fools: Bottom Right Medium Square On Wall": 0x114, + "Sandy Grave: Lonely Mimic Alcove": 0x116, + "13th Street: Near Big Room": 0x118, + "Forgotten City: Pyramid East 3F": 0x119, + "Great Stairway: Upper Grand Staircase Lower Alcove": 0x11B, + "Nation of Fools: Right Vertical Room": 0x11C, + "Sandy Grave: Fleaman Pit": 0x11E, + "Nation of Fools: Top-Right Room Bottom Item": 0x11F, + "Forgotten City: Pyramid East 5F": 0x122, + "Burnt Paradise: Lower Vertical Hall": 0x123, + "Buried Chamber: West Shaft": 0x124, + "Great Stairway: Lower Grand Staircase Middle Alcove": 0x127, + "Entrance: Behemoth Chase Room": LocationInformation(0x02304DA4, "overlay_79"), + "Entrance: First Passage Hidden Wall": LocationInformation(0x02304B34, "overlay_79"), + "Tower of Death: Lower Passage Hidden Item": 0x131, + "Dark Academy: West Building Breakable Wall": 0x134, + "Forgotten City: Armored Fleaman Pit": 0x137, + "Nation of Fools: Top-Left Room Right Building": 0x13F, + "Forest of Doom: Left Atrium Breakable Wall": 0x140, + "Sandy Grave: Giant Shaft Low Item": 0x144, + "The Throne Room: Great Stairs Hidden": 0x145, + "Nation of Fools: Top-Right Room Breakable Wall": 0x146, + "Burnt Paradise: Bottom Right Breakable Wall": 0x147, + "Burnt Paradise: Right Upper Big Corner On Wall": 0x148, + "Nation of Fools: Right Center Room": 0x149, + "Burnt Paradise: Left Big Corner": 0x14A, + "Buried Chamber: Hidden Wall Item": 0x014C, + "13th Street: Bakery Middle": 0x14D, + "City of Haze: Cart Secret Right": 0x14E, + "13th Street: Right Dance Hall Top Right": 0x150, + "13th Street: Bakery Left": 0x152, + "13th Street: Train Room Secret Left": 0x153, + "City of Haze: Bar Right": 0x154, + "13th Street: Square Room": 0x155, + "13th Street: Bakery Right": 0x156, + "Burnt Paradise: Bottom Right Corner Room": 0x157, + "Nation of Fools: Left Center Room": 0x15F, + "Dark Academy: Main Building Square Room Breakable Wall": 0x160, + "Nation of Fools: Upside Down Building": 0x161, + "Dark Academy: Main Building Minotaur Room": 0x163, + "Dark Academy: West Building Top Floor Right": 0x164, + "Nation of Fools: Secret Room": 0x166, + "Forest of Doom: Left Atrium Middle Room": 0x167, + "Burnt Paradise: Upper Right Big Room": 0x16B, + "Forest of Doom: Near Cave Save Breakable Wall": 0x16C, + "Sandy Grave: Pyramid East 5F": 0x173, + "Nest of Evil: First Item": 0x175, + "Nest of Evil: Second Item": 0x176, + "Forest of Doom: Left Atrium Upper Room": 0x177, + "Great Stairway: Connector Pipe Left": 0x180, + "Great Stairway: Connector Pipe Right": 0x181, + "The Throne Room: Great Stairs Left": 0x182, + "13th Street: Right Dance Hall Top Left": 0x183, + "Dark Academy: West Building Top Floor Left": 0x184, + "Forgotten City: Pyramid East 1F": 0x185, + "Forgotten City: Lower Underground Square Lower Item": 0x186, + "Tower of Death: Stella Item": 0x187, + "Master's Keep: Whip Trial": 0x188, + + "Quest: Preparations": 0x0200 } + +"Dummy": LocationInformation(0x022F9110, "overlay_78"), \ No newline at end of file From 9314a92d9a6d82ad6e512065322d7b655a7ee79d Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 29 Apr 2026 00:56:48 -0500 Subject: [PATCH 42/58] More loc checks --- worlds/cv_por/static_location_data.py | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index a14e9520258a..10857a3a6d7e 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -73,7 +73,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Nation of Fools: Bottom-Left Room Building": 0x48, "Sandy Grave: Pyramid 3F Lower": 0x49, "Tower of Death: Rampart Room": 0x4A, - "Entrance: Beyond Great Armor Hallway": LocationInformation(0x02305020, "overlay_79"), + "Entrance: Beyond Great Armor Hallway": 0x4B, "Tower of Death: Elevator Room Lower": 0x4C, "Forest of Doom: Right Atrium East Square Room Right": 0x4D, "Forgotten City: Outside Left": 0x4E, @@ -321,7 +321,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Nation of Fools: Mid-Right Corner Room": 0x1A, "Sandy Grave: Lower Big Underground Room Bottom": 0x1B, "Sandy Grave: Left Trap": 0x1C, - "Buried Chamber: Central Secret": 0x20, + "Buried Chamber: Central Secret": LocationInformation(0x022FEA00, "overlay_80"), "Sandy Grave: Sandworm Stomach": 0x21, "Tower of Death: Second Gears Room Left": 0x22, "Great Stairway: Lower Grand Staircase Corner Nook": 0x23, @@ -337,7 +337,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Burnt Paradise: Right Lower Isolated Room": 0x2D, "The Throne Room: Great Stairs Under Stairs": 0x2E, "13th Street: Behind Final Guard": 0x2F, - "Buried Chamber: Right Secret": 0x30, + "Buried Chamber: Right Secret": LocationInformation(0x022FEC34, "overlay_80"), "Sandy Grave: Bottom Right Room": 0x31, "Tower of Death: Passage Hidden Item": 0x32, "Great Stairway: Left Loft Lower": 0x33, @@ -353,14 +353,14 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "The Throne Room: Great Stairs Center": 0x3E, "Forgotten City: Upper Underground Square": 0x3F, "Entrance: Double Room Lower": LocationInformation(0x0230914C, "overlay_119"), - "Buried Chamber: Westmost Room": 0x41, + "Buried Chamber: Westmost Room": LocationInformation(0x022FE700, "overlay_80"), "Sandy Grave: Pyramid East 1F": 0x42, "City of Haze: Register Room": 0x46, "City of Haze: Bar Left": 0x47, "Nation of Fools: Bottom-Left Room Building": 0x48, "Sandy Grave: Pyramid 3F Lower": 0x49, "Tower of Death: Rampart Room": 0x4A, - "Entrance: Beyond Great Armor Hallway": 0x4B, + "Entrance: Beyond Great Armor Hallway": LocationInformation(0x02305020, "overlay_79"), "Tower of Death: Elevator Room Lower": 0x4C, "Forest of Doom: Right Atrium East Square Room Right": 0x4D, "Forgotten City: Outside Left": 0x4E, @@ -376,7 +376,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Tower of Death: Lower Gears Room Spike Shaft": 0x5A, "Master's Keep: Right Square Room": 0x5D, "City of Haze: Central Hallway Stairs": 0x60, - "Buried Chamber: Right Shade Hall": 0x61, + "Buried Chamber: Right Shade Hall": LocationInformation(0x022FED6C, "overlay_80"), "City of Haze: Right Corner Room": 0x64, "City of Haze: Left Plaza": 0x65, "Great Stairway: Underground Hidden Room": 0x66, @@ -389,7 +389,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "City of Haze: Center Room Lower": 0x6E, "Sandy Grave: On Statue": 0x6F, "Tower of Death: Third Gears Room": 0x70, - "Entrance: Above Metal Block Room": LocationInformation(0x02304F3C, "overlay_79"), + "Entrance: Above Metal Block Room": LocationInformation(0x02304F3C, "overlay_79", True), "Dark Academy: Main Building Entrance": 0x72, "Burnt Paradise: Bottom Corner": 0x73, "Forest of Doom: Left Atrium Top": 0x75, @@ -410,7 +410,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "City of Haze: Dance Halls Basement": 0x8B, "City of Haze: Central Hallway Lower": 0x8C, "Burnt Paradise: Upper Right Big Room Right Side": 0x8F, - "Buried Chamber: Central Hall": 0x92, + "Buried Chamber: Central Hall": LocationInformation(0x022FE94C, "overlay_80"), "Great Stairway: Central Nook": 0x93, "Sandy Grave: Eastmost Item": 0x94, "Sandy Grave: Behind Bricks": 0x95, @@ -419,7 +419,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "City of Haze: Right Dance Hall Secret Room": 0xA7, "Nation of Fools: Bottom Left Medium Square On Wall": 0xA8, "City of Haze: First Big Room": 0xA9, - "Buried Chamber: Final Shaft Item": 0xAA, + "Buried Chamber: Final Shaft Item": LocationInformation(0x022FEE5C, "overlay_80"), "Great Stairway: Central Upper": 0xAB, "Forest of Doom: Right Atrium East Top Room": 0xAC, "Dark Academy: Right Building Top Floor": 0xAD, @@ -441,8 +441,8 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Great Stairway: Lower Grand Staircase Upper Alcove": 0xCA, "Great Stairway: Underground East Room": 0xCB, "Tower of Death: Elevator Room Top": 0xCC, - "Entrance: Hub Room": LocationInformation(0x02304AB0, "overlay_79"), - "Buried Chamber: Eastmost Room": 0xCE, + "Entrance: Hub Room": LocationInformation(0x02304AB0, "overlay_79", True), + "Buried Chamber: Eastmost Room": LocationInformation(0x022FEE38, "overlay_80"), "Tower of Death: Hidden Room": 0xD1, "Nation of Fools: Bottom-Right Room Middle Building": 0xD4, "Forgotten City: Pyramid 5F": 0xD5, @@ -483,7 +483,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Forgotten City: Lower Underground Square Upper Item": 0x105, "City of Haze: Center Room Top": 0x10E, "Great Stairway: Upper Grand Staircase Middle Alcove": 0x10F, - "Buried Chamber: Right Shaft Top": 0x110, + "Buried Chamber: Right Shaft Top": LocationInformation(0x022FEDA8, "overlay_80", True), "Sandy Grave: Pyramid 3F Upper": 0x113, "Nation of Fools: Bottom Right Medium Square On Wall": 0x114, "Sandy Grave: Lonely Mimic Alcove": 0x116, @@ -495,7 +495,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Nation of Fools: Top-Right Room Bottom Item": 0x11F, "Forgotten City: Pyramid East 5F": 0x122, "Burnt Paradise: Lower Vertical Hall": 0x123, - "Buried Chamber: West Shaft": 0x124, + "Buried Chamber: West Shaft": LocationInformation(0x022FE748, "overlay_80", True), "Great Stairway: Lower Grand Staircase Middle Alcove": 0x127, "Entrance: Behemoth Chase Room": LocationInformation(0x02304DA4, "overlay_79"), "Entrance: First Passage Hidden Wall": LocationInformation(0x02304B34, "overlay_79"), @@ -511,7 +511,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Burnt Paradise: Right Upper Big Corner On Wall": 0x148, "Nation of Fools: Right Center Room": 0x149, "Burnt Paradise: Left Big Corner": 0x14A, - "Buried Chamber: Hidden Wall Item": 0x014C, + "Buried Chamber: Hidden Wall Item": LocationInformation(0x022FECA0, "overlay_80"), "13th Street: Bakery Middle": 0x14D, "City of Haze: Cart Secret Right": 0x14E, "13th Street: Right Dance Hall Top Right": 0x150, From e705baa3fd25b34b873079358a1f370bd249b373 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 29 Apr 2026 07:32:52 -0500 Subject: [PATCH 43/58] more data getting --- worlds/cv_por/Rom.py | 2 + worlds/cv_por/static_location_data.py | 107 +++++++++++++------------- 2 files changed, 55 insertions(+), 54 deletions(-) diff --git a/worlds/cv_por/Rom.py b/worlds/cv_por/Rom.py index 4e26f2a52a73..db5dda180978 100644 --- a/worlds/cv_por/Rom.py +++ b/worlds/cv_por/Rom.py @@ -8,11 +8,13 @@ hash_us = "2edd57540cae45842fbd19c45a4214f9" + class FilePointer(NamedTuple): rom_address: int base_address: int file_size: int + file_pointers = { "arm9": FilePointer(0x4000, 0x02000000, 0xFDBB8), "overlay_113": FilePointer(0x636600, 0x022E8820, 0x6220), diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index 10857a3a6d7e..26fcea496150 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -3,12 +3,14 @@ # Because of this, they are asynchronous and out of order from what is expected # They are listed here in numerical order for clarity + class LocationInformation(NamedTuple): # In-game information used to define each Location in the ROM. pointer: int file: str is_grounded: bool = False location_type: str = "Normal" + location_ids = { "Entrance: Drawbridge Pit Item": 0x01, "Entrance: Drawbridge Switch Item": 0x02, @@ -225,14 +227,14 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Nation of Fools: Right Center Room": 0x149, "Burnt Paradise: Left Big Corner": 0x14A, "Buried Chamber: Hidden Wall Item": 0x014C, - "13th Street: Bakery Middle": 0x14D, + "13th Street: Bakery Middle": LocationInformation(0x022FCB54, "overlay_105"), "City of Haze: Cart Secret Right": 0x14E, - "13th Street: Right Dance Hall Top Right": 0x150, - "13th Street: Bakery Left": 0x152, + "13th Street: Right Dance Hall Top Right": LocationInformation(0x022FED84, "overlay_105", True), + "13th Street: Bakery Left": LocationInformation(0x022FCB48, "overlay_105"), "13th Street: Train Room Secret Left": 0x153, "City of Haze: Bar Right": 0x154, "13th Street: Square Room": 0x155, - "13th Street: Bakery Right": 0x156, + "13th Street: Bakery Right": LocationInformation(0x022FCB60, "overlay_105"), "Burnt Paradise: Bottom Right Corner Room": 0x157, "Nation of Fools: Left Center Room": 0x15F, "Dark Academy: Main Building Square Room Breakable Wall": 0x160, @@ -301,8 +303,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "City of Haze: Church Hallway": LocationInformation(0x022F6E7C, "overlay_94"), "Entrance: Statue Room Above Statue": LocationInformation(0x02304E64, "overlay_79"), "City of Haze: Post-Boss Room": LocationInformation(0x02302A18, "overlay_95"), - - "City of Haze: Heavy Cart Item": 0x06, + "City of Haze: Heavy Cart Item": LocationInformation(0x022F6F3C, "overlay_94"), "Sandy Grave: Pyramid 5F Nook": 0x07, "Great Stairway: After Keremet Lower": 0x09, "Forgotten City: Pyramid 1F Bricks": 0x0A, @@ -314,8 +315,8 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Dark Academy: Right Building Center Room": 0x12, "The Throne Room: Above Throne Left": 0x13, "Sandy Grave: Boulder Room Tunnel": 0x14, - "City of Haze: Before Cart Room": 0x16, - "City of Haze: Underground Square Right": 0x17, + "City of Haze: Before Cart Room": LocationInformation(0x022F6D98, "overlay_94"), + "City of Haze: Underground Square Right": LocationInformation(0x02302B68, "overlay_95"), "Sandy Grave: Upper Big Underground Top Right": 0x18, "Nation of Fools: Bottom Corner Room": 0x19, "Nation of Fools: Mid-Right Corner Room": 0x1A, @@ -330,13 +331,13 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Forgotten City: Big Shaft Room Left": 0x26, "Nation of Fools: Left Upper Corner Room": 0x27, "Dark Academy: Main Building Ghoul Room": 0x28, - "13th Street: Triple Moth Room": 0x29, + "13th Street: Triple Moth Room": LocationInformation(0x022FCC38, "overlay_105", True), "Tower of Death: Secret Room": 0x2A, "Nation of Fools: Right Horizontal Room": 0x2B, "Burnt Paradise: Left Lower Isolated Room": 0x2C, "Burnt Paradise: Right Lower Isolated Room": 0x2D, "The Throne Room: Great Stairs Under Stairs": 0x2E, - "13th Street: Behind Final Guard": 0x2F, + "13th Street: Behind Final Guard": LocationInformation(0x022FEE50, "overlay_105", True), "Buried Chamber: Right Secret": LocationInformation(0x022FEC34, "overlay_80"), "Sandy Grave: Bottom Right Room": 0x31, "Tower of Death: Passage Hidden Item": 0x32, @@ -345,7 +346,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Forgotten City: Big Shaft Room Right": 0x36, "Nation of Fools: Right Bottom Corner Room": 0x37, "Master's Keep: Hidden Room": 0x38, - "13th Street: Three Amalaric Snipers": 0x39, + "13th Street: Three Amalaric Snipers": LocationInformation(0x022FF0F0, "overlay_105", True), "Dark Academy: Main Building Three Toads": 0x3A, "Nation of Fools: Top Corner Room": 0x3B, "Burnt Paradise: Left Upper Isolated Room": 0x3C, @@ -355,8 +356,8 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Entrance: Double Room Lower": LocationInformation(0x0230914C, "overlay_119"), "Buried Chamber: Westmost Room": LocationInformation(0x022FE700, "overlay_80"), "Sandy Grave: Pyramid East 1F": 0x42, - "City of Haze: Register Room": 0x46, - "City of Haze: Bar Left": 0x47, + "City of Haze: Register Room": LocationInformation(0x02301A3C, "overlay_93"), + "City of Haze: Bar Left": LocationInformation(0x023019DC, "overlay_93"), "Nation of Fools: Bottom-Left Room Building": 0x48, "Sandy Grave: Pyramid 3F Lower": 0x49, "Tower of Death: Rampart Room": 0x4A, @@ -367,7 +368,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Sandy Grave: Upper Big Underground Top Left": 0x4F, "Burnt Paradise: Left Upper Big Corner": 0x51, "Great Stairway: Upper Grand Staircase Top Left Item": 0x53, - "City of Haze: Axe Courtyard": 0x54, + "City of Haze: Axe Courtyard": LocationInformation(0x022F6F84, "overlay_94"), "Tower of Death: Top of the Tower": 0x55, "Great Stairway: Upper Underground Room": 0x56, "Entrance: Drawbridge Upper Item": LocationInformation(0x022F9128, "overlay_78"), @@ -375,10 +376,10 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Tower of Death: Second Gears Room Right": 0x59, "Tower of Death: Lower Gears Room Spike Shaft": 0x5A, "Master's Keep: Right Square Room": 0x5D, - "City of Haze: Central Hallway Stairs": 0x60, + "City of Haze: Central Hallway Stairs": LocationInformation(0x022F6EB8, "overlay_94"), "Buried Chamber: Right Shade Hall": LocationInformation(0x022FED6C, "overlay_80"), - "City of Haze: Right Corner Room": 0x64, - "City of Haze: Left Plaza": 0x65, + "City of Haze: Right Corner Room": LocationInformation(0x02301AA8, "overlay_93"), + "City of Haze: Left Plaza": LocationInformation(0x02301DE4, "overlay_93"), "Great Stairway: Underground Hidden Room": 0x66, "Sandy Grave: Lower Big Underground Room Top": 0x67, "Nation of Fools: Upper Upside Down": 0x68, @@ -386,7 +387,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Dark Academy: Main Building Secret Ceiling": 0x6B, "Forest of Doom: Secret Cave Room": 0x6C, "Nest of Evil: Doppelganger Reward": 0x6D, - "City of Haze: Center Room Lower": 0x6E, + "City of Haze: Center Room Lower": LocationInformation(0x02301C4C, "overlay_93"), "Sandy Grave: On Statue": 0x6F, "Tower of Death: Third Gears Room": 0x70, "Entrance: Above Metal Block Room": LocationInformation(0x02304F3C, "overlay_79", True), @@ -400,44 +401,44 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Burnt Paradise: Bottom Right Big Room": 0x7A, "Entrance: Right Hidden Room": LocationInformation(0x022F92C0, "overlay_78"), "Entrance: First Passage Lower Item": LocationInformation(0x02304AE0, "overlay_79"), - "City of Haze: Left Dance Hall Right Item": 0x7E, - "City of Haze: Tunnel Room Left": 0x7F, + "City of Haze: Left Dance Hall Right Item": LocationInformation(0x02302784, "overlay_95"), + "City of Haze: Tunnel Room Left": LocationInformation(0x02302928, "overlay_95", True), "Burnt Paradise: Upper Left Big Room Middle Building": 0x82, - "City of Haze: Chunky Room": 0x84, + "City of Haze: Chunky Room": LocationInformation(0x023018BC, "overlay_93"), "Entrance: Pre-Behemoth Boss Room": LocationInformation(0x022F91E8, "overlay_78"), "Great Stairway: After Keremet Upper": 0x89, "Tower of Death: Gear Belt Room": 0x8A, - "City of Haze: Dance Halls Basement": 0x8B, - "City of Haze: Central Hallway Lower": 0x8C, + "City of Haze: Dance Halls Basement": LocationInformation(0x023028C8, "overlay_95"), + "City of Haze: Central Hallway Lower": LocationInformation(0x022F6EC4, "overlay_94"), "Burnt Paradise: Upper Right Big Room Right Side": 0x8F, "Buried Chamber: Central Hall": LocationInformation(0x022FE94C, "overlay_80"), "Great Stairway: Central Nook": 0x93, "Sandy Grave: Eastmost Item": 0x94, "Sandy Grave: Behind Bricks": 0x95, - "City of Haze: Underground Square Left": 0xA0, + "City of Haze: Underground Square Left": LocationInformation(0x02302C10, "overlay_95"), "Great Stairway: Upper Grand Staircase Upper Alcove": 0xA1, - "City of Haze: Right Dance Hall Secret Room": 0xA7, + "City of Haze: Right Dance Hall Secret Room": LocationInformation(0x023029E8, "overlay_95", True), "Nation of Fools: Bottom Left Medium Square On Wall": 0xA8, - "City of Haze: First Big Room": 0xA9, + "City of Haze: First Big Room": LocationInformation(0x023016C4, "overlay_93"), "Buried Chamber: Final Shaft Item": LocationInformation(0x022FEE5C, "overlay_80"), "Great Stairway: Central Upper": 0xAB, "Forest of Doom: Right Atrium East Top Room": 0xAC, "Dark Academy: Right Building Top Floor": 0xAD, "Sandy Grave: Boulder Room Corner Alcove": 0xAE, "Forgotten City: Post-Boss Item": 0xAF, - "City of Haze: Cart Secret Left": 0xB0, - "City of Haze: Winding Rooms": 0xB3, + "City of Haze: Cart Secret Left": LocationInformation(0x022F7008, "overlay_94"), + "City of Haze: Winding Rooms": LocationInformation(0x023017C0, "overlay_93"), "Great Stairway: Lower Grand Staircase Lower Alcove": 0xB4, "Forest of Doom: Right Atrium East Square Room Left": 0xB5, "Forgotten City: Bottom Chamber": 0xB9, - "City of Haze: Meat Room": 0xBB, + "City of Haze: Meat Room": LocationInformation(0x02301628, "overlay_93"), "Great Stairway: Big Underground Room Hidden Item": 0xBF, "Great Stairway: Right Loft": 0xC0, "Great Stairway: Left Loft": 0xC1, "Nation of Fools: Bottom-Right Room Left Building": 0xC2, - "13th Street: Many Nyxes Room": 0xC4, + "13th Street: Many Nyxes Room": LocationInformation(0x022FEF7C, "overlay_105", True), "The Throne Room: Above Throne Right": 0xC5, - "City of Haze: Tunnel Room Right": 0xC8, + "City of Haze: Tunnel Room Right": LocationInformation(0x02302934, "overlay_95", True), "Great Stairway: Lower Grand Staircase Upper Alcove": 0xCA, "Great Stairway: Underground East Room": 0xCB, "Tower of Death: Elevator Room Top": 0xCC, @@ -446,48 +447,48 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Tower of Death: Hidden Room": 0xD1, "Nation of Fools: Bottom-Right Room Middle Building": 0xD4, "Forgotten City: Pyramid 5F": 0xD5, - "13th Street: Train Room Secret Right": 0xD6, - "City of Haze: Left Dance Hall Left Item": 0xD8, + "13th Street: Train Room Secret Right": LocationInformation(0x022F3B8C, "overlay_104"), + "City of Haze: Left Dance Hall Left Item": LocationInformation(0x02302778, "overlay_95"), "Great Stairway: Big Underground Room Lower": 0xDA, "Tower of Death: Elevator Room Middle": 0xDB, "Nation of Fools: Crevice Item": 0xDC, - "City of Haze: First Passage": 0xDD, + "City of Haze: First Passage": LocationInformation(0x02301598, "overlay_93"), "Forgotten City: Gorgon Room": 0xDE, - "City of Haze: Axe Armor Item": 0xE0, + "City of Haze: Axe Armor Item": LocationInformation(0x02301AD8, "overlay_93"), "Sandy Grave: Giant Shaft Upper Item": 0xE1, - "City of Haze: Dance Halls Vertical Hallway": 0xE2, + "City of Haze: Dance Halls Vertical Hallway": LocationInformation(0x0230288C, "overlay_95"), "Tower of Death: Above Motorcycles": 0xE3, "Nation of Fools: Top-Right Room Axe Item": 0xE4, "Dark Academy: Main Building Secret Malphas Room": 0xE5, - "13th Street: Underground Secret Room": 0xE6, + "13th Street: Underground Secret Room": LocationInformation(0x022FF2AC, "overlay_105", True), "Great Stairway: Big Underground Room Upper": 0xE7, "Forest of Doom: Right Atrium West Top Room": 0xE8, "Burnt Paradise: Upper Left Big Room Left Building": 0xE9, - "13th Street: Underground Demon Room": 0xEA, + "13th Street: Underground Demon Room": LocationInformation(0x022FF264, "overlay_105", True), "Sandy Grave: Mega Ghost Room Lower": 0xEB, - "13th Street: Left Big Room": 0xEC, - "City of Haze: Bread Shelf": 0xED, + "13th Street: Left Big Room": LocationInformation(0x022FD058, "overlay_105", True), + "City of Haze: Bread Shelf": LocationInformation(0x02301D78, "overlay_93", True), "Burnt Paradise: Upper Left Lower Corner Upright Building": 0xEE, - "City of Haze: Underground Secret Room": 0xEF, + "City of Haze: Underground Secret Room": LocationInformation(0x02302B50, "overlay_95", True), "Sandy Grave: Mega Ghost Room Upper": 0xF0, "Burnt Paradise: Lower Left Big Room": 0xF1, - "13th Street: Secret Floor Room": 0xF3, - "City of Haze: Right Dance Hall Item": 0xF7, + "13th Street: Secret Floor Room": LocationInformation(0x022FF024, "overlay_105"), + "City of Haze: Right Dance Hall Item": LocationInformation(0x02302964, "overlay_95"), "Tower of Death: Top Gears Room": 0xF9, - "13th Street: East Courtyard": 0xFA, - "City of Haze: Corner Room": 0xFC, + "13th Street: East Courtyard": LocationInformation(0x022FCA04, "overlay_105", True), + "City of Haze: Corner Room": LocationInformation(0x0230176C, "overlay_93"), "Sandy Grave: Giant Shaft Middle Item": 0xFD, "Entrance: Lilith Room": LocationInformation(0x02304E28, "overlay_79"), "Sandy Grave: Right Trap": 0x103, "Nation of Fools: Left Bottom Corner Room": 0x104, "Forgotten City: Lower Underground Square Upper Item": 0x105, - "City of Haze: Center Room Top": 0x10E, + "City of Haze: Center Room Top": LocationInformation(0x02301C40, "overlay_93"), "Great Stairway: Upper Grand Staircase Middle Alcove": 0x10F, "Buried Chamber: Right Shaft Top": LocationInformation(0x022FEDA8, "overlay_80", True), "Sandy Grave: Pyramid 3F Upper": 0x113, "Nation of Fools: Bottom Right Medium Square On Wall": 0x114, "Sandy Grave: Lonely Mimic Alcove": 0x116, - "13th Street: Near Big Room": 0x118, + "13th Street: Near Big Room": LocationInformation(0x022FCE18, "overlay_105", True), "Forgotten City: Pyramid East 3F": 0x119, "Great Stairway: Upper Grand Staircase Lower Alcove": 0x11B, "Nation of Fools: Right Vertical Room": 0x11C, @@ -513,12 +514,12 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Burnt Paradise: Left Big Corner": 0x14A, "Buried Chamber: Hidden Wall Item": LocationInformation(0x022FECA0, "overlay_80"), "13th Street: Bakery Middle": 0x14D, - "City of Haze: Cart Secret Right": 0x14E, + "City of Haze: Cart Secret Right": LocationInformation(0x022F7014, "overlay_94"), "13th Street: Right Dance Hall Top Right": 0x150, "13th Street: Bakery Left": 0x152, - "13th Street: Train Room Secret Left": 0x153, - "City of Haze: Bar Right": 0x154, - "13th Street: Square Room": 0x155, + "13th Street: Train Room Secret Left": LocationInformation(0x022F3B80, "overlay_104"), + "City of Haze: Bar Right": LocationInformation(0x023019E8, "overlay_93"), + "13th Street: Square Room": LocationInformation(0x022FCA4C, "overlay_105", True), "13th Street: Bakery Right": 0x156, "Burnt Paradise: Bottom Right Corner Room": 0x157, "Nation of Fools: Left Center Room": 0x15F, @@ -537,7 +538,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Great Stairway: Connector Pipe Left": 0x180, "Great Stairway: Connector Pipe Right": 0x181, "The Throne Room: Great Stairs Left": 0x182, - "13th Street: Right Dance Hall Top Left": 0x183, + "13th Street: Right Dance Hall Top Left": LocationInformation(0x022FED78, "overlay_105", True), "Dark Academy: West Building Top Floor Left": 0x184, "Forgotten City: Pyramid East 1F": 0x185, "Forgotten City: Lower Underground Square Lower Item": 0x186, @@ -546,5 +547,3 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Quest: Preparations": 0x0200 } - -"Dummy": LocationInformation(0x022F9110, "overlay_78"), \ No newline at end of file From f201888badaefde9264bf19076669f078ad9e28b Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 29 Apr 2026 08:01:35 -0500 Subject: [PATCH 44/58] Desert locs --- worlds/cv_por/static_location_data.py | 84 +++++++++++++-------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index 26fcea496150..aa85fda18a97 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -304,9 +304,9 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Entrance: Statue Room Above Statue": LocationInformation(0x02304E64, "overlay_79"), "City of Haze: Post-Boss Room": LocationInformation(0x02302A18, "overlay_95"), "City of Haze: Heavy Cart Item": LocationInformation(0x022F6F3C, "overlay_94"), - "Sandy Grave: Pyramid 5F Nook": 0x07, + "Sandy Grave: Pyramid 5F Nook": LocationInformation(0x023071AC, "overlay_91"), "Great Stairway: After Keremet Lower": 0x09, - "Forgotten City: Pyramid 1F Bricks": 0x0A, + "Forgotten City: Pyramid 1F Bricks": LocationInformation(0x02308924, "overlay_102"), "Nation of Fools: Central Top Shaft": 0x0D, "Entrance: Above Hub": LocationInformation(0x02304A8C, "overlay_79"), "Entrance: Double Room Upper": LocationInformation(0x02309140, "overlay_119"), @@ -314,21 +314,21 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Forest of Doom: Post-Boss Room": 0x11, "Dark Academy: Right Building Center Room": 0x12, "The Throne Room: Above Throne Left": 0x13, - "Sandy Grave: Boulder Room Tunnel": 0x14, + "Sandy Grave: Boulder Room Tunnel": LocationInformation(0x023064EC, "overlay_92"), "City of Haze: Before Cart Room": LocationInformation(0x022F6D98, "overlay_94"), "City of Haze: Underground Square Right": LocationInformation(0x02302B68, "overlay_95"), - "Sandy Grave: Upper Big Underground Top Right": 0x18, + "Sandy Grave: Upper Big Underground Top Right": LocationInformation(0x02306078, "overlay_92", True), "Nation of Fools: Bottom Corner Room": 0x19, "Nation of Fools: Mid-Right Corner Room": 0x1A, - "Sandy Grave: Lower Big Underground Room Bottom": 0x1B, - "Sandy Grave: Left Trap": 0x1C, + "Sandy Grave: Lower Big Underground Room Bottom": LocationInformation(0x02306354, "overlay_92", True), + "Sandy Grave: Left Trap": LocationInformation(0x02306270, "overlay_92", True), "Buried Chamber: Central Secret": LocationInformation(0x022FEA00, "overlay_80"), - "Sandy Grave: Sandworm Stomach": 0x21, + "Sandy Grave: Sandworm Stomach": LocationInformation(0x02307674, "overlay_91"), "Tower of Death: Second Gears Room Left": 0x22, "Great Stairway: Lower Grand Staircase Corner Nook": 0x23, "Master's Keep: Entrance": 0x24, "Forest of Doom: Left Atrium Big Room": 0x25, - "Forgotten City: Big Shaft Room Left": 0x26, + "Forgotten City: Big Shaft Room Left": LocationInformation(0x02304648, "overlay_103", True), "Nation of Fools: Left Upper Corner Room": 0x27, "Dark Academy: Main Building Ghoul Room": 0x28, "13th Street: Triple Moth Room": LocationInformation(0x022FCC38, "overlay_105", True), @@ -339,11 +339,11 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "The Throne Room: Great Stairs Under Stairs": 0x2E, "13th Street: Behind Final Guard": LocationInformation(0x022FEE50, "overlay_105", True), "Buried Chamber: Right Secret": LocationInformation(0x022FEC34, "overlay_80"), - "Sandy Grave: Bottom Right Room": 0x31, + "Sandy Grave: Bottom Right Room": LocationInformation(0x02306540, "overlay_92"), "Tower of Death: Passage Hidden Item": 0x32, "Great Stairway: Left Loft Lower": 0x33, "Entrance: Center Hidden Room": LocationInformation(0x02304F0C, "overlay_79"), - "Forgotten City: Big Shaft Room Right": 0x36, + "Forgotten City: Big Shaft Room Right": LocationInformation(0x02304654, "overlay_103", True), "Nation of Fools: Right Bottom Corner Room": 0x37, "Master's Keep: Hidden Room": 0x38, "13th Street: Three Amalaric Snipers": LocationInformation(0x022FF0F0, "overlay_105", True), @@ -352,20 +352,20 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Burnt Paradise: Left Upper Isolated Room": 0x3C, "Burnt Paradise: Right Upper Isolated Room": 0x3D, "The Throne Room: Great Stairs Center": 0x3E, - "Forgotten City: Upper Underground Square": 0x3F, + "Forgotten City: Upper Underground Square": LocationInformation(0x023048A0, "overlay_103", True), "Entrance: Double Room Lower": LocationInformation(0x0230914C, "overlay_119"), "Buried Chamber: Westmost Room": LocationInformation(0x022FE700, "overlay_80"), - "Sandy Grave: Pyramid East 1F": 0x42, + "Sandy Grave: Pyramid East 1F": LocationInformation(0x02307500, "overlay_91"), "City of Haze: Register Room": LocationInformation(0x02301A3C, "overlay_93"), "City of Haze: Bar Left": LocationInformation(0x023019DC, "overlay_93"), "Nation of Fools: Bottom-Left Room Building": 0x48, - "Sandy Grave: Pyramid 3F Lower": 0x49, + "Sandy Grave: Pyramid 3F Lower": LocationInformation(0x0230711C, "overlay_91"), "Tower of Death: Rampart Room": 0x4A, "Entrance: Beyond Great Armor Hallway": LocationInformation(0x02305020, "overlay_79"), "Tower of Death: Elevator Room Lower": 0x4C, "Forest of Doom: Right Atrium East Square Room Right": 0x4D, - "Forgotten City: Outside Left": 0x4E, - "Sandy Grave: Upper Big Underground Top Left": 0x4F, + "Forgotten City: Outside Left": LocationInformation(0x02308864, "overlay_102"), + "Sandy Grave: Upper Big Underground Top Left": LocationInformation(0x02306084, "overlay_92"), "Burnt Paradise: Left Upper Big Corner": 0x51, "Great Stairway: Upper Grand Staircase Top Left Item": 0x53, "City of Haze: Axe Courtyard": LocationInformation(0x022F6F84, "overlay_94"), @@ -381,21 +381,21 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "City of Haze: Right Corner Room": LocationInformation(0x02301AA8, "overlay_93"), "City of Haze: Left Plaza": LocationInformation(0x02301DE4, "overlay_93"), "Great Stairway: Underground Hidden Room": 0x66, - "Sandy Grave: Lower Big Underground Room Top": 0x67, + "Sandy Grave: Lower Big Underground Room Top": LocationInformation(0x02306348, "overlay_92"), "Nation of Fools: Upper Upside Down": 0x68, - "Forgotten City: Lower Boulder Room Tunnel Alcove": 0x69, + "Forgotten City: Lower Boulder Room Tunnel Alcove": LocationInformation(0x023049A8, "overlay_103"), "Dark Academy: Main Building Secret Ceiling": 0x6B, "Forest of Doom: Secret Cave Room": 0x6C, "Nest of Evil: Doppelganger Reward": 0x6D, "City of Haze: Center Room Lower": LocationInformation(0x02301C4C, "overlay_93"), - "Sandy Grave: On Statue": 0x6F, + "Sandy Grave: On Statue": LocationInformation(0x02307068, "overlay_91", True), "Tower of Death: Third Gears Room": 0x70, "Entrance: Above Metal Block Room": LocationInformation(0x02304F3C, "overlay_79", True), "Dark Academy: Main Building Entrance": 0x72, "Burnt Paradise: Bottom Corner": 0x73, "Forest of Doom: Left Atrium Top": 0x75, "Great Stairway: Before Keremet Room": 0x76, - "Forgotten City: Lower Boulder Room Upper Alcove": 0x77, + "Forgotten City: Lower Boulder Room Upper Alcove": LocationInformation(0x0230499C, "overlay_103", True), "Entrance: First Passage Upper Item": LocationInformation(0x02304AEC, "overlay_79"), "Great Stairway: Upper Grand Staircase Corner Nook": 0x79, "Burnt Paradise: Bottom Right Big Room": 0x7A, @@ -413,8 +413,8 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Burnt Paradise: Upper Right Big Room Right Side": 0x8F, "Buried Chamber: Central Hall": LocationInformation(0x022FE94C, "overlay_80"), "Great Stairway: Central Nook": 0x93, - "Sandy Grave: Eastmost Item": 0x94, - "Sandy Grave: Behind Bricks": 0x95, + "Sandy Grave: Eastmost Item": LocationInformation(0x02307608, "overlay_91", True), + "Sandy Grave: Behind Bricks": LocationInformation(0x02307074, "overlay_91"), "City of Haze: Underground Square Left": LocationInformation(0x02302C10, "overlay_95"), "Great Stairway: Upper Grand Staircase Upper Alcove": 0xA1, "City of Haze: Right Dance Hall Secret Room": LocationInformation(0x023029E8, "overlay_95", True), @@ -424,13 +424,13 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Great Stairway: Central Upper": 0xAB, "Forest of Doom: Right Atrium East Top Room": 0xAC, "Dark Academy: Right Building Top Floor": 0xAD, - "Sandy Grave: Boulder Room Corner Alcove": 0xAE, - "Forgotten City: Post-Boss Item": 0xAF, + "Sandy Grave: Boulder Room Corner Alcove": LocationInformation(0x023064F8, "overlay_92"), + "Forgotten City: Post-Boss Item": LocationInformation(0x023046B4, "overlay_103"), "City of Haze: Cart Secret Left": LocationInformation(0x022F7008, "overlay_94"), "City of Haze: Winding Rooms": LocationInformation(0x023017C0, "overlay_93"), "Great Stairway: Lower Grand Staircase Lower Alcove": 0xB4, "Forest of Doom: Right Atrium East Square Room Left": 0xB5, - "Forgotten City: Bottom Chamber": 0xB9, + "Forgotten City: Bottom Chamber": LocationInformation(0x02304D50, "overlay_103"), "City of Haze: Meat Room": LocationInformation(0x02301628, "overlay_93"), "Great Stairway: Big Underground Room Hidden Item": 0xBF, "Great Stairway: Right Loft": 0xC0, @@ -446,16 +446,16 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Buried Chamber: Eastmost Room": LocationInformation(0x022FEE38, "overlay_80"), "Tower of Death: Hidden Room": 0xD1, "Nation of Fools: Bottom-Right Room Middle Building": 0xD4, - "Forgotten City: Pyramid 5F": 0xD5, + "Forgotten City: Pyramid 5F": LocationInformation(0x02308A2C, "overlay_102", True), "13th Street: Train Room Secret Right": LocationInformation(0x022F3B8C, "overlay_104"), "City of Haze: Left Dance Hall Left Item": LocationInformation(0x02302778, "overlay_95"), "Great Stairway: Big Underground Room Lower": 0xDA, "Tower of Death: Elevator Room Middle": 0xDB, "Nation of Fools: Crevice Item": 0xDC, "City of Haze: First Passage": LocationInformation(0x02301598, "overlay_93"), - "Forgotten City: Gorgon Room": 0xDE, + "Forgotten City: Gorgon Room": LocationInformation(0x02304858, "overlay_103", True), "City of Haze: Axe Armor Item": LocationInformation(0x02301AD8, "overlay_93"), - "Sandy Grave: Giant Shaft Upper Item": 0xE1, + "Sandy Grave: Giant Shaft Upper Item": LocationInformation(0x02305DFC, "overlay_92"), "City of Haze: Dance Halls Vertical Hallway": LocationInformation(0x0230288C, "overlay_95"), "Tower of Death: Above Motorcycles": 0xE3, "Nation of Fools: Top-Right Room Axe Item": 0xE4, @@ -465,36 +465,36 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Forest of Doom: Right Atrium West Top Room": 0xE8, "Burnt Paradise: Upper Left Big Room Left Building": 0xE9, "13th Street: Underground Demon Room": LocationInformation(0x022FF264, "overlay_105", True), - "Sandy Grave: Mega Ghost Room Lower": 0xEB, + "Sandy Grave: Mega Ghost Room Lower": LocationInformation(0x02307434, "overlay_91", True), "13th Street: Left Big Room": LocationInformation(0x022FD058, "overlay_105", True), "City of Haze: Bread Shelf": LocationInformation(0x02301D78, "overlay_93", True), "Burnt Paradise: Upper Left Lower Corner Upright Building": 0xEE, "City of Haze: Underground Secret Room": LocationInformation(0x02302B50, "overlay_95", True), - "Sandy Grave: Mega Ghost Room Upper": 0xF0, + "Sandy Grave: Mega Ghost Room Upper": LocationInformation(0x0230741C, "overlay_91"), "Burnt Paradise: Lower Left Big Room": 0xF1, "13th Street: Secret Floor Room": LocationInformation(0x022FF024, "overlay_105"), "City of Haze: Right Dance Hall Item": LocationInformation(0x02302964, "overlay_95"), "Tower of Death: Top Gears Room": 0xF9, "13th Street: East Courtyard": LocationInformation(0x022FCA04, "overlay_105", True), "City of Haze: Corner Room": LocationInformation(0x0230176C, "overlay_93"), - "Sandy Grave: Giant Shaft Middle Item": 0xFD, + "Sandy Grave: Giant Shaft Middle Item": LocationInformation(0x02305DE4, "overlay_92", True), "Entrance: Lilith Room": LocationInformation(0x02304E28, "overlay_79"), - "Sandy Grave: Right Trap": 0x103, + "Sandy Grave: Right Trap": LocationInformation(0x0230627C, "overlay_92", True), "Nation of Fools: Left Bottom Corner Room": 0x104, - "Forgotten City: Lower Underground Square Upper Item": 0x105, + "Forgotten City: Lower Underground Square Upper Item": LocationInformation(0x02304A80, "overlay_103"), "City of Haze: Center Room Top": LocationInformation(0x02301C40, "overlay_93"), "Great Stairway: Upper Grand Staircase Middle Alcove": 0x10F, "Buried Chamber: Right Shaft Top": LocationInformation(0x022FEDA8, "overlay_80", True), - "Sandy Grave: Pyramid 3F Upper": 0x113, + "Sandy Grave: Pyramid 3F Upper": LocationInformation(0x02307128, "overlay_91"), "Nation of Fools: Bottom Right Medium Square On Wall": 0x114, - "Sandy Grave: Lonely Mimic Alcove": 0x116, + "Sandy Grave: Lonely Mimic Alcove": LocationInformation(0x023064A4, "overlay_92", True), "13th Street: Near Big Room": LocationInformation(0x022FCE18, "overlay_105", True), - "Forgotten City: Pyramid East 3F": 0x119, + "Forgotten City: Pyramid East 3F": LocationInformation(0x02308C9C, "overlay_102", True), "Great Stairway: Upper Grand Staircase Lower Alcove": 0x11B, "Nation of Fools: Right Vertical Room": 0x11C, - "Sandy Grave: Fleaman Pit": 0x11E, + "Sandy Grave: Fleaman Pit": LocationInformation(0x023073A4, "overlay_91"), "Nation of Fools: Top-Right Room Bottom Item": 0x11F, - "Forgotten City: Pyramid East 5F": 0x122, + "Forgotten City: Pyramid East 5F": LocationInformation(0x02308BC4, "overlay_102", True), "Burnt Paradise: Lower Vertical Hall": 0x123, "Buried Chamber: West Shaft": LocationInformation(0x022FE748, "overlay_80", True), "Great Stairway: Lower Grand Staircase Middle Alcove": 0x127, @@ -502,10 +502,10 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Entrance: First Passage Hidden Wall": LocationInformation(0x02304B34, "overlay_79"), "Tower of Death: Lower Passage Hidden Item": 0x131, "Dark Academy: West Building Breakable Wall": 0x134, - "Forgotten City: Armored Fleaman Pit": 0x137, + "Forgotten City: Armored Fleaman Pit": LocationInformation(0x02308C24, "overlay_102", True), "Nation of Fools: Top-Left Room Right Building": 0x13F, "Forest of Doom: Left Atrium Breakable Wall": 0x140, - "Sandy Grave: Giant Shaft Low Item": 0x144, + "Sandy Grave: Giant Shaft Low Item": LocationInformation(0x02305DF0, "overlay_92", True), "The Throne Room: Great Stairs Hidden": 0x145, "Nation of Fools: Top-Right Room Breakable Wall": 0x146, "Burnt Paradise: Bottom Right Breakable Wall": 0x147, @@ -531,7 +531,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Forest of Doom: Left Atrium Middle Room": 0x167, "Burnt Paradise: Upper Right Big Room": 0x16B, "Forest of Doom: Near Cave Save Breakable Wall": 0x16C, - "Sandy Grave: Pyramid East 5F": 0x173, + "Sandy Grave: Pyramid East 5F": LocationInformation(0x02307344, "overlay_91"), "Nest of Evil: First Item": 0x175, "Nest of Evil: Second Item": 0x176, "Forest of Doom: Left Atrium Upper Room": 0x177, @@ -540,8 +540,8 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "The Throne Room: Great Stairs Left": 0x182, "13th Street: Right Dance Hall Top Left": LocationInformation(0x022FED78, "overlay_105", True), "Dark Academy: West Building Top Floor Left": 0x184, - "Forgotten City: Pyramid East 1F": 0x185, - "Forgotten City: Lower Underground Square Lower Item": 0x186, + "Forgotten City: Pyramid East 1F": LocationInformation(0x02308CF0, "overlay_102", True), + "Forgotten City: Lower Underground Square Lower Item": LocationInformation(0x02304A8C, "overlay_103", True), "Tower of Death: Stella Item": 0x187, "Master's Keep: Whip Trial": 0x188, From 82ec02d92fdfe40c9fafd4d8b2222f8eaedb68d4 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 29 Apr 2026 08:38:15 -0500 Subject: [PATCH 45/58] Circus locs --- worlds/cv_por/static_location_data.py | 100 +++++++++++++------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index aa85fda18a97..09572532c096 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -227,14 +227,14 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Nation of Fools: Right Center Room": 0x149, "Burnt Paradise: Left Big Corner": 0x14A, "Buried Chamber: Hidden Wall Item": 0x014C, - "13th Street: Bakery Middle": LocationInformation(0x022FCB54, "overlay_105"), + "13th Street: Bakery Middle": 0x14D, "City of Haze: Cart Secret Right": 0x14E, - "13th Street: Right Dance Hall Top Right": LocationInformation(0x022FED84, "overlay_105", True), - "13th Street: Bakery Left": LocationInformation(0x022FCB48, "overlay_105"), + "13th Street: Right Dance Hall Top Right": 0x150, + "13th Street: Bakery Left": 0x152, "13th Street: Train Room Secret Left": 0x153, "City of Haze: Bar Right": 0x154, "13th Street: Square Room": 0x155, - "13th Street: Bakery Right": LocationInformation(0x022FCB60, "overlay_105"), + "13th Street: Bakery Right": 0x156, "Burnt Paradise: Bottom Right Corner Room": 0x157, "Nation of Fools: Left Center Room": 0x15F, "Dark Academy: Main Building Square Room Breakable Wall": 0x160, @@ -307,10 +307,10 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Sandy Grave: Pyramid 5F Nook": LocationInformation(0x023071AC, "overlay_91"), "Great Stairway: After Keremet Lower": 0x09, "Forgotten City: Pyramid 1F Bricks": LocationInformation(0x02308924, "overlay_102"), - "Nation of Fools: Central Top Shaft": 0x0D, + "Nation of Fools: Central Top Shaft": LocationInformation(0x022F6948, "overlay_98"), "Entrance: Above Hub": LocationInformation(0x02304A8C, "overlay_79"), "Entrance: Double Room Upper": LocationInformation(0x02309140, "overlay_119"), - "Nation of Fools: Legion Arena": 0x10, + "Nation of Fools: Legion Arena": LocationInformation(0x022F6A98, "overlay_98", True), "Forest of Doom: Post-Boss Room": 0x11, "Dark Academy: Right Building Center Room": 0x12, "The Throne Room: Above Throne Left": 0x13, @@ -318,8 +318,8 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "City of Haze: Before Cart Room": LocationInformation(0x022F6D98, "overlay_94"), "City of Haze: Underground Square Right": LocationInformation(0x02302B68, "overlay_95"), "Sandy Grave: Upper Big Underground Top Right": LocationInformation(0x02306078, "overlay_92", True), - "Nation of Fools: Bottom Corner Room": 0x19, - "Nation of Fools: Mid-Right Corner Room": 0x1A, + "Nation of Fools: Bottom Corner Room": LocationInformation(0x023005F4, "overlay_96", True), + "Nation of Fools: Mid-Right Corner Room": LocationInformation(0x02300438, "overlay_96", True), "Sandy Grave: Lower Big Underground Room Bottom": LocationInformation(0x02306354, "overlay_92", True), "Sandy Grave: Left Trap": LocationInformation(0x02306270, "overlay_92", True), "Buried Chamber: Central Secret": LocationInformation(0x022FEA00, "overlay_80"), @@ -329,13 +329,13 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Master's Keep: Entrance": 0x24, "Forest of Doom: Left Atrium Big Room": 0x25, "Forgotten City: Big Shaft Room Left": LocationInformation(0x02304648, "overlay_103", True), - "Nation of Fools: Left Upper Corner Room": 0x27, + "Nation of Fools: Left Upper Corner Room": LocationInformation(0x02300840, "overlay_96", True), "Dark Academy: Main Building Ghoul Room": 0x28, "13th Street: Triple Moth Room": LocationInformation(0x022FCC38, "overlay_105", True), "Tower of Death: Secret Room": 0x2A, - "Nation of Fools: Right Horizontal Room": 0x2B, - "Burnt Paradise: Left Lower Isolated Room": 0x2C, - "Burnt Paradise: Right Lower Isolated Room": 0x2D, + "Nation of Fools: Right Horizontal Room": LocationInformation(0x02300528, "overlay_96", True), + "Burnt Paradise: Left Lower Isolated Room": LocationInformation(0x02303A94, "overlay_107", True), + "Burnt Paradise: Right Lower Isolated Room": LocationInformation(0x02303D34, "overlay_107", True), "The Throne Room: Great Stairs Under Stairs": 0x2E, "13th Street: Behind Final Guard": LocationInformation(0x022FEE50, "overlay_105", True), "Buried Chamber: Right Secret": LocationInformation(0x022FEC34, "overlay_80"), @@ -344,13 +344,13 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Great Stairway: Left Loft Lower": 0x33, "Entrance: Center Hidden Room": LocationInformation(0x02304F0C, "overlay_79"), "Forgotten City: Big Shaft Room Right": LocationInformation(0x02304654, "overlay_103", True), - "Nation of Fools: Right Bottom Corner Room": 0x37, + "Nation of Fools: Right Bottom Corner Room": LocationInformation(0x02300510, "overlay_96", True), "Master's Keep: Hidden Room": 0x38, "13th Street: Three Amalaric Snipers": LocationInformation(0x022FF0F0, "overlay_105", True), "Dark Academy: Main Building Three Toads": 0x3A, - "Nation of Fools: Top Corner Room": 0x3B, - "Burnt Paradise: Left Upper Isolated Room": 0x3C, - "Burnt Paradise: Right Upper Isolated Room": 0x3D, + "Nation of Fools: Top Corner Room": LocationInformation(0x02300258, "overlay_96", True), + "Burnt Paradise: Left Upper Isolated Room": LocationInformation(0x02303908, "overlay_107", True), + "Burnt Paradise: Right Upper Isolated Room": LocationInformation(0x02303E0C, "overlay_107", True), "The Throne Room: Great Stairs Center": 0x3E, "Forgotten City: Upper Underground Square": LocationInformation(0x023048A0, "overlay_103", True), "Entrance: Double Room Lower": LocationInformation(0x0230914C, "overlay_119"), @@ -358,7 +358,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Sandy Grave: Pyramid East 1F": LocationInformation(0x02307500, "overlay_91"), "City of Haze: Register Room": LocationInformation(0x02301A3C, "overlay_93"), "City of Haze: Bar Left": LocationInformation(0x023019DC, "overlay_93"), - "Nation of Fools: Bottom-Left Room Building": 0x48, + "Nation of Fools: Bottom-Left Room Building": LocationInformation(0x02305ACC, "overlay_97"), "Sandy Grave: Pyramid 3F Lower": LocationInformation(0x0230711C, "overlay_91"), "Tower of Death: Rampart Room": 0x4A, "Entrance: Beyond Great Armor Hallway": LocationInformation(0x02305020, "overlay_79"), @@ -366,7 +366,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Forest of Doom: Right Atrium East Square Room Right": 0x4D, "Forgotten City: Outside Left": LocationInformation(0x02308864, "overlay_102"), "Sandy Grave: Upper Big Underground Top Left": LocationInformation(0x02306084, "overlay_92"), - "Burnt Paradise: Left Upper Big Corner": 0x51, + "Burnt Paradise: Left Upper Big Corner": LocationInformation(0x02304D04, "overlay_108"), "Great Stairway: Upper Grand Staircase Top Left Item": 0x53, "City of Haze: Axe Courtyard": LocationInformation(0x022F6F84, "overlay_94"), "Tower of Death: Top of the Tower": 0x55, @@ -382,7 +382,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "City of Haze: Left Plaza": LocationInformation(0x02301DE4, "overlay_93"), "Great Stairway: Underground Hidden Room": 0x66, "Sandy Grave: Lower Big Underground Room Top": LocationInformation(0x02306348, "overlay_92"), - "Nation of Fools: Upper Upside Down": 0x68, + "Nation of Fools: Upper Upside Down": LocationInformation(0x02300378, "overlay_96"), "Forgotten City: Lower Boulder Room Tunnel Alcove": LocationInformation(0x023049A8, "overlay_103"), "Dark Academy: Main Building Secret Ceiling": 0x6B, "Forest of Doom: Secret Cave Room": 0x6C, @@ -392,25 +392,25 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Tower of Death: Third Gears Room": 0x70, "Entrance: Above Metal Block Room": LocationInformation(0x02304F3C, "overlay_79", True), "Dark Academy: Main Building Entrance": 0x72, - "Burnt Paradise: Bottom Corner": 0x73, + "Burnt Paradise: Bottom Corner": LocationInformation(0x02303698, "overlay_107", True), "Forest of Doom: Left Atrium Top": 0x75, "Great Stairway: Before Keremet Room": 0x76, "Forgotten City: Lower Boulder Room Upper Alcove": LocationInformation(0x0230499C, "overlay_103", True), "Entrance: First Passage Upper Item": LocationInformation(0x02304AEC, "overlay_79"), "Great Stairway: Upper Grand Staircase Corner Nook": 0x79, - "Burnt Paradise: Bottom Right Big Room": 0x7A, + "Burnt Paradise: Bottom Right Big Room": LocationInformation(0x02305088, "overlay_108", True), "Entrance: Right Hidden Room": LocationInformation(0x022F92C0, "overlay_78"), "Entrance: First Passage Lower Item": LocationInformation(0x02304AE0, "overlay_79"), "City of Haze: Left Dance Hall Right Item": LocationInformation(0x02302784, "overlay_95"), "City of Haze: Tunnel Room Left": LocationInformation(0x02302928, "overlay_95", True), - "Burnt Paradise: Upper Left Big Room Middle Building": 0x82, + "Burnt Paradise: Upper Left Big Room Middle Building": LocationInformation(0x02304D88, "overlay_108", True), "City of Haze: Chunky Room": LocationInformation(0x023018BC, "overlay_93"), "Entrance: Pre-Behemoth Boss Room": LocationInformation(0x022F91E8, "overlay_78"), "Great Stairway: After Keremet Upper": 0x89, "Tower of Death: Gear Belt Room": 0x8A, "City of Haze: Dance Halls Basement": LocationInformation(0x023028C8, "overlay_95"), "City of Haze: Central Hallway Lower": LocationInformation(0x022F6EC4, "overlay_94"), - "Burnt Paradise: Upper Right Big Room Right Side": 0x8F, + "Burnt Paradise: Upper Right Big Room Right Side": LocationInformation(0x02304ECC, "overlay_108", True), "Buried Chamber: Central Hall": LocationInformation(0x022FE94C, "overlay_80"), "Great Stairway: Central Nook": 0x93, "Sandy Grave: Eastmost Item": LocationInformation(0x02307608, "overlay_91", True), @@ -418,7 +418,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "City of Haze: Underground Square Left": LocationInformation(0x02302C10, "overlay_95"), "Great Stairway: Upper Grand Staircase Upper Alcove": 0xA1, "City of Haze: Right Dance Hall Secret Room": LocationInformation(0x023029E8, "overlay_95", True), - "Nation of Fools: Bottom Left Medium Square On Wall": 0xA8, + "Nation of Fools: Bottom Left Medium Square On Wall": LocationInformation(0x02305B2C, "overlay_97"), "City of Haze: First Big Room": LocationInformation(0x023016C4, "overlay_93"), "Buried Chamber: Final Shaft Item": LocationInformation(0x022FEE5C, "overlay_80"), "Great Stairway: Central Upper": 0xAB, @@ -435,7 +435,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Great Stairway: Big Underground Room Hidden Item": 0xBF, "Great Stairway: Right Loft": 0xC0, "Great Stairway: Left Loft": 0xC1, - "Nation of Fools: Bottom-Right Room Left Building": 0xC2, + "Nation of Fools: Bottom-Right Room Left Building": LocationInformation(0x023059A0, "overlay_97"), "13th Street: Many Nyxes Room": LocationInformation(0x022FEF7C, "overlay_105", True), "The Throne Room: Above Throne Right": 0xC5, "City of Haze: Tunnel Room Right": LocationInformation(0x02302934, "overlay_95", True), @@ -445,33 +445,33 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Entrance: Hub Room": LocationInformation(0x02304AB0, "overlay_79", True), "Buried Chamber: Eastmost Room": LocationInformation(0x022FEE38, "overlay_80"), "Tower of Death: Hidden Room": 0xD1, - "Nation of Fools: Bottom-Right Room Middle Building": 0xD4, + "Nation of Fools: Bottom-Right Room Middle Building": LocationInformation(0x02305994, "overlay_97", True), "Forgotten City: Pyramid 5F": LocationInformation(0x02308A2C, "overlay_102", True), "13th Street: Train Room Secret Right": LocationInformation(0x022F3B8C, "overlay_104"), "City of Haze: Left Dance Hall Left Item": LocationInformation(0x02302778, "overlay_95"), "Great Stairway: Big Underground Room Lower": 0xDA, "Tower of Death: Elevator Room Middle": 0xDB, - "Nation of Fools: Crevice Item": 0xDC, + "Nation of Fools: Crevice Item": LocationInformation(0x02305778, "overlay_97", True), "City of Haze: First Passage": LocationInformation(0x02301598, "overlay_93"), "Forgotten City: Gorgon Room": LocationInformation(0x02304858, "overlay_103", True), "City of Haze: Axe Armor Item": LocationInformation(0x02301AD8, "overlay_93"), "Sandy Grave: Giant Shaft Upper Item": LocationInformation(0x02305DFC, "overlay_92"), "City of Haze: Dance Halls Vertical Hallway": LocationInformation(0x0230288C, "overlay_95"), "Tower of Death: Above Motorcycles": 0xE3, - "Nation of Fools: Top-Right Room Axe Item": 0xE4, + "Nation of Fools: Top-Right Room Axe Item": LocationInformation(0x02305808, "overlay_97", True), "Dark Academy: Main Building Secret Malphas Room": 0xE5, "13th Street: Underground Secret Room": LocationInformation(0x022FF2AC, "overlay_105", True), "Great Stairway: Big Underground Room Upper": 0xE7, "Forest of Doom: Right Atrium West Top Room": 0xE8, - "Burnt Paradise: Upper Left Big Room Left Building": 0xE9, + "Burnt Paradise: Upper Left Big Room Left Building": LocationInformation(0x02304D94, "overlay_108", True), "13th Street: Underground Demon Room": LocationInformation(0x022FF264, "overlay_105", True), "Sandy Grave: Mega Ghost Room Lower": LocationInformation(0x02307434, "overlay_91", True), "13th Street: Left Big Room": LocationInformation(0x022FD058, "overlay_105", True), "City of Haze: Bread Shelf": LocationInformation(0x02301D78, "overlay_93", True), - "Burnt Paradise: Upper Left Lower Corner Upright Building": 0xEE, + "Burnt Paradise: Upper Left Lower Corner Upright Building": LocationInformation(0x02304E0C, "overlay_108", True), "City of Haze: Underground Secret Room": LocationInformation(0x02302B50, "overlay_95", True), "Sandy Grave: Mega Ghost Room Upper": LocationInformation(0x0230741C, "overlay_91"), - "Burnt Paradise: Lower Left Big Room": 0xF1, + "Burnt Paradise: Lower Left Big Room": LocationInformation(0x02304C38, "overlay_108", True), "13th Street: Secret Floor Room": LocationInformation(0x022FF024, "overlay_105"), "City of Haze: Right Dance Hall Item": LocationInformation(0x02302964, "overlay_95"), "Tower of Death: Top Gears Room": 0xF9, @@ -480,22 +480,22 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Sandy Grave: Giant Shaft Middle Item": LocationInformation(0x02305DE4, "overlay_92", True), "Entrance: Lilith Room": LocationInformation(0x02304E28, "overlay_79"), "Sandy Grave: Right Trap": LocationInformation(0x0230627C, "overlay_92", True), - "Nation of Fools: Left Bottom Corner Room": 0x104, + "Nation of Fools: Left Bottom Corner Room": LocationInformation(0x02300750, "overlay_96", True), "Forgotten City: Lower Underground Square Upper Item": LocationInformation(0x02304A80, "overlay_103"), "City of Haze: Center Room Top": LocationInformation(0x02301C40, "overlay_93"), "Great Stairway: Upper Grand Staircase Middle Alcove": 0x10F, "Buried Chamber: Right Shaft Top": LocationInformation(0x022FEDA8, "overlay_80", True), "Sandy Grave: Pyramid 3F Upper": LocationInformation(0x02307128, "overlay_91"), - "Nation of Fools: Bottom Right Medium Square On Wall": 0x114, + "Nation of Fools: Bottom Right Medium Square On Wall": LocationInformation(0x023058F8, "overlay_97", True), "Sandy Grave: Lonely Mimic Alcove": LocationInformation(0x023064A4, "overlay_92", True), "13th Street: Near Big Room": LocationInformation(0x022FCE18, "overlay_105", True), "Forgotten City: Pyramid East 3F": LocationInformation(0x02308C9C, "overlay_102", True), "Great Stairway: Upper Grand Staircase Lower Alcove": 0x11B, - "Nation of Fools: Right Vertical Room": 0x11C, + "Nation of Fools: Right Vertical Room": LocationInformation(0x02300450, "overlay_96", True), "Sandy Grave: Fleaman Pit": LocationInformation(0x023073A4, "overlay_91"), - "Nation of Fools: Top-Right Room Bottom Item": 0x11F, + "Nation of Fools: Top-Right Room Bottom Item": LocationInformation(0x023057F0, "overlay_97"), "Forgotten City: Pyramid East 5F": LocationInformation(0x02308BC4, "overlay_102", True), - "Burnt Paradise: Lower Vertical Hall": 0x123, + "Burnt Paradise: Lower Vertical Hall": LocationInformation(0x02304B60, "overlay_108", True), "Buried Chamber: West Shaft": LocationInformation(0x022FE748, "overlay_80", True), "Great Stairway: Lower Grand Staircase Middle Alcove": 0x127, "Entrance: Behemoth Chase Room": LocationInformation(0x02304DA4, "overlay_79"), @@ -503,33 +503,33 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Tower of Death: Lower Passage Hidden Item": 0x131, "Dark Academy: West Building Breakable Wall": 0x134, "Forgotten City: Armored Fleaman Pit": LocationInformation(0x02308C24, "overlay_102", True), - "Nation of Fools: Top-Left Room Right Building": 0x13F, + "Nation of Fools: Top-Left Room Right Building": LocationInformation(0x02305BBC, "overlay_97", True), "Forest of Doom: Left Atrium Breakable Wall": 0x140, "Sandy Grave: Giant Shaft Low Item": LocationInformation(0x02305DF0, "overlay_92", True), "The Throne Room: Great Stairs Hidden": 0x145, - "Nation of Fools: Top-Right Room Breakable Wall": 0x146, - "Burnt Paradise: Bottom Right Breakable Wall": 0x147, - "Burnt Paradise: Right Upper Big Corner On Wall": 0x148, - "Nation of Fools: Right Center Room": 0x149, - "Burnt Paradise: Left Big Corner": 0x14A, + "Nation of Fools: Top-Right Room Breakable Wall": LocationInformation(0x023057FC, "overlay_97"), + "Burnt Paradise: Bottom Right Breakable Wall": LocationInformation(0x02303848, "overlay_107"), + "Burnt Paradise: Right Upper Big Corner On Wall": LocationInformation(0x02304F2C, "overlay_108", True), + "Nation of Fools: Right Center Room": LocationInformation(0x022F6A5C, "overlay_98", True), + "Burnt Paradise: Left Big Corner": LocationInformation(0x02304B90, "overlay_108"), "Buried Chamber: Hidden Wall Item": LocationInformation(0x022FECA0, "overlay_80"), "13th Street: Bakery Middle": 0x14D, "City of Haze: Cart Secret Right": LocationInformation(0x022F7014, "overlay_94"), - "13th Street: Right Dance Hall Top Right": 0x150, - "13th Street: Bakery Left": 0x152, + "13th Street: Right Dance Hall Top Right": LocationInformation(0x022FED84, "overlay_105", True), + "13th Street: Bakery Left": LocationInformation(0x022FCB48, "overlay_105"), "13th Street: Train Room Secret Left": LocationInformation(0x022F3B80, "overlay_104"), "City of Haze: Bar Right": LocationInformation(0x023019E8, "overlay_93"), "13th Street: Square Room": LocationInformation(0x022FCA4C, "overlay_105", True), - "13th Street: Bakery Right": 0x156, - "Burnt Paradise: Bottom Right Corner Room": 0x157, - "Nation of Fools: Left Center Room": 0x15F, + "13th Street: Bakery Right": LocationInformation(0x022FCB60, "overlay_105"), + "Burnt Paradise: Bottom Right Corner Room": LocationInformation(0x023050A0, "overlay_108", True), + "Nation of Fools: Left Center Room": LocationInformation(0x022F6A74, "overlay_98", True), "Dark Academy: Main Building Square Room Breakable Wall": 0x160, - "Nation of Fools: Upside Down Building": 0x161, + "Nation of Fools: Upside Down Building": LocationInformation(0x02305C64, "overlay_97", True), "Dark Academy: Main Building Minotaur Room": 0x163, "Dark Academy: West Building Top Floor Right": 0x164, - "Nation of Fools: Secret Room": 0x166, + "Nation of Fools: Secret Room": LocationInformation(0x023003D8, "overlay_96", True), "Forest of Doom: Left Atrium Middle Room": 0x167, - "Burnt Paradise: Upper Right Big Room": 0x16B, + "Burnt Paradise: Upper Right Big Room": LocationInformation(0x02304ED8, "overlay_108", True), "Forest of Doom: Near Cave Save Breakable Wall": 0x16C, "Sandy Grave: Pyramid East 5F": LocationInformation(0x02307344, "overlay_91"), "Nest of Evil: First Item": 0x175, From f367a20a29b1b548322b76fdcc25922ede4702ba Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 29 Apr 2026 08:43:26 -0500 Subject: [PATCH 46/58] Simple areas --- worlds/cv_por/static_location_data.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index 09572532c096..37ceb416b08a 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -313,7 +313,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Nation of Fools: Legion Arena": LocationInformation(0x022F6A98, "overlay_98", True), "Forest of Doom: Post-Boss Room": 0x11, "Dark Academy: Right Building Center Room": 0x12, - "The Throne Room: Above Throne Left": 0x13, + "The Throne Room: Above Throne Left": LocationInformation(0x022F19F8, "overlay_87"), "Sandy Grave: Boulder Room Tunnel": LocationInformation(0x023064EC, "overlay_92"), "City of Haze: Before Cart Room": LocationInformation(0x022F6D98, "overlay_94"), "City of Haze: Underground Square Right": LocationInformation(0x02302B68, "overlay_95"), @@ -336,7 +336,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Nation of Fools: Right Horizontal Room": LocationInformation(0x02300528, "overlay_96", True), "Burnt Paradise: Left Lower Isolated Room": LocationInformation(0x02303A94, "overlay_107", True), "Burnt Paradise: Right Lower Isolated Room": LocationInformation(0x02303D34, "overlay_107", True), - "The Throne Room: Great Stairs Under Stairs": 0x2E, + "The Throne Room: Great Stairs Under Stairs": LocationInformation(0x022F1B54, "overlay_87", True), "13th Street: Behind Final Guard": LocationInformation(0x022FEE50, "overlay_105", True), "Buried Chamber: Right Secret": LocationInformation(0x022FEC34, "overlay_80"), "Sandy Grave: Bottom Right Room": LocationInformation(0x02306540, "overlay_92"), @@ -351,7 +351,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Nation of Fools: Top Corner Room": LocationInformation(0x02300258, "overlay_96", True), "Burnt Paradise: Left Upper Isolated Room": LocationInformation(0x02303908, "overlay_107", True), "Burnt Paradise: Right Upper Isolated Room": LocationInformation(0x02303E0C, "overlay_107", True), - "The Throne Room: Great Stairs Center": 0x3E, + "The Throne Room: Great Stairs Center": LocationInformation(0x022F1B60, "overlay_87", True), "Forgotten City: Upper Underground Square": LocationInformation(0x023048A0, "overlay_103", True), "Entrance: Double Room Lower": LocationInformation(0x0230914C, "overlay_119"), "Buried Chamber: Westmost Room": LocationInformation(0x022FE700, "overlay_80"), @@ -386,7 +386,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Forgotten City: Lower Boulder Room Tunnel Alcove": LocationInformation(0x023049A8, "overlay_103"), "Dark Academy: Main Building Secret Ceiling": 0x6B, "Forest of Doom: Secret Cave Room": 0x6C, - "Nest of Evil: Doppelganger Reward": 0x6D, + "Nest of Evil: Doppelganger Reward": LocationInformation(0x022EE9FC, "overlay_113"), "City of Haze: Center Room Lower": LocationInformation(0x02301C4C, "overlay_93"), "Sandy Grave: On Statue": LocationInformation(0x02307068, "overlay_91", True), "Tower of Death: Third Gears Room": 0x70, @@ -437,7 +437,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Great Stairway: Left Loft": 0xC1, "Nation of Fools: Bottom-Right Room Left Building": LocationInformation(0x023059A0, "overlay_97"), "13th Street: Many Nyxes Room": LocationInformation(0x022FEF7C, "overlay_105", True), - "The Throne Room: Above Throne Right": 0xC5, + "The Throne Room: Above Throne Right": LocationInformation(0x022F1A04, "overlay_87", True), "City of Haze: Tunnel Room Right": LocationInformation(0x02302934, "overlay_95", True), "Great Stairway: Lower Grand Staircase Upper Alcove": 0xCA, "Great Stairway: Underground East Room": 0xCB, @@ -506,7 +506,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Nation of Fools: Top-Left Room Right Building": LocationInformation(0x02305BBC, "overlay_97", True), "Forest of Doom: Left Atrium Breakable Wall": 0x140, "Sandy Grave: Giant Shaft Low Item": LocationInformation(0x02305DF0, "overlay_92", True), - "The Throne Room: Great Stairs Hidden": 0x145, + "The Throne Room: Great Stairs Hidden": LocationInformation(0x022F1B78, "overlay_87"), "Nation of Fools: Top-Right Room Breakable Wall": LocationInformation(0x023057FC, "overlay_97"), "Burnt Paradise: Bottom Right Breakable Wall": LocationInformation(0x02303848, "overlay_107"), "Burnt Paradise: Right Upper Big Corner On Wall": LocationInformation(0x02304F2C, "overlay_108", True), @@ -532,12 +532,12 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Burnt Paradise: Upper Right Big Room": LocationInformation(0x02304ED8, "overlay_108", True), "Forest of Doom: Near Cave Save Breakable Wall": 0x16C, "Sandy Grave: Pyramid East 5F": LocationInformation(0x02307344, "overlay_91"), - "Nest of Evil: First Item": 0x175, - "Nest of Evil: Second Item": 0x176, + "Nest of Evil: First Item": LocationInformation(0x022EE060, "overlay_113", True), + "Nest of Evil: Second Item": LocationInformation(0x022EE864, "overlay_113", True), "Forest of Doom: Left Atrium Upper Room": 0x177, "Great Stairway: Connector Pipe Left": 0x180, "Great Stairway: Connector Pipe Right": 0x181, - "The Throne Room: Great Stairs Left": 0x182, + "The Throne Room: Great Stairs Left": LocationInformation(0x022F1B6C, "overlay_87", True), "13th Street: Right Dance Hall Top Left": LocationInformation(0x022FED78, "overlay_105", True), "Dark Academy: West Building Top Floor Left": 0x184, "Forgotten City: Pyramid East 1F": LocationInformation(0x02308CF0, "overlay_102", True), From 923c4f2e735e1bba913fc3d51d975d77b82a13c5 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 29 Apr 2026 09:10:38 -0500 Subject: [PATCH 47/58] Forest locs --- worlds/cv_por/static_location_data.py | 100 +++++++++++++------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index 37ceb416b08a..f03ad65bd3de 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -305,14 +305,14 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "City of Haze: Post-Boss Room": LocationInformation(0x02302A18, "overlay_95"), "City of Haze: Heavy Cart Item": LocationInformation(0x022F6F3C, "overlay_94"), "Sandy Grave: Pyramid 5F Nook": LocationInformation(0x023071AC, "overlay_91"), - "Great Stairway: After Keremet Lower": 0x09, + "Great Stairway: After Keremet Lower": LocationInformation(0x022FAD60, "overlay_82"), "Forgotten City: Pyramid 1F Bricks": LocationInformation(0x02308924, "overlay_102"), "Nation of Fools: Central Top Shaft": LocationInformation(0x022F6948, "overlay_98"), "Entrance: Above Hub": LocationInformation(0x02304A8C, "overlay_79"), "Entrance: Double Room Upper": LocationInformation(0x02309140, "overlay_119"), "Nation of Fools: Legion Arena": LocationInformation(0x022F6A98, "overlay_98", True), - "Forest of Doom: Post-Boss Room": 0x11, - "Dark Academy: Right Building Center Room": 0x12, + "Forest of Doom: Post-Boss Room": LocationInformation(0x022FB1B0, "overlay_99"), + "Dark Academy: Right Building Center Room": LocationInformation(0x022F2A5C, "overlay_112"), "The Throne Room: Above Throne Left": LocationInformation(0x022F19F8, "overlay_87"), "Sandy Grave: Boulder Room Tunnel": LocationInformation(0x023064EC, "overlay_92"), "City of Haze: Before Cart Room": LocationInformation(0x022F6D98, "overlay_94"), @@ -325,12 +325,12 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Buried Chamber: Central Secret": LocationInformation(0x022FEA00, "overlay_80"), "Sandy Grave: Sandworm Stomach": LocationInformation(0x02307674, "overlay_91"), "Tower of Death: Second Gears Room Left": 0x22, - "Great Stairway: Lower Grand Staircase Corner Nook": 0x23, + "Great Stairway: Lower Grand Staircase Corner Nook": LocationInformation(0x022FC944, "overlay_83"), "Master's Keep: Entrance": 0x24, - "Forest of Doom: Left Atrium Big Room": 0x25, + "Forest of Doom: Left Atrium Big Room": LocationInformation(0x02302C50, "overlay_100"), "Forgotten City: Big Shaft Room Left": LocationInformation(0x02304648, "overlay_103", True), "Nation of Fools: Left Upper Corner Room": LocationInformation(0x02300840, "overlay_96", True), - "Dark Academy: Main Building Ghoul Room": 0x28, + "Dark Academy: Main Building Ghoul Room": LocationInformation(0x022F84F8, "overlay_111", True), "13th Street: Triple Moth Room": LocationInformation(0x022FCC38, "overlay_105", True), "Tower of Death: Secret Room": 0x2A, "Nation of Fools: Right Horizontal Room": LocationInformation(0x02300528, "overlay_96", True), @@ -341,13 +341,13 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Buried Chamber: Right Secret": LocationInformation(0x022FEC34, "overlay_80"), "Sandy Grave: Bottom Right Room": LocationInformation(0x02306540, "overlay_92"), "Tower of Death: Passage Hidden Item": 0x32, - "Great Stairway: Left Loft Lower": 0x33, + "Great Stairway: Left Loft Lower": LocationInformation(0x022F8798, "overlay_84", True), "Entrance: Center Hidden Room": LocationInformation(0x02304F0C, "overlay_79"), "Forgotten City: Big Shaft Room Right": LocationInformation(0x02304654, "overlay_103", True), "Nation of Fools: Right Bottom Corner Room": LocationInformation(0x02300510, "overlay_96", True), "Master's Keep: Hidden Room": 0x38, "13th Street: Three Amalaric Snipers": LocationInformation(0x022FF0F0, "overlay_105", True), - "Dark Academy: Main Building Three Toads": 0x3A, + "Dark Academy: Main Building Three Toads": LocationInformation(0x022F8060, "overlay_111"), "Nation of Fools: Top Corner Room": LocationInformation(0x02300258, "overlay_96", True), "Burnt Paradise: Left Upper Isolated Room": LocationInformation(0x02303908, "overlay_107", True), "Burnt Paradise: Right Upper Isolated Room": LocationInformation(0x02303E0C, "overlay_107", True), @@ -363,14 +363,14 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Tower of Death: Rampart Room": 0x4A, "Entrance: Beyond Great Armor Hallway": LocationInformation(0x02305020, "overlay_79"), "Tower of Death: Elevator Room Lower": 0x4C, - "Forest of Doom: Right Atrium East Square Room Right": 0x4D, + "Forest of Doom: Right Atrium East Square Room Right": LocationInformation(0x022FDF10, "overlay_101"), "Forgotten City: Outside Left": LocationInformation(0x02308864, "overlay_102"), "Sandy Grave: Upper Big Underground Top Left": LocationInformation(0x02306084, "overlay_92"), "Burnt Paradise: Left Upper Big Corner": LocationInformation(0x02304D04, "overlay_108"), - "Great Stairway: Upper Grand Staircase Top Left Item": 0x53, + "Great Stairway: Upper Grand Staircase Top Left Item": LocationInformation(0x022FCB0C, "overlay_83"), "City of Haze: Axe Courtyard": LocationInformation(0x022F6F84, "overlay_94"), "Tower of Death: Top of the Tower": 0x55, - "Great Stairway: Upper Underground Room": 0x56, + "Great Stairway: Upper Underground Room": LocationInformation(0x022FAB38, "overlay_82"), "Entrance: Drawbridge Upper Item": LocationInformation(0x022F9128, "overlay_78"), "Entrance: Statue Room Upper": LocationInformation(0x02304E70, "overlay_79"), "Tower of Death: Second Gears Room Right": 0x59, @@ -380,24 +380,24 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Buried Chamber: Right Shade Hall": LocationInformation(0x022FED6C, "overlay_80"), "City of Haze: Right Corner Room": LocationInformation(0x02301AA8, "overlay_93"), "City of Haze: Left Plaza": LocationInformation(0x02301DE4, "overlay_93"), - "Great Stairway: Underground Hidden Room": 0x66, + "Great Stairway: Underground Hidden Room": LocationInformation(0x022FACF4, "overlay_82"), "Sandy Grave: Lower Big Underground Room Top": LocationInformation(0x02306348, "overlay_92"), "Nation of Fools: Upper Upside Down": LocationInformation(0x02300378, "overlay_96"), "Forgotten City: Lower Boulder Room Tunnel Alcove": LocationInformation(0x023049A8, "overlay_103"), - "Dark Academy: Main Building Secret Ceiling": 0x6B, - "Forest of Doom: Secret Cave Room": 0x6C, + "Dark Academy: Main Building Secret Ceiling": LocationInformation(0x022F8420, "overlay_111"), + "Forest of Doom: Secret Cave Room": LocationInformation(0x022FDEB0, "overlay_101"), "Nest of Evil: Doppelganger Reward": LocationInformation(0x022EE9FC, "overlay_113"), "City of Haze: Center Room Lower": LocationInformation(0x02301C4C, "overlay_93"), "Sandy Grave: On Statue": LocationInformation(0x02307068, "overlay_91", True), "Tower of Death: Third Gears Room": 0x70, "Entrance: Above Metal Block Room": LocationInformation(0x02304F3C, "overlay_79", True), - "Dark Academy: Main Building Entrance": 0x72, + "Dark Academy: Main Building Entrance": LocationInformation(0x022F8660, "overlay_111", True), "Burnt Paradise: Bottom Corner": LocationInformation(0x02303698, "overlay_107", True), - "Forest of Doom: Left Atrium Top": 0x75, - "Great Stairway: Before Keremet Room": 0x76, + "Forest of Doom: Left Atrium Top": LocationInformation(0x023027C4, "overlay_100"), + "Great Stairway: Before Keremet Room": LocationInformation(0x022FA864, "overlay_81", True), "Forgotten City: Lower Boulder Room Upper Alcove": LocationInformation(0x0230499C, "overlay_103", True), "Entrance: First Passage Upper Item": LocationInformation(0x02304AEC, "overlay_79"), - "Great Stairway: Upper Grand Staircase Corner Nook": 0x79, + "Great Stairway: Upper Grand Staircase Corner Nook": LocationInformation(0x022FCB3C, "overlay_83"), "Burnt Paradise: Bottom Right Big Room": LocationInformation(0x02305088, "overlay_108", True), "Entrance: Right Hidden Room": LocationInformation(0x022F92C0, "overlay_78"), "Entrance: First Passage Lower Item": LocationInformation(0x02304AE0, "overlay_79"), @@ -406,41 +406,41 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Burnt Paradise: Upper Left Big Room Middle Building": LocationInformation(0x02304D88, "overlay_108", True), "City of Haze: Chunky Room": LocationInformation(0x023018BC, "overlay_93"), "Entrance: Pre-Behemoth Boss Room": LocationInformation(0x022F91E8, "overlay_78"), - "Great Stairway: After Keremet Upper": 0x89, + "Great Stairway: After Keremet Upper": LocationInformation(0x022FAD6C, "overlay_82", True), "Tower of Death: Gear Belt Room": 0x8A, "City of Haze: Dance Halls Basement": LocationInformation(0x023028C8, "overlay_95"), "City of Haze: Central Hallway Lower": LocationInformation(0x022F6EC4, "overlay_94"), "Burnt Paradise: Upper Right Big Room Right Side": LocationInformation(0x02304ECC, "overlay_108", True), "Buried Chamber: Central Hall": LocationInformation(0x022FE94C, "overlay_80"), - "Great Stairway: Central Nook": 0x93, + "Great Stairway: Central Nook": LocationInformation(0x022F881C, "overlay_84", True), "Sandy Grave: Eastmost Item": LocationInformation(0x02307608, "overlay_91", True), "Sandy Grave: Behind Bricks": LocationInformation(0x02307074, "overlay_91"), "City of Haze: Underground Square Left": LocationInformation(0x02302C10, "overlay_95"), - "Great Stairway: Upper Grand Staircase Upper Alcove": 0xA1, + "Great Stairway: Upper Grand Staircase Upper Alcove": LocationInformation(0x022FCB30, "overlay_83"), "City of Haze: Right Dance Hall Secret Room": LocationInformation(0x023029E8, "overlay_95", True), "Nation of Fools: Bottom Left Medium Square On Wall": LocationInformation(0x02305B2C, "overlay_97"), "City of Haze: First Big Room": LocationInformation(0x023016C4, "overlay_93"), "Buried Chamber: Final Shaft Item": LocationInformation(0x022FEE5C, "overlay_80"), - "Great Stairway: Central Upper": 0xAB, - "Forest of Doom: Right Atrium East Top Room": 0xAC, - "Dark Academy: Right Building Top Floor": 0xAD, + "Great Stairway: Central Upper": LocationInformation(0x022F8828, "overlay_84", True), + "Forest of Doom: Right Atrium East Top Room": LocationInformation(0x022FDDF0, "overlay_101"), + "Dark Academy: Right Building Top Floor": LocationInformation(0x022F29E4, "overlay_112", True), "Sandy Grave: Boulder Room Corner Alcove": LocationInformation(0x023064F8, "overlay_92"), "Forgotten City: Post-Boss Item": LocationInformation(0x023046B4, "overlay_103"), "City of Haze: Cart Secret Left": LocationInformation(0x022F7008, "overlay_94"), "City of Haze: Winding Rooms": LocationInformation(0x023017C0, "overlay_93"), - "Great Stairway: Lower Grand Staircase Lower Alcove": 0xB4, - "Forest of Doom: Right Atrium East Square Room Left": 0xB5, + "Great Stairway: Lower Grand Staircase Lower Alcove": LocationInformation(0x022FC950, "overlay_83", True), + "Forest of Doom: Right Atrium East Square Room Left": LocationInformation(0x022FDF1C, "overlay_101"), "Forgotten City: Bottom Chamber": LocationInformation(0x02304D50, "overlay_103"), "City of Haze: Meat Room": LocationInformation(0x02301628, "overlay_93"), - "Great Stairway: Big Underground Room Hidden Item": 0xBF, - "Great Stairway: Right Loft": 0xC0, - "Great Stairway: Left Loft": 0xC1, + "Great Stairway: Big Underground Room Hidden Item": LocationInformation(0x022FAE38, "overlay_82"), + "Great Stairway: Right Loft": LocationInformation(0x022F893C, "overlay_84", True), + "Great Stairway: Left Loft": LocationInformation(0x022F878C, "overlay_84", True), "Nation of Fools: Bottom-Right Room Left Building": LocationInformation(0x023059A0, "overlay_97"), "13th Street: Many Nyxes Room": LocationInformation(0x022FEF7C, "overlay_105", True), "The Throne Room: Above Throne Right": LocationInformation(0x022F1A04, "overlay_87", True), "City of Haze: Tunnel Room Right": LocationInformation(0x02302934, "overlay_95", True), - "Great Stairway: Lower Grand Staircase Upper Alcove": 0xCA, - "Great Stairway: Underground East Room": 0xCB, + "Great Stairway: Lower Grand Staircase Upper Alcove": LocationInformation(0x022FC968, "overlay_83"), + "Great Stairway: Underground East Room": LocationInformation(0x022FACA0, "overlay_82", True), "Tower of Death: Elevator Room Top": 0xCC, "Entrance: Hub Room": LocationInformation(0x02304AB0, "overlay_79", True), "Buried Chamber: Eastmost Room": LocationInformation(0x022FEE38, "overlay_80"), @@ -449,7 +449,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Forgotten City: Pyramid 5F": LocationInformation(0x02308A2C, "overlay_102", True), "13th Street: Train Room Secret Right": LocationInformation(0x022F3B8C, "overlay_104"), "City of Haze: Left Dance Hall Left Item": LocationInformation(0x02302778, "overlay_95"), - "Great Stairway: Big Underground Room Lower": 0xDA, + "Great Stairway: Big Underground Room Lower": LocationInformation(0x022FAE20, "overlay_82"), "Tower of Death: Elevator Room Middle": 0xDB, "Nation of Fools: Crevice Item": LocationInformation(0x02305778, "overlay_97", True), "City of Haze: First Passage": LocationInformation(0x02301598, "overlay_93"), @@ -459,10 +459,10 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "City of Haze: Dance Halls Vertical Hallway": LocationInformation(0x0230288C, "overlay_95"), "Tower of Death: Above Motorcycles": 0xE3, "Nation of Fools: Top-Right Room Axe Item": LocationInformation(0x02305808, "overlay_97", True), - "Dark Academy: Main Building Secret Malphas Room": 0xE5, + "Dark Academy: Main Building Secret Malphas Room": LocationInformation(0x022F83B4, "overlay_111", True), "13th Street: Underground Secret Room": LocationInformation(0x022FF2AC, "overlay_105", True), - "Great Stairway: Big Underground Room Upper": 0xE7, - "Forest of Doom: Right Atrium West Top Room": 0xE8, + "Great Stairway: Big Underground Room Upper": LocationInformation(0x022FAE2C, "overlay_82"), + "Forest of Doom: Right Atrium West Top Room": LocationInformation(0x022FDB44, "overlay_101"), "Burnt Paradise: Upper Left Big Room Left Building": LocationInformation(0x02304D94, "overlay_108", True), "13th Street: Underground Demon Room": LocationInformation(0x022FF264, "overlay_105", True), "Sandy Grave: Mega Ghost Room Lower": LocationInformation(0x02307434, "overlay_91", True), @@ -483,28 +483,28 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Nation of Fools: Left Bottom Corner Room": LocationInformation(0x02300750, "overlay_96", True), "Forgotten City: Lower Underground Square Upper Item": LocationInformation(0x02304A80, "overlay_103"), "City of Haze: Center Room Top": LocationInformation(0x02301C40, "overlay_93"), - "Great Stairway: Upper Grand Staircase Middle Alcove": 0x10F, + "Great Stairway: Upper Grand Staircase Middle Alcove": LocationInformation(0x022FCB24, "overlay_83", True), "Buried Chamber: Right Shaft Top": LocationInformation(0x022FEDA8, "overlay_80", True), "Sandy Grave: Pyramid 3F Upper": LocationInformation(0x02307128, "overlay_91"), "Nation of Fools: Bottom Right Medium Square On Wall": LocationInformation(0x023058F8, "overlay_97", True), "Sandy Grave: Lonely Mimic Alcove": LocationInformation(0x023064A4, "overlay_92", True), "13th Street: Near Big Room": LocationInformation(0x022FCE18, "overlay_105", True), "Forgotten City: Pyramid East 3F": LocationInformation(0x02308C9C, "overlay_102", True), - "Great Stairway: Upper Grand Staircase Lower Alcove": 0x11B, + "Great Stairway: Upper Grand Staircase Lower Alcove": LocationInformation(0x022FCB18, "overlay_83", True), "Nation of Fools: Right Vertical Room": LocationInformation(0x02300450, "overlay_96", True), "Sandy Grave: Fleaman Pit": LocationInformation(0x023073A4, "overlay_91"), "Nation of Fools: Top-Right Room Bottom Item": LocationInformation(0x023057F0, "overlay_97"), "Forgotten City: Pyramid East 5F": LocationInformation(0x02308BC4, "overlay_102", True), "Burnt Paradise: Lower Vertical Hall": LocationInformation(0x02304B60, "overlay_108", True), "Buried Chamber: West Shaft": LocationInformation(0x022FE748, "overlay_80", True), - "Great Stairway: Lower Grand Staircase Middle Alcove": 0x127, + "Great Stairway: Lower Grand Staircase Middle Alcove": LocationInformation(0x022FC95C, "overlay_83", True), "Entrance: Behemoth Chase Room": LocationInformation(0x02304DA4, "overlay_79"), "Entrance: First Passage Hidden Wall": LocationInformation(0x02304B34, "overlay_79"), "Tower of Death: Lower Passage Hidden Item": 0x131, - "Dark Academy: West Building Breakable Wall": 0x134, + "Dark Academy: West Building Breakable Wall": LocationInformation(0x022F6084, "overlay_109"), "Forgotten City: Armored Fleaman Pit": LocationInformation(0x02308C24, "overlay_102", True), "Nation of Fools: Top-Left Room Right Building": LocationInformation(0x02305BBC, "overlay_97", True), - "Forest of Doom: Left Atrium Breakable Wall": 0x140, + "Forest of Doom: Left Atrium Breakable Wall": LocationInformation(0x02302A40, "overlay_100"), "Sandy Grave: Giant Shaft Low Item": LocationInformation(0x02305DF0, "overlay_92", True), "The Throne Room: Great Stairs Hidden": LocationInformation(0x022F1B78, "overlay_87"), "Nation of Fools: Top-Right Room Breakable Wall": LocationInformation(0x023057FC, "overlay_97"), @@ -513,7 +513,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Nation of Fools: Right Center Room": LocationInformation(0x022F6A5C, "overlay_98", True), "Burnt Paradise: Left Big Corner": LocationInformation(0x02304B90, "overlay_108"), "Buried Chamber: Hidden Wall Item": LocationInformation(0x022FECA0, "overlay_80"), - "13th Street: Bakery Middle": 0x14D, + "13th Street: Bakery Middle": LocationInformation(0x022FCB54, "overlay_105"), "City of Haze: Cart Secret Right": LocationInformation(0x022F7014, "overlay_94"), "13th Street: Right Dance Hall Top Right": LocationInformation(0x022FED84, "overlay_105", True), "13th Street: Bakery Left": LocationInformation(0x022FCB48, "overlay_105"), @@ -523,23 +523,23 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "13th Street: Bakery Right": LocationInformation(0x022FCB60, "overlay_105"), "Burnt Paradise: Bottom Right Corner Room": LocationInformation(0x023050A0, "overlay_108", True), "Nation of Fools: Left Center Room": LocationInformation(0x022F6A74, "overlay_98", True), - "Dark Academy: Main Building Square Room Breakable Wall": 0x160, + "Dark Academy: Main Building Square Room Breakable Wall": LocationInformation(0x022F8198, "overlay_111"), "Nation of Fools: Upside Down Building": LocationInformation(0x02305C64, "overlay_97", True), - "Dark Academy: Main Building Minotaur Room": 0x163, - "Dark Academy: West Building Top Floor Right": 0x164, + "Dark Academy: Main Building Minotaur Room": LocationInformation(0x022F8564, "overlay_111", True), + "Dark Academy: West Building Top Floor Right": LocationInformation(0x022F60F0, "overlay_109", True), "Nation of Fools: Secret Room": LocationInformation(0x023003D8, "overlay_96", True), - "Forest of Doom: Left Atrium Middle Room": 0x167, + "Forest of Doom: Left Atrium Middle Room": LocationInformation(0x023028D8, "overlay_100", True), "Burnt Paradise: Upper Right Big Room": LocationInformation(0x02304ED8, "overlay_108", True), - "Forest of Doom: Near Cave Save Breakable Wall": 0x16C, + "Forest of Doom: Near Cave Save Breakable Wall": LocationInformation(0x022FB120, "overlay_99"), "Sandy Grave: Pyramid East 5F": LocationInformation(0x02307344, "overlay_91"), "Nest of Evil: First Item": LocationInformation(0x022EE060, "overlay_113", True), "Nest of Evil: Second Item": LocationInformation(0x022EE864, "overlay_113", True), - "Forest of Doom: Left Atrium Upper Room": 0x177, - "Great Stairway: Connector Pipe Left": 0x180, - "Great Stairway: Connector Pipe Right": 0x181, + "Forest of Doom: Left Atrium Upper Room": LocationInformation(0x0230280C, "overlay_100"), + "Great Stairway: Connector Pipe Left": LocationInformation(0x022F8A80, "overlay_84", True), + "Great Stairway: Connector Pipe Right": LocationInformation(0x022F8A8C, "overlay_84", True), "The Throne Room: Great Stairs Left": LocationInformation(0x022F1B6C, "overlay_87", True), "13th Street: Right Dance Hall Top Left": LocationInformation(0x022FED78, "overlay_105", True), - "Dark Academy: West Building Top Floor Left": 0x184, + "Dark Academy: West Building Top Floor Left": LocationInformation(0x022F60E4, "overlay_109", True), "Forgotten City: Pyramid East 1F": LocationInformation(0x02308CF0, "overlay_102", True), "Forgotten City: Lower Underground Square Lower Item": LocationInformation(0x02304A8C, "overlay_103", True), "Tower of Death: Stella Item": 0x187, From 08843145c63ece4c2b4af28068352b3c5555ac7b Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 29 Apr 2026 09:21:08 -0500 Subject: [PATCH 48/58] Finish getting loc daa --- worlds/cv_por/static_location_data.py | 44 +++++++++++++-------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index f03ad65bd3de..409ef41c2abd 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -324,15 +324,15 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Sandy Grave: Left Trap": LocationInformation(0x02306270, "overlay_92", True), "Buried Chamber: Central Secret": LocationInformation(0x022FEA00, "overlay_80"), "Sandy Grave: Sandworm Stomach": LocationInformation(0x02307674, "overlay_91"), - "Tower of Death: Second Gears Room Left": 0x22, + "Tower of Death: Second Gears Room Left": LocationInformation(0x02306634, "overlay_85"), "Great Stairway: Lower Grand Staircase Corner Nook": LocationInformation(0x022FC944, "overlay_83"), - "Master's Keep: Entrance": 0x24, + "Master's Keep: Entrance": LocationInformation(0x022FBECC, "overlay_88"), "Forest of Doom: Left Atrium Big Room": LocationInformation(0x02302C50, "overlay_100"), "Forgotten City: Big Shaft Room Left": LocationInformation(0x02304648, "overlay_103", True), "Nation of Fools: Left Upper Corner Room": LocationInformation(0x02300840, "overlay_96", True), "Dark Academy: Main Building Ghoul Room": LocationInformation(0x022F84F8, "overlay_111", True), "13th Street: Triple Moth Room": LocationInformation(0x022FCC38, "overlay_105", True), - "Tower of Death: Secret Room": 0x2A, + "Tower of Death: Secret Room": LocationInformation(0x0230673C, "overlay_85", True), "Nation of Fools: Right Horizontal Room": LocationInformation(0x02300528, "overlay_96", True), "Burnt Paradise: Left Lower Isolated Room": LocationInformation(0x02303A94, "overlay_107", True), "Burnt Paradise: Right Lower Isolated Room": LocationInformation(0x02303D34, "overlay_107", True), @@ -340,12 +340,12 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "13th Street: Behind Final Guard": LocationInformation(0x022FEE50, "overlay_105", True), "Buried Chamber: Right Secret": LocationInformation(0x022FEC34, "overlay_80"), "Sandy Grave: Bottom Right Room": LocationInformation(0x02306540, "overlay_92"), - "Tower of Death: Passage Hidden Item": 0x32, + "Tower of Death: Passage Hidden Item": LocationInformation(0x0230661C, "overlay_85"), "Great Stairway: Left Loft Lower": LocationInformation(0x022F8798, "overlay_84", True), "Entrance: Center Hidden Room": LocationInformation(0x02304F0C, "overlay_79"), "Forgotten City: Big Shaft Room Right": LocationInformation(0x02304654, "overlay_103", True), "Nation of Fools: Right Bottom Corner Room": LocationInformation(0x02300510, "overlay_96", True), - "Master's Keep: Hidden Room": 0x38, + "Master's Keep: Hidden Room": LocationInformation(0x022FBC14, "overlay_88"), "13th Street: Three Amalaric Snipers": LocationInformation(0x022FF0F0, "overlay_105", True), "Dark Academy: Main Building Three Toads": LocationInformation(0x022F8060, "overlay_111"), "Nation of Fools: Top Corner Room": LocationInformation(0x02300258, "overlay_96", True), @@ -360,22 +360,22 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "City of Haze: Bar Left": LocationInformation(0x023019DC, "overlay_93"), "Nation of Fools: Bottom-Left Room Building": LocationInformation(0x02305ACC, "overlay_97"), "Sandy Grave: Pyramid 3F Lower": LocationInformation(0x0230711C, "overlay_91"), - "Tower of Death: Rampart Room": 0x4A, + "Tower of Death: Rampart Room": LocationInformation(0x022FC224, "overlay_86"), "Entrance: Beyond Great Armor Hallway": LocationInformation(0x02305020, "overlay_79"), - "Tower of Death: Elevator Room Lower": 0x4C, + "Tower of Death: Elevator Room Lower": LocationInformation(0x02306130, "overlay_85"), "Forest of Doom: Right Atrium East Square Room Right": LocationInformation(0x022FDF10, "overlay_101"), "Forgotten City: Outside Left": LocationInformation(0x02308864, "overlay_102"), "Sandy Grave: Upper Big Underground Top Left": LocationInformation(0x02306084, "overlay_92"), "Burnt Paradise: Left Upper Big Corner": LocationInformation(0x02304D04, "overlay_108"), "Great Stairway: Upper Grand Staircase Top Left Item": LocationInformation(0x022FCB0C, "overlay_83"), "City of Haze: Axe Courtyard": LocationInformation(0x022F6F84, "overlay_94"), - "Tower of Death: Top of the Tower": 0x55, + "Tower of Death: Top of the Tower": LocationInformation(0x023060DC, "overlay_85"), "Great Stairway: Upper Underground Room": LocationInformation(0x022FAB38, "overlay_82"), "Entrance: Drawbridge Upper Item": LocationInformation(0x022F9128, "overlay_78"), "Entrance: Statue Room Upper": LocationInformation(0x02304E70, "overlay_79"), - "Tower of Death: Second Gears Room Right": 0x59, - "Tower of Death: Lower Gears Room Spike Shaft": 0x5A, - "Master's Keep: Right Square Room": 0x5D, + "Tower of Death: Second Gears Room Right": LocationInformation(0x02306640, "overlay_85"), + "Tower of Death: Lower Gears Room Spike Shaft": LocationInformation(0x023067B4, "overlay_85"), + "Master's Keep: Right Square Room": LocationInformation(0x022FBCD4, "overlay_88"), "City of Haze: Central Hallway Stairs": LocationInformation(0x022F6EB8, "overlay_94"), "Buried Chamber: Right Shade Hall": LocationInformation(0x022FED6C, "overlay_80"), "City of Haze: Right Corner Room": LocationInformation(0x02301AA8, "overlay_93"), @@ -389,7 +389,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Nest of Evil: Doppelganger Reward": LocationInformation(0x022EE9FC, "overlay_113"), "City of Haze: Center Room Lower": LocationInformation(0x02301C4C, "overlay_93"), "Sandy Grave: On Statue": LocationInformation(0x02307068, "overlay_91", True), - "Tower of Death: Third Gears Room": 0x70, + "Tower of Death: Third Gears Room": LocationInformation(0x02306448, "overlay_85"), "Entrance: Above Metal Block Room": LocationInformation(0x02304F3C, "overlay_79", True), "Dark Academy: Main Building Entrance": LocationInformation(0x022F8660, "overlay_111", True), "Burnt Paradise: Bottom Corner": LocationInformation(0x02303698, "overlay_107", True), @@ -407,7 +407,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "City of Haze: Chunky Room": LocationInformation(0x023018BC, "overlay_93"), "Entrance: Pre-Behemoth Boss Room": LocationInformation(0x022F91E8, "overlay_78"), "Great Stairway: After Keremet Upper": LocationInformation(0x022FAD6C, "overlay_82", True), - "Tower of Death: Gear Belt Room": 0x8A, + "Tower of Death: Gear Belt Room": LocationInformation(0x022FC2A8, "overlay_86"), "City of Haze: Dance Halls Basement": LocationInformation(0x023028C8, "overlay_95"), "City of Haze: Central Hallway Lower": LocationInformation(0x022F6EC4, "overlay_94"), "Burnt Paradise: Upper Right Big Room Right Side": LocationInformation(0x02304ECC, "overlay_108", True), @@ -441,23 +441,23 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "City of Haze: Tunnel Room Right": LocationInformation(0x02302934, "overlay_95", True), "Great Stairway: Lower Grand Staircase Upper Alcove": LocationInformation(0x022FC968, "overlay_83"), "Great Stairway: Underground East Room": LocationInformation(0x022FACA0, "overlay_82", True), - "Tower of Death: Elevator Room Top": 0xCC, + "Tower of Death: Elevator Room Top": LocationInformation(0x02306118, "overlay_85"), "Entrance: Hub Room": LocationInformation(0x02304AB0, "overlay_79", True), "Buried Chamber: Eastmost Room": LocationInformation(0x022FEE38, "overlay_80"), - "Tower of Death: Hidden Room": 0xD1, + "Tower of Death: Hidden Room": LocationInformation(0x022FC368, "overlay_86"), "Nation of Fools: Bottom-Right Room Middle Building": LocationInformation(0x02305994, "overlay_97", True), "Forgotten City: Pyramid 5F": LocationInformation(0x02308A2C, "overlay_102", True), "13th Street: Train Room Secret Right": LocationInformation(0x022F3B8C, "overlay_104"), "City of Haze: Left Dance Hall Left Item": LocationInformation(0x02302778, "overlay_95"), "Great Stairway: Big Underground Room Lower": LocationInformation(0x022FAE20, "overlay_82"), - "Tower of Death: Elevator Room Middle": 0xDB, + "Tower of Death: Elevator Room Middle": LocationInformation(0x02306124, "overlay_85"), "Nation of Fools: Crevice Item": LocationInformation(0x02305778, "overlay_97", True), "City of Haze: First Passage": LocationInformation(0x02301598, "overlay_93"), "Forgotten City: Gorgon Room": LocationInformation(0x02304858, "overlay_103", True), "City of Haze: Axe Armor Item": LocationInformation(0x02301AD8, "overlay_93"), "Sandy Grave: Giant Shaft Upper Item": LocationInformation(0x02305DFC, "overlay_92"), "City of Haze: Dance Halls Vertical Hallway": LocationInformation(0x0230288C, "overlay_95"), - "Tower of Death: Above Motorcycles": 0xE3, + "Tower of Death: Above Motorcycles": LocationInformation(0x022FC440, "overlay_86", True), "Nation of Fools: Top-Right Room Axe Item": LocationInformation(0x02305808, "overlay_97", True), "Dark Academy: Main Building Secret Malphas Room": LocationInformation(0x022F83B4, "overlay_111", True), "13th Street: Underground Secret Room": LocationInformation(0x022FF2AC, "overlay_105", True), @@ -474,7 +474,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Burnt Paradise: Lower Left Big Room": LocationInformation(0x02304C38, "overlay_108", True), "13th Street: Secret Floor Room": LocationInformation(0x022FF024, "overlay_105"), "City of Haze: Right Dance Hall Item": LocationInformation(0x02302964, "overlay_95"), - "Tower of Death: Top Gears Room": 0xF9, + "Tower of Death: Top Gears Room": LocationInformation(0x02306298, "overlay_85"), "13th Street: East Courtyard": LocationInformation(0x022FCA04, "overlay_105", True), "City of Haze: Corner Room": LocationInformation(0x0230176C, "overlay_93"), "Sandy Grave: Giant Shaft Middle Item": LocationInformation(0x02305DE4, "overlay_92", True), @@ -500,7 +500,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Great Stairway: Lower Grand Staircase Middle Alcove": LocationInformation(0x022FC95C, "overlay_83", True), "Entrance: Behemoth Chase Room": LocationInformation(0x02304DA4, "overlay_79"), "Entrance: First Passage Hidden Wall": LocationInformation(0x02304B34, "overlay_79"), - "Tower of Death: Lower Passage Hidden Item": 0x131, + "Tower of Death: Lower Passage Hidden Item": LocationInformation(0x023068BC, "overlay_85"), "Dark Academy: West Building Breakable Wall": LocationInformation(0x022F6084, "overlay_109"), "Forgotten City: Armored Fleaman Pit": LocationInformation(0x02308C24, "overlay_102", True), "Nation of Fools: Top-Left Room Right Building": LocationInformation(0x02305BBC, "overlay_97", True), @@ -542,8 +542,8 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Dark Academy: West Building Top Floor Left": LocationInformation(0x022F60E4, "overlay_109", True), "Forgotten City: Pyramid East 1F": LocationInformation(0x02308CF0, "overlay_102", True), "Forgotten City: Lower Underground Square Lower Item": LocationInformation(0x02304A8C, "overlay_103", True), - "Tower of Death: Stella Item": 0x187, - "Master's Keep: Whip Trial": 0x188, + "Tower of Death: Stella Item": LocationInformation(0x0230916B, "overlay_119", False, "Cutscene"), + "Master's Keep: Whip Trial": LocationInformation(0x02309168, "overlay_119", False, "Cutscene"), - "Quest: Preparations": 0x0200 + "Quest: Preparations": LocationInformation(0x020DFD40, "arm9", False, "Quest") } From 1e1fc624160765b34330a02fecb1e4834c73da49 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 29 Apr 2026 09:35:57 -0500 Subject: [PATCH 49/58] Define item types and IDs --- worlds/cv_por/Rom.py | 24 ++++++++++++++++++++++-- worlds/cv_por/generator_main.py | 2 +- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/worlds/cv_por/Rom.py b/worlds/cv_por/Rom.py index db5dda180978..8d8064b5b6b7 100644 --- a/worlds/cv_por/Rom.py +++ b/worlds/cv_por/Rom.py @@ -5,6 +5,7 @@ from . import world_version from .static_location_data import location_data_table from .Options import NestofEvil +from BaseClasses import ItemClassification hash_us = "2edd57540cae45842fbd19c45a4214f9" @@ -41,7 +42,7 @@ def get_bytes(self) -> bytes: return bytes(self.file) -def patch_rom(world, rom, player: int, code_patch): +def patch_rom(world, rom, code_patch): rom.name = f"{world.player}_{world.auth_id}" patch_name = bytearray(rom.name, "utf8")[:0x14] patch_name.append(0) # Add a terminator here @@ -83,9 +84,28 @@ def patch_rom(world, rom, player: int, code_patch): for location in world.get_locations(): if not location.address: # Filter all events out of this continue - + item = location.item data = location_data_table[location.name] + if item.player != location.player: # Is this an offworld item? + item_type = 2 + if ItemClassification.progression in item.classification: + color = 0x0C + item_id = 0x50 + elif ItemClassification.trap in item.classification: + color = 0x06 + item_id = 0x50 + elif ItemClassification.useful in item.classification: + color = 0x07 + item_id = 0x4F + else: + color = 0x0E + item_id = 0x4F + else: + color = 0 + item_type = (item.code & 0xFF00) >> 8 + item_id = item.code & 0xFF + rom.write_file("token_patch.bin", rom.get_token_binary()) diff --git a/worlds/cv_por/generator_main.py b/worlds/cv_por/generator_main.py index 6dfb30f2f0f7..63ce7f3b741e 100644 --- a/worlds/cv_por/generator_main.py +++ b/worlds/cv_por/generator_main.py @@ -93,7 +93,7 @@ def generate_output(world, output_directory: str) -> None: code_patch = pkgutil.get_data(__name__, "src/overlay_119.bin") patch = PoRProcPatch(player=world.player, player_name=world.multiworld.player_name[world.player]) patch.write_file("por_base.bsdiff4", pkgutil.get_data(__name__, "src/por_base.bsdiff4")) - patch_rom(world, patch, world.player, code_patch) + patch_rom(world, patch, code_patch) world.rom_name = patch.name From 5aad6eda2750923d9b5ba0917249d8ade917354b Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Wed, 29 Apr 2026 23:51:35 -0500 Subject: [PATCH 50/58] Add stuff --- worlds/cv_por/Items.py | 2 +- worlds/cv_por/Rom.py | 52 +++++++++++- worlds/cv_por/src/overlay_119.bin | Bin 2004 -> 2310 bytes worlds/cv_por/src/por_ap_main.asm | 112 +++++++++++++++++++++++++- worlds/cv_por/src/por_base.bsdiff4 | Bin 5103 -> 5183 bytes worlds/cv_por/static_location_data.py | 16 ++-- 6 files changed, 166 insertions(+), 16 deletions(-) diff --git a/worlds/cv_por/Items.py b/worlds/cv_por/Items.py index a2f68e169559..05ff633eedfa 100644 --- a/worlds/cv_por/Items.py +++ b/worlds/cv_por/Items.py @@ -568,4 +568,4 @@ def get_item_names_per_category() -> Dict[str, Set[str]]: "Truffle", "Peking Duck", "Birthday Cake" -] +] \ No newline at end of file diff --git a/worlds/cv_por/Rom.py b/worlds/cv_por/Rom.py index 8d8064b5b6b7..e34fd4fc7510 100644 --- a/worlds/cv_por/Rom.py +++ b/worlds/cv_por/Rom.py @@ -17,9 +17,41 @@ class FilePointer(NamedTuple): file_pointers = { - "arm9": FilePointer(0x4000, 0x02000000, 0xFDBB8), - "overlay_113": FilePointer(0x636600, 0x022E8820, 0x6220), - "overlay_119": FilePointer(0x2CF0800, 0x02308EC0, 0x1F000), + "arm9": FilePointer(0x4000, 0x02000000, 0xFDBB7), + "overlay_78": FilePointer(0x369000, 0x022E8820, 0x10ABF), + "overlay_79": FilePointer(0x379C00, 0x022E8820, 0x1C83F), + "overlay_80": FilePointer(0x396600, 0x022E8820, 0x1671F), + "overlay_81": FilePointer(0x3ACE00, 0x022E8820, 0x121BF), + "overlay_82": FilePointer(0x3BF000, 0x022E8820, 0x1275F), + "overlay_83": FilePointer(0x3D1800, 0x022E8820, 0x143FF), + "overlay_84": FilePointer(0x3E5C00, 0x022E8820, 0x1029F), + "overlay_85": FilePointer(0x3F6000, 0x022E8820, 0x1E19F), + "overlay_86": FilePointer(0x414200, 0x022E8820, 0x13D5F), + "overlay_87": FilePointer(0x428000, 0x022E8820, 0x941F), + "overlay_88": FilePointer(0x431600, 0x022E8820, 0x137BF), + "overlay_91": FilePointer(0x452E00, 0x022E8820, 0x1EE7F), + "overlay_92": FilePointer(0x471E00, 0x022E8820, 0x1DE7F), + "overlay_93": FilePointer(0x48FE00, 0x022E8820, 0x1963F), + "overlay_94": FilePointer(0x4A9600, 0x022E8820, 0xE83F), + "overlay_95": FilePointer(0x4B8000, 0x022E8820, 0x1A59F), + "overlay_96": FilePointer(0x4D2600, 0x022E8820, 0x1809F), + "overlay_97": FilePointer(0x4EA800, 0x022E8820, 0x1D49F), + "overlay_98": FilePointer(0x507E00, 0x022E8820, 0xE29F), + "overlay_99": FilePointer(0x516200, 0x022E8820, 0x129BF), + "overlay_100": FilePointer(0x528C00, 0x022E8820, 0x1A53F), + "overlay_101": FilePointer(0x543200, 0x022E8820, 0x1577F), + "overlay_102": FilePointer(0x558A00, 0x022E8820, 0x2067F), + "overlay_103": FilePointer(0x579200, 0x022E8820, 0x1C59F), + "overlay_104": FilePointer(0x595800, 0x022E8820, 0xB45F), + "overlay_105": FilePointer(0x5A0E00, 0x022E8820, 0x1493F), + "overlay_106": FilePointer(0x5B5800, 0x022E8820, 0x16B3F), + "overlay_107": FilePointer(0x5CC400, 0x022E8820, 0x1B6DF), + "overlay_108": FilePointer(0x5E7C00, 0x022E8820, 0x1C8FF), + "overlay_109": FilePointer(0x604C00, 0x022E8820, 0xDBDF), + "overlay_111": FilePointer(0x61C200, 0x022E8820, 0xFEDF), + "overlay_112": FilePointer(0x62C200, 0x022E8820, 0xA33F), + "overlay_113": FilePointer(0x636600, 0x022E8820, 0x621F), + "overlay_119": FilePointer(0x2CF0800, 0x02308EC0, 0x1F000) } @@ -81,6 +113,7 @@ def patch_rom(world, rom, code_patch): # If Nest of Evil is required, your goal (either brauner OR drac) will check it. rom.write_to_file(0x02309171, "overlay_119", bytearray([goal_requirements])) #################################### + # Location handler for location in world.get_locations(): if not location.address: # Filter all events out of this continue @@ -106,6 +139,17 @@ def patch_rom(world, rom, code_patch): item_type = (item.code & 0xFF00) >> 8 item_id = item.code & 0xFF + if data.location_type == "Normal": + rom.write_to_file(0x02308F40 + location.address, "overlay_119", bytearray([color])) # Item color table + rom.write_to_file(data.pointer + 6, data.file, bytearray([item_type])) + rom.write_to_file(data.pointer + 10, data.file, bytearray([item_id])) + elif data.location_type == "Cutscene": + rom.write_to_file(data.pointer, data.file, bytearray([item_id, item_type, color])) + elif data.location_type == "Quest": + print(":Huhcat:") + else: + raise ValueError(f"Error! Location {location.name} has invalid location type {data.location_type}!") + rom.write_file("token_patch.bin", rom.get_token_binary()) @@ -130,7 +174,7 @@ def get_source_data(cls) -> bytes: def write_to_file(self, offset: int, file_name: str, value: typing.Iterable[int]) -> None: file = file_pointers[file_name] address = offset - file.base_address - if address < 0 or address > file.file_size: + if address < 0 or (address + len(value )> file.file_size): raise ValueError(f"Out of Range: Tried to write {value} at {hex(offset)} in {file_name}") address = file.rom_address + address self.write_token(APTokenTypes.WRITE, address, bytes(value)) diff --git a/worlds/cv_por/src/overlay_119.bin b/worlds/cv_por/src/overlay_119.bin index 34daf9e1102b7bce0adef3a57dc7bac17821ee66..2f88de2bc14c17ca6e1fa5b54f0219c4465728b3 100644 GIT binary patch literal 2310 zcmZ`)U1%It6h1RM>24;WJDbg4x7uz@Xhj=4i9VFF!bb5;>L#?9hd`qElQyxoYQcw? zCR(v*(6k1r7}zQ3OB6}4LM#cSCPL66(@H@x$nH+3*&irDXKQeS<9B9f(=E0Gmpk{I zbH8)Wch0>xP5net75V8fB`7M8Q<^q(3^JzIDjT3M5lIy+D?b_UK;6JrVp1@Y{yHKFr%Nd#E+zqfSF4x+r6Y z{Jne+yWe0ZlwqQ~$@YxPJfGiL$en75o2Q`@=?e7UgE!1LLwxSGVBdDz&J8;L zz^O92$t7cOEMLEGIcLGLZHHSg;_x80b%)cKQR3il;BbBt&ezPCofWaS(Z#KHaYe+Y zL1#UpqldbTZz1u-RL0psJgC7p>1N~+K8u0#5Pl+szD%N)mpC4MF>X#SEF>0u zx9w+Ld*Lqj+zYrva+5*klkbff{Ak>}Q;t9M4CC@$RLVoB*y8n7%2mUNzu(z+2D}yf zPv}DrJqj*kzpU0D!u=AfOW+*Vc#(?`e!FHJeLL*(&u3iqa7}cx3ivhPhVPKDD`THN z@L$Vo^t0%4RL#D)_F)WFGd}&o%Ci3|S0+R>V*)b9I*Xa>tUVAn^_u_J`1615+?-g% zA1SNtcfkVAeQV~Nt1J2D-g(QxZq2;OI`@3Qmv7&YEi<39s55ib4}T?2p`TTU?2Fy* z9=sRMyYQg19yi5GCB+!Kf$^AYBi>=7n_;ZkW(RCAryGZg{Wj_Plgj+z8A1Iz5)wG9 z#Xq0h0PbHw=}1KaOQv}tpPdzo@iWiK3we3YU9(@V{R(7Xxia=mL%4Imb3|A&uVKI6 z{lAL;G1sni*4U2whWX$-@2~aS?~4z-R${EF{BEL#v}jf$A4z>OHLZS(cQcr3#(ViV zYI7MjPH(}+2b-UMN3DUm8+z)~VB3)5b6UYUV&;As<8yZmdeq>^3ftqIfUN)-{3Ty< zWz+0i8BJ97;a+ofy^gLmvkUmPQtx2n4%oy#!Pvmk2yp?onB#nU9BZL(#JR6neoBd( z*MK$lyk9)G)YL;qAzOjn#3!owF7RH%`VXx8G0(#880O;`Kj8mW%x_?H;=#6qAv;}vpbz8nR&^>Y#ZI^_?_9=bc=T3a_64& z-S3?HopbN5(l}AHlM?hAnUob2jZ>D0RMG^=N2m+m{!#y^5}`LGOslc|*HdU|8lTq2B-63&aK8bkws10*>(dGRe3bjhKwkxUr8yp{B|C_PJwcBE zCy~n&@FA}CH#8-zCwdAtYxvB*YwiT|gguONLjqudFQT?%;rIORP4}r59BQnL1o2J8 zoVJ_wK^=qNa}a7c{p;|^3qF27O|kLns}0|6&;#x??JL-<;q=}QOrE{S&3SOonQRNs z#eH`qU2_!VbO`f>?~R}@OTcuIvBEloS2>eno{Ki)nU{5*-%)o@++DCw!6#`M{!7pe zv$|~a{b|R(oxbnpgLu$XJ+`GKb8s$;AA3RXd%hoN8$%uuG{qxfk$`xo5gr&t;rFhd4Ul)nX72{mH!irZmqX+S+6B z4yAJn`A-J>jzhOb|B07$YkU}5=6+e}+mCl6*K6V)cE@lQk$Jy+1ARLc>d$vME4e0y z-3k6~Xv1%X7TP!_-YI2(*XU;%YShKCxb_(gO}9>ea;lDdKeTo_^fhC1@5}_;Lu((gFT5X!6@N1CE3D@N>dabA zB3@Hd_!l0{`{&2QJ$UnjxBcN@J!{JjwaOe1f#dNIBX7bOb_Q$43;~0A_1=7=-{!)2 zJuUI@SAhDBno(%jgKxgKNxZW{9j%Temu&A`%_&nOf7W^ZTur$gt~oB(-i7T#Xye$4 zypQ_m)`(cLuV8;7{I1}8F2ptO8soTcSPy>l5^LT$F>Fxd~1XF@nGm8yIJW|SwCp|7guF$qj1 z8O9XY5yup}3d~j1R6;o{g56m`mS18nG{ch27$86|(3o%l#7k5Yu%T08wTWy1%7zmF zuqNr^DgdA|*vxML9uH>-Yk0O74?6oEq=T*h4&VuJ0+bC^tHKyb5eWbR@COK>Jmz*2 zk75Jk0t^8itgcy8#R!&Rka%dO5CEVRJTQw0Faai%%>=~ic&a>t6dQm8%iTnUz2wXW%Wvf4q?F_G?>}!yo=+N5E;&oZz&`jqn{u8| zeLN;+q_y}di)hs|*r*&gmG5o;z2MK{Q{QH%>e%C>Dc5|K;d`P73*DNl?~Lhh$dPiZ z{pa{dVny6~>VXr=Q}a6Ut`2v|NcZC{{iBiv-CcIUQ>spaw41u9f$-eFaoPIjtF`tF_vr7A zMM%;p>PjGw>P?1*UslLxI`j{=j6!m7!stbUB9?&MlN5X;@+$^b>q(-Eoo3fp_tUK2 zii1LMFlh)NuPy50G>6L)9ASy41o@?uWWi}fZ!AWn*iSrQsgiF~n_HWOJ=Ra#kth0W zsUCn@!C;!<7z#RZpS<4&uVdM#(isYJg-4fh>o;u z&kh<1)O5biXL5RwQtp5-Ktf#&(`NbqLV-h6ak(F(bc!pI7u$cQf!hlF5%gf z%zDa^i-p9&i=MBgHbat2f4>vdFvsn|Num}g?svQt<|=15=9R4)3d_!}3_s&FTmGUw z$+XktUJYxH&xvO4ty9SnYr==_>(r#9K}rFm5cZ$qPFm4@^r3vKj95wJ2>DrlzHB_3 zJvD4I`E6d%Z^4W+2xPNCgS@tmH6Ye@%8~3v>~%cs>m6-X>78H}j0QU!)9I=*bq91- zynkiVIjisY|AAi~PNs^`UJIU6zkPA{nPpkIv5X8uTT#|HLsMzg?;9QOvM&1hE2iji zhPQFOw;Zi`zE-sbM!93i0V_sS z1_l`a>vv{txCdfhIpZ}q{Jh?B##&P#r>}x2OPbUqAG#51%AH}B#qi%uxpusH{RN`L z(pJE_J(o@OAxe(m1R26-Kre^zED(-e(Gm-Y&Q2S^TKvEpxD`8KyH3mk!pTeP^wBU8 zOSaGFY%Scw=^MK5bShHRVMhwnV${X@F&B%RFB$zbSwi(`1$0U&-ZBhkob}KpUk{XZo69 zxKuNp(B;aI`o;#i1CtM0%->udlQ=a*G#IagDG9U1g;HO+Mz4)`#QB~43MeW+sPg;W zUe(g`Gq)>aZl(10(GUy4;g=(`MHd#%tS3Ij^JE@cs7C0I{5X^4wK_YO1b=+;5pI#q--33IlmChFPM1zkf#7TLmx` zUn%X_QTW2eNe(lG0D*u>y+n9{V~cByMBz`8x>Z6mS zefb))xBp&|$C5Wh{O+8N+frxb;Leodd#-D-TWw2eanPAh>q&#Af(JO*VI5dn3HFD4 zJAYpUvayVFKRhD~#;R3tQ7J~qAga|hnHnnw_p;Ba(ItWmq9a_&YqX2zPd_)f@$la5 z{`V_GpUo=DacRGR(2zMl-6p;!j zt#4xCw31|*kvny$eT&PxUZ8z_a)#ao&}%RlA3z)cAP4~AR4O&7qao3>T}vNrYGAU6 z<89`El?T(zh~%!xaEIMK_8AbV+Lm{L16+mmon228{qwQ&w#rSPXIDG5Cx#-Kl;Ip% zUvvoJtBGL=R4#;RNi89a@Pq&~7c5cA4Iv0oxqI_M{2@Y3Y(sgi&62FPVr&B@ zPEP$5ABYFv%LAY_e1dJIkHC>3N8)U+Zv)tW)e3Bnz(G(h7t$lgf&>cyNRI@c&tY$O ztdVSMZiy5Kt8PNA;8M|kddh`T@GvuLB1BFP-E31HJwr0YOe`E#H2A?epO z_{S^Hoi$)Z?2BM5<@JlmacrGyTMX~J``u5?vQ*K4L)?rsT@5ATWh4-3hCZkswl8Y> zop-C|q)%YAmsiqLzc}2J7Ms+yC%Eq|lL@t8hue}f-om@hO<9XWml1Xx4F?=r{(cxH zG6R2%gmaZQv^X!9vw)*k8~5A`QZRDjVNY-u$t*Jq(rWViv1_^!zOzcN6U5ejycpOi+KpG;b@0N>__UC`nCtD|5sFE`56+s?gBcZ3Cuh|W_TvaBuyDIGD ztaxeI_g2ww@Dhm zMSpHzQ(Q1JiAF&WHRI=Yk?6auTVvD}npo|= znI@WbX#W5cPqVid-80|M$KFabG2E3RWBI*=SGxJJ!)0W0T;~j}r)(9vcV>ShH8iqUhf~wB z7BWip{!=ra3@{7Dw{t$xSaR|$q&iH48mcW_yPGg^IZRqt9rxs<$HIxx0wIggXzKDz zqmQp_qLXoZ5)F z@%a!F9`3=+zUe&1G6( znNj^RvR84HRMU{5A)y-)`AjV(@zK0_5QsA8*xEJyhYgKnbhUpc7OA=8H}4t zqiiptp`#K3vpErPCr(vv;fZTZf4jukVP>Zx`Z<$q3z+6rdaTUl86b7+QggZ%Y?_`y z3lj|F>WSwn)iU!ISs&YJNA#wWtIkVFe@RgoB_7ahC?I`U^jt`C=8F%7egCSb^C>WI z)X{A=O)j95i)PW%qwIfugjc!)Tj{T~Z8f{wqvX_aldr?rIPg5=T)T*drY( zK`i#4`F@MJcX|T5WYUf{eT47X*h#uzmi_pqbG@{!ky0M`Agh>tE-32Kk(inGaG!ii*zPO_{&^K&Zjm?x;J;d82^p@c;)lpxPMMg zRBu$|NQ?E&p)!m0B}I|>Lz)GU`!)&x*mgY!8|>Y!ym|l2K$_@jSAv{A;82MbE&uh? p3dzdV?QPF}e^e1%F`W=u!XxNdN!;|NsC0|NsC0 z?^pYK@BjaQ|NZ}e|NsAf-uU8%+_WCXEbDG{hT7^)ximglIAv0g%z4Xa}eb27oj&00006nlxwwN2#D1 z4FG6pXd0f7kf)+OMwo^`8fl;!00U1@G8!5H8UPspXv6>j0009(0002U&;S5r13{)i zN`L?Xpa2a3000000000005miJ00000000000Dk}g0j7Zxr8Lb_JwrxA8VK}_149!{ z88S3_jR0iO(9mh7hp5N{5X~b^8UVx^WY94gJwVXVF%1Joj6tS=0MIcl;<5UMPG(V~ zz@I>&`=S1p0PNS?0U%HaG6T0!?-&@QB6VnmzYlwntqoX2hfR$mAmteUTwMG5lh|Uf z%YVmE=)?Y^En<@GYY&P-lrASlH?%AZY`l~kV5lgxeiadGal=@Id3b1yw1E2yM_n3a}7RaM4u|q6(^|SfV1BNFoVs6cp?tH6j4EkU$i# zfFahv6+(z8jc6z$Tc9WlL;-AI0DuPb5d{-H1VI+0ML|&9AS$mh5EN6If&wCb(SKA> zP1YbF157|rETDh@pk5+?peTn01W`a#;Ov20zvxgP)`Fp31-s4?HN+N_B#b142_YD7 z5fuRyM7(M!5o^lD6p=?a1lL5UKvz8j;)*87mGLo!B?V$9L_{|d0lCTvBq1QeLIEZ^ z7Wi%PMFAB=qTeV13QGcnrYLr>1Al&s7PhnmiRa2-mkCw~7ZNLN9h7Q7@V}}A;42p* zxOkW;q=~!1kYNQRNf`#>NeICK1XUB4>TFAHe$w(-m-{?`lCw&*7-9imF~s`rpnXem zmWiMV1La)a#VUtup%M&}6tj5rr9%5}2r8vP{Qpu-ks@seZa^ZHt&&AZm49y6sG!lq zxx>mzty^E-rWrE0hg3$vDNh(YTlQ-}BMO0GQF4irT_y+vxdd@OPJvX%1_1&$>y2z&$BWpo544o^oHk6X23sF?bReu4OAVv_bc>+Hy zirE;biPE{_P9kSZ)>yio(PPN6cRCNt41*=I&p`CMHJIk~nKe!}IV*|DIFs&cpOIp| z#?0>VhJPs&K|@G zgV>7$8!{nxiwL!cAAhFA2^;F+t@uNSmo`;}4|SVjmq)NCa_2Q1`Gfg1HA!ypj1d9V zUFjNC>Lb&p%FnAY<37>N(h4E;{9WAC-Yjxg0WfGGR^rW5VDOWiIG!>ZVnBiQ3+M=8 zt{cE>NWtjr*p?OuHjk>Tn5V(6qMTIM^HkuopKGt~rzZbyt$#U{OGU_Z4aNZJ$U*0_ z&tc#$;ff~H0L>?T8d#O$3bIiO*9u58HdcASYT|~Qx%r`O&T~NR{9gDM9j4Wm4a3{| zuHunjAbp$%+1J)W7f9O{81ZcLj2J*ruZm0I5UNg^1YaCywpLl%B~J8uf|RVQc>R~D z+&8LT&1P(l6@M#q(}5ylj6s2>$p!bEIEEl^*%;K>2?+F=a3BPIm{Z=9fgxh8U20xp zLLfrQqbSiwd31%d#NwcL5LEDwA=LjDQJGOb8VDd`V5Wo{E!0>AwJo|@Sd9(~Zn-~K zl=YeE!!Wty2nbG#79Mh$!qY1U4N+q_bIjpW|FVlMQheD%+fG~0SEv9fFTG#5FR6ld19XuG)#h?x(vBU!4JdF z6o_L-4~zaH_*U!SU7%B_)vmm}&U_mw+O16Wy{MS^@?D z0Du4hK>z^-DWJ?{B2b$ZB2b<#n^O%;LqU;& zJ%1sBV1Q|m83q6Z82}qZ0KnAr5R*U@+MbO-G6A3f02(v^0004?0iXZ@po1m=OafwL z$$$YcCIVt$m;e9(0WvTX0vJFE5hj|0LZ|9aXr890r1FM?MuvcCrqlyK0iX>4003wJ z4FLC9Hj#P&T6Ewlpx?m9I}#$ zAXS)07Zqu-e*weqgQz3^XJZTOKC=+VL_z@4at?^zra1vguC7>FgCeA2qKQ;(-=lH5 z)T+IZ$`lGT!K<#z>FcIK^5E^g6Z$%eB2d?0;GWN z8nttdT@k!Or@HUJ5+kSYzH1%%fq#qT$(j7iIfHnwDSzG$mWcbHp|eM!xr49%Y11Rg zb1rE3(rmXh^zHetdVdJU2tB>NTnLwq-E~c4r*&+Cdas#5jZqCXGNu z4*eke>1;DHPbhudi}1}AFM*Wx>)&)0v30QGj|{PiEE;AuQ$=)=q%r|XKWh8{mYzI;pAgo}Zi|Jt!MY`EAv7smNe8TZHxwdCzOM34eXQ?tjh}$gVJ)TV-{9Gsd-jt^D&wM7)%!{ z6ciT{N(?>EF=A90g#v=Yihmt!AX6gR6uwkuqOgs$atL)|Buc&Hd9)?`?_+oM0iws?}n=p-QawYn7WUN0$sQtIog3j!8c&f~R?R{r{OT?Rb0R zt<2~4Xa1|TH=&}+$w4PvC~jpx0&bBm9dT}U=^c#cFiZRmMneUrJ%2_nJB79J={@c` z89aF-q~S|bzoAijFtr<=QBuxl6R#V&ijtjDpqas(>tBkB)c9WJMMTfwu+J4=aizsX z2;Dmj4zD(SlLbW2v^T5p?C4^NL9a)m-du!p*IClz*!Ao$)+UzPK;d6)9iiV6%nEtT zYfY-!R6#zaz*J&*ntyQ(Ru0k=B{o+|U~jvjN>pR6nQ`U>rEw)o*-|2?9%+hov_w`& zQPH@yuruP5mT+%57U)4s8+tWP%TbPH0j0a@qUjKXR80i#UUR;ph7uJ8NIqhiaH&gE zQn6+8pWPxLC@SRL>z)16$v)W+EvH#wlG;CCaUbRDVDlV?*b`4Ka4^+0~?M zGh{L|#@wXAOeivtz@~}IGu2nT1{BUB*-Cuw31T?z@N_a((k!EflPR=QCagVf6&u`r zL`>MlipMjg)_(|Ac4?22)VyvOBMrDU?&OI=O1L;Tnp9zPOe{DAE09sS-Y^i6TTteb z6+kW?>x1Y$+5{wV8;)oQO!L%y#N5>LGV15@H*y};vF6yjb9*?DxXya}rC4Fdm_aPl zvoo;=M96q*xInThxJw*Dt?dwiQbQXnuH?N^q@#m7MStislshM}LOjwCk$@90V(*sC z*AJamRueUIbj8lFaAT75J0u9cA{chg9}~BNb8#dMtng!a3=pqk0q^czG#?5T?d(}f z5RCQ1KFv!!6GVthNRwC<9fi!%cOivfE_G69|5+xgCORR6<|BqR(jh`3frjT@d{unR z0)&eV34c2bXdd*7VH#2M=+&_MP@*Pl-^lQ99yZQQ&|_VSS&XYM!AD>8&1=#uL7kO6GItD%Sd&#Cko>kTn<#{e`s^FUS$Db w)mjQmfD>qlfoObMLJ^y@mDgTJ8ZD;zICh9<0fj9&X+QY8k}1N3f{fQ5NE7~IiU0rr diff --git a/worlds/cv_por/static_location_data.py b/worlds/cv_por/static_location_data.py index 409ef41c2abd..e6e13bf663d4 100644 --- a/worlds/cv_por/static_location_data.py +++ b/worlds/cv_por/static_location_data.py @@ -337,7 +337,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Burnt Paradise: Left Lower Isolated Room": LocationInformation(0x02303A94, "overlay_107", True), "Burnt Paradise: Right Lower Isolated Room": LocationInformation(0x02303D34, "overlay_107", True), "The Throne Room: Great Stairs Under Stairs": LocationInformation(0x022F1B54, "overlay_87", True), - "13th Street: Behind Final Guard": LocationInformation(0x022FEE50, "overlay_105", True), + "13th Street: Behind Final Guard": LocationInformation(0x022FEE50, "overlay_106", True), "Buried Chamber: Right Secret": LocationInformation(0x022FEC34, "overlay_80"), "Sandy Grave: Bottom Right Room": LocationInformation(0x02306540, "overlay_92"), "Tower of Death: Passage Hidden Item": LocationInformation(0x0230661C, "overlay_85"), @@ -346,7 +346,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Forgotten City: Big Shaft Room Right": LocationInformation(0x02304654, "overlay_103", True), "Nation of Fools: Right Bottom Corner Room": LocationInformation(0x02300510, "overlay_96", True), "Master's Keep: Hidden Room": LocationInformation(0x022FBC14, "overlay_88"), - "13th Street: Three Amalaric Snipers": LocationInformation(0x022FF0F0, "overlay_105", True), + "13th Street: Three Amalaric Snipers": LocationInformation(0x022FF0F0, "overlay_106", True), "Dark Academy: Main Building Three Toads": LocationInformation(0x022F8060, "overlay_111"), "Nation of Fools: Top Corner Room": LocationInformation(0x02300258, "overlay_96", True), "Burnt Paradise: Left Upper Isolated Room": LocationInformation(0x02303908, "overlay_107", True), @@ -436,7 +436,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Great Stairway: Right Loft": LocationInformation(0x022F893C, "overlay_84", True), "Great Stairway: Left Loft": LocationInformation(0x022F878C, "overlay_84", True), "Nation of Fools: Bottom-Right Room Left Building": LocationInformation(0x023059A0, "overlay_97"), - "13th Street: Many Nyxes Room": LocationInformation(0x022FEF7C, "overlay_105", True), + "13th Street: Many Nyxes Room": LocationInformation(0x022FEF7C, "overlay_106", True), "The Throne Room: Above Throne Right": LocationInformation(0x022F1A04, "overlay_87", True), "City of Haze: Tunnel Room Right": LocationInformation(0x02302934, "overlay_95", True), "Great Stairway: Lower Grand Staircase Upper Alcove": LocationInformation(0x022FC968, "overlay_83"), @@ -460,11 +460,11 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Tower of Death: Above Motorcycles": LocationInformation(0x022FC440, "overlay_86", True), "Nation of Fools: Top-Right Room Axe Item": LocationInformation(0x02305808, "overlay_97", True), "Dark Academy: Main Building Secret Malphas Room": LocationInformation(0x022F83B4, "overlay_111", True), - "13th Street: Underground Secret Room": LocationInformation(0x022FF2AC, "overlay_105", True), + "13th Street: Underground Secret Room": LocationInformation(0x022FF2AC, "overlay_106", True), "Great Stairway: Big Underground Room Upper": LocationInformation(0x022FAE2C, "overlay_82"), "Forest of Doom: Right Atrium West Top Room": LocationInformation(0x022FDB44, "overlay_101"), "Burnt Paradise: Upper Left Big Room Left Building": LocationInformation(0x02304D94, "overlay_108", True), - "13th Street: Underground Demon Room": LocationInformation(0x022FF264, "overlay_105", True), + "13th Street: Underground Demon Room": LocationInformation(0x022FF264, "overlay_106", True), "Sandy Grave: Mega Ghost Room Lower": LocationInformation(0x02307434, "overlay_91", True), "13th Street: Left Big Room": LocationInformation(0x022FD058, "overlay_105", True), "City of Haze: Bread Shelf": LocationInformation(0x02301D78, "overlay_93", True), @@ -472,7 +472,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "City of Haze: Underground Secret Room": LocationInformation(0x02302B50, "overlay_95", True), "Sandy Grave: Mega Ghost Room Upper": LocationInformation(0x0230741C, "overlay_91"), "Burnt Paradise: Lower Left Big Room": LocationInformation(0x02304C38, "overlay_108", True), - "13th Street: Secret Floor Room": LocationInformation(0x022FF024, "overlay_105"), + "13th Street: Secret Floor Room": LocationInformation(0x022FF024, "overlay_106"), "City of Haze: Right Dance Hall Item": LocationInformation(0x02302964, "overlay_95"), "Tower of Death: Top Gears Room": LocationInformation(0x02306298, "overlay_85"), "13th Street: East Courtyard": LocationInformation(0x022FCA04, "overlay_105", True), @@ -515,7 +515,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Buried Chamber: Hidden Wall Item": LocationInformation(0x022FECA0, "overlay_80"), "13th Street: Bakery Middle": LocationInformation(0x022FCB54, "overlay_105"), "City of Haze: Cart Secret Right": LocationInformation(0x022F7014, "overlay_94"), - "13th Street: Right Dance Hall Top Right": LocationInformation(0x022FED84, "overlay_105", True), + "13th Street: Right Dance Hall Top Right": LocationInformation(0x022FED84, "overlay_106", True), "13th Street: Bakery Left": LocationInformation(0x022FCB48, "overlay_105"), "13th Street: Train Room Secret Left": LocationInformation(0x022F3B80, "overlay_104"), "City of Haze: Bar Right": LocationInformation(0x023019E8, "overlay_93"), @@ -538,7 +538,7 @@ class LocationInformation(NamedTuple): # In-game information used to define eac "Great Stairway: Connector Pipe Left": LocationInformation(0x022F8A80, "overlay_84", True), "Great Stairway: Connector Pipe Right": LocationInformation(0x022F8A8C, "overlay_84", True), "The Throne Room: Great Stairs Left": LocationInformation(0x022F1B6C, "overlay_87", True), - "13th Street: Right Dance Hall Top Left": LocationInformation(0x022FED78, "overlay_105", True), + "13th Street: Right Dance Hall Top Left": LocationInformation(0x022FED78, "overlay_106", True), "Dark Academy: West Building Top Floor Left": LocationInformation(0x022F60E4, "overlay_109", True), "Forgotten City: Pyramid East 1F": LocationInformation(0x02308CF0, "overlay_102", True), "Forgotten City: Lower Underground Square Lower Item": LocationInformation(0x02304A8C, "overlay_103", True), From 71a1fa5ff31c6ada70b2f0c0f20240563f4f31a8 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Thu, 30 Apr 2026 00:24:22 -0500 Subject: [PATCH 51/58] Fix item links --- worlds/cv_por/Client.py | 25 +++++++++++++++++++++++++ worlds/cv_por/Options.py | 4 ++-- worlds/cv_por/Rom.py | 30 +++++++++++++++++++++++++++--- worlds/cv_por/__init__.py | 18 +++++------------- 4 files changed, 59 insertions(+), 18 deletions(-) create mode 100644 worlds/cv_por/Client.py diff --git a/worlds/cv_por/Client.py b/worlds/cv_por/Client.py new file mode 100644 index 000000000000..ad9c2fec5396 --- /dev/null +++ b/worlds/cv_por/Client.py @@ -0,0 +1,25 @@ +from typing import TYPE_CHECKING +import worlds._bizhawk as bizhawk +from worlds._bizhawk.client import BizHawkClient +from .Rom import world_version + +if TYPE_CHECKING: + from worlds._bizhawk.context import BizHawkClientContext + + +class PoRClient(BizHawkClient): + game = "Castlevania: Portrait of Ruin" + system = ("NDS") + patch_suffix = ".apcvpor" + most_recent_connect: str = "" + client_version: str = world_version + + def __init__(self) -> None: + super().__init__() + + async def validate_rom(self, ctx: "BizHawkClientContext") -> bool: + return False + + + async def game_watcher(self, ctx: "BizHawkClientContext") -> None: + print("NOT DONE YET") \ No newline at end of file diff --git a/worlds/cv_por/Options.py b/worlds/cv_por/Options.py index 688e9cdd520a..724a0214e3b6 100644 --- a/worlds/cv_por/Options.py +++ b/worlds/cv_por/Options.py @@ -22,7 +22,7 @@ class RevealBreakableWalls(Toggle): display_name = "Reveal Hidden Walls" -class NestofEvil(Choice): # TODO!!! IMPLEMENT +class NestofEvil(Choice): """The state of the Nest of Evil. Vanilla: Nest of Evil has its original rewards, the two tome pages and Greatest Five. Randomized: The three items in the Nest of Evil are random, and added to the item pool. @@ -36,7 +36,7 @@ class NestofEvil(Choice): # TODO!!! IMPLEMENT default = 0 -class BraunerRequired(DefaultOnToggle): # TODO!!! IMPLEMENT +class BraunerRequired(DefaultOnToggle): """If enabled, you will be required to fight Brauner before you can access the Throne Room, Of course, this won't do anything if the goal is set to Brauner.""" display_name = "Brauner Required" diff --git a/worlds/cv_por/Rom.py b/worlds/cv_por/Rom.py index e34fd4fc7510..09cbea28123c 100644 --- a/worlds/cv_por/Rom.py +++ b/worlds/cv_por/Rom.py @@ -1,12 +1,14 @@ +import hashlib +import os import typing import struct from worlds.Files import APProcedurePatch, APTokenMixin, APTokenTypes, APPatchExtension from typing import Sequence, NamedTuple -from . import world_version from .static_location_data import location_data_table from .Options import NestofEvil from BaseClasses import ItemClassification +world_version = "1.0" hash_us = "2edd57540cae45842fbd19c45a4214f9" @@ -146,7 +148,7 @@ def patch_rom(world, rom, code_patch): elif data.location_type == "Cutscene": rom.write_to_file(data.pointer, data.file, bytearray([item_id, item_type, color])) elif data.location_type == "Quest": - print(":Huhcat:") + rom.write_to_file(data.pointer, data.file, bytearray([item_type, item_id])) else: raise ValueError(f"Error! Location {location.name} has invalid location type {data.location_type}!") @@ -180,4 +182,26 @@ def write_to_file(self, offset: int, file_name: str, value: typing.Iterable[int] self.write_token(APTokenTypes.WRITE, address, bytes(value)) def copy_bytes(self, source: int, amount: int, destination: int) -> None: - self.write_token(APTokenTypes.COPY, destination, (amount, source)) \ No newline at end of file + self.write_token(APTokenTypes.COPY, destination, (amount, source)) + +def get_base_rom_bytes(file_name: str = "") -> bytes: + base_rom_bytes = getattr(get_base_rom_bytes, "base_rom_bytes", None) + if not base_rom_bytes: + file_name = get_base_rom_path(file_name) + base_rom_bytes = bytes(Utils.read_snes_rom(open(file_name, "rb"))) + + basemd5 = hashlib.md5() + basemd5.update(base_rom_bytes) + if hash_us != basemd5.hexdigest(): + raise Exception('Supplied Base Rom does not match known MD5 for US release. ' + 'Get the correct game and version, then dump it') + get_base_rom_bytes.base_rom_bytes = base_rom_bytes + return base_rom_bytes + +def get_base_rom_path(file_name: str = "") -> str: + from worlds.cv_por import PoRWorld + if not file_name: + file_name = PoRWorld.settings.rom_file + if not os.path.exists(file_name): + file_name = Utils.user_path(file_name) + return file_name \ No newline at end of file diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py index f244ae6f9125..86c1d802dac9 100644 --- a/worlds/cv_por/__init__.py +++ b/worlds/cv_por/__init__.py @@ -8,10 +8,8 @@ from .Items import item_table, get_item_names_per_category from .Options import PoROptions, por_option_groups from .static_location_data import location_ids -from .generator_main import CVPoRItem - -world_version = "1.0" - +from .generator_main import CVPoRItem, generate_early, create_regions +from .Client import PoRClient class PoRWeb(WebWorld): theme = "ocean" @@ -58,6 +56,8 @@ class PoRWorld(World): options_dataclass = PoROptions options: PoROptions + generate_early = generate_early + create_regions = create_regions # locked_locations: List[str] # ocation_cache: List[Location] @@ -312,14 +312,6 @@ def __init__(self, multiworld: MultiWorld, player: int): ] - def generate_early(self): - from .generator_main import generate_early - generate_early(self) - - def create_regions(self): - from .generator_main import create_regions - create_regions(self) - def create_items(self): from .generator_main import create_items create_items(self) @@ -334,7 +326,7 @@ def generate_output(self, output_directory: str) -> None: def get_filler_item_name(self): from .generator_main import get_filler_item_name - get_filler_item_name(self) + return get_filler_item_name(self) def create_item(self, name: str) -> CVPoRItem: from .generator_main import set_classifications From 69eb3482365cc508fa632ea199828a5433bfe95f Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Thu, 30 Apr 2026 01:14:24 -0500 Subject: [PATCH 52/58] Adjust check pos --- worlds/cv_por/Rom.py | 53 ++++++++++++++++++++++++++++++++++++--- worlds/cv_por/__init__.py | 2 ++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/worlds/cv_por/Rom.py b/worlds/cv_por/Rom.py index 09cbea28123c..7c7fc82f7b6d 100644 --- a/worlds/cv_por/Rom.py +++ b/worlds/cv_por/Rom.py @@ -1,5 +1,6 @@ import hashlib import os +import Utils import typing import struct from worlds.Files import APProcedurePatch, APTokenMixin, APTokenTypes, APPatchExtension @@ -66,11 +67,22 @@ def __init__(self, file: bytes, name: str | None = None) -> None: def read_byte(self, offset: int) -> int: return self.file[offset] - def read_bytes(self, offset: int, length: int) -> bytes: - return self.file[offset:offset + length] + def read_from_file(self, offset: int, file_name: str, length: int) -> bytes: + file = file_pointers[file_name] + address = offset - file.base_address + if address < 0 or (address + length > file.file_size): + raise ValueError(f"Out of Range: Tried to write {value} at {hex(offset)} in {file_name}") + address = file.rom_address + address + + return self.file[address:address + length] - def write_bytes(self, offset: int, values: Sequence[int]) -> None: - self.file[offset:offset + len(values)] = values + def write_to_file(self, offset: int, file_name: str, values: Sequence[int]) -> None: + file = file_pointers[file_name] + address = offset - file.base_address + if address < 0 or (address + len(values )> file.file_size): + raise ValueError(f"Out of Range: Tried to write {value} at {hex(offset)} in {file_name}") + address = file.rom_address + address + self.file[address:address + len(values)] = values def get_bytes(self) -> bytes: return bytes(self.file) @@ -165,6 +177,7 @@ class PoRProcPatch(APProcedurePatch, APTokenMixin): procedure = [ ("apply_bsdiff4", ["por_base.bsdiff4"]), ("apply_tokens", ["token_patch.bin"]), + ("check_patch_version", []), ("adjust_item_positions", []), ("apply_modifiers", []) ] @@ -184,6 +197,38 @@ def write_to_file(self, offset: int, file_name: str, value: typing.Iterable[int] def copy_bytes(self, source: int, amount: int, destination: int) -> None: self.write_token(APTokenTypes.COPY, destination, (amount, source)) +class PorPatchExtentions(APPatchExtension): + game = "Castlevania: Portrait of Ruin" + @staticmethod + def check_patch_version(caller: APProcedurePatch, rom: bytes) -> bytes: + rom = LocalRom(rom) + version_check = rom.read_from_file(0x02308F35, "overlay_119", 11) + version = version_check.rstrip(b"\x00") + version = version.decode("ascii") + if version != world_version: # Installed world is different from generated world + raise Exception(f"Error! this patch was generated on Portrait of Ruin APworld version: {version}, but installed APworld is version: {world_version}. " + + f"Please use APWorld version {version} to patch your game.") + return rom.get_bytes() + + def adjust_item_positions(caller: APProcedurePatch, rom: bytes) -> bytes: + rom = LocalRom(rom) + for location in location_data_table: + data = location_data_table[location] + if data.is_grounded and data.location_type == "Normal": + item_type = int.from_bytes(rom.read_from_file(data.pointer + 6, data.file, 1)) + item_id = int.from_bytes(rom.read_from_file(data.pointer + 10, data.file, 1)) + if (item_type == 1 and item_id < 4) or item_type == 8: # Coins and skills + y_pos = int.from_bytes(rom.read_from_file(data.pointer +2, data.file, 2), byteorder="little") + y_pos -= 0x10 + rom.write_to_file(data.pointer + 2, data.file, struct.pack("H", y_pos)) + + return rom.get_bytes() + + def apply_modifiers(caller: APProcedurePatch, rom: bytes) -> bytes: + print("TODO!") + rom = LocalRom(rom) + return rom.get_bytes() + def get_base_rom_bytes(file_name: str = "") -> bytes: base_rom_bytes = getattr(get_base_rom_bytes, "base_rom_bytes", None) if not base_rom_bytes: diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py index 86c1d802dac9..24e0c15b2184 100644 --- a/worlds/cv_por/__init__.py +++ b/worlds/cv_por/__init__.py @@ -35,6 +35,8 @@ class RomFile(settings.UserFilePath): copy_to = "CASTLEVANIA2_ACBEA4_00.nds" md5 = "2edd57540cae45842fbd19c45a4214f9" + rom_file: RomFile = RomFile(RomFile.copy_to) + class PoRWorld(World): """The year is 1944. Dracula's castle has reappeared, and it's been taken over From 70b3d069d207f1ddca9c3d0ba543944a031641af Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Thu, 30 Apr 2026 05:56:51 -0500 Subject: [PATCH 53/58] Fix quests --- worlds/cv_por/Rom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/cv_por/Rom.py b/worlds/cv_por/Rom.py index 7c7fc82f7b6d..c30520d9d02f 100644 --- a/worlds/cv_por/Rom.py +++ b/worlds/cv_por/Rom.py @@ -160,7 +160,7 @@ def patch_rom(world, rom, code_patch): elif data.location_type == "Cutscene": rom.write_to_file(data.pointer, data.file, bytearray([item_id, item_type, color])) elif data.location_type == "Quest": - rom.write_to_file(data.pointer, data.file, bytearray([item_type, item_id])) + rom.write_to_file(data.pointer, data.file, bytearray([item_id, item_type])) else: raise ValueError(f"Error! Location {location.name} has invalid location type {data.location_type}!") From b49cf543a3213c079e16d49435ee4159349d047b Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Thu, 30 Apr 2026 17:58:56 -0500 Subject: [PATCH 54/58] Update the ASM file to make quests use the new format --- worlds/cv_por/Rom.py | 3 +- worlds/cv_por/src/por_ap_main.asm | 114 +++++++++++++++++++++++++++++- 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/worlds/cv_por/Rom.py b/worlds/cv_por/Rom.py index c30520d9d02f..436c96bb3175 100644 --- a/worlds/cv_por/Rom.py +++ b/worlds/cv_por/Rom.py @@ -225,8 +225,9 @@ def adjust_item_positions(caller: APProcedurePatch, rom: bytes) -> bytes: return rom.get_bytes() def apply_modifiers(caller: APProcedurePatch, rom: bytes) -> bytes: - print("TODO!") rom = LocalRom(rom) + exp_multiplier = struct.unpack("H", rom.read_from_file(0x02309176, "overlay_119", 2))[0] # Read the multiplier + exp_multiplier = exp_multiplier / 100 return rom.get_bytes() def get_base_rom_bytes(file_name: str = "") -> bytes: diff --git a/worlds/cv_por/src/por_ap_main.asm b/worlds/cv_por/src/por_ap_main.asm index ecf399359300..e8f3f3c537b3 100644 --- a/worlds/cv_por/src/por_ap_main.asm +++ b/worlds/cv_por/src/por_ap_main.asm @@ -66,6 +66,118 @@ .org 0x020E537C .dw @PostBehemothRoom +;;;;;;;;;;;;;;;;;;;;;;;;; + ; Quest rewards, refitted for new format... + .org 0x020DFD40 ; Preparation + .dh 0x0862 + + .org 0x020DFD50 ; Meat + .dh 0x0345 + + .org 0x020DFD60 ; Bible + .dh 0x080E + + .org 0x020DFD70 ; Whip 2 + .dh 0x086A + + .org 0x020DFD80 ; Spinning Art + .dh 0x0866 + + .org 0x020DFD90 ; rocket slash + .dh 0x0826 + + .org 0x020DFDA0 ; thief ring + .dh 0x070B + + .org 0x020DFDb0 ; blessed ring + .dh 0x0711 + + .org 0x020DFDc0 ; holy water + .dh 0x080D + + .org 0x020DFDD0 ; martial art + .dh 0x0867 + + .org 0x020DFDE0 ; holy appearance + .dh 0x082F + + .org 0x020DFDF0 ; luck boost + .dh 0x0836 + + .org 0x020DFE00 ; mental 1 + .dh 0x0209 + + .org 0x020DFE10 ; mental 2 + .dh 0x0209 + + .org 0x020DFE20 ; spear of legend + .dh 0x0337 + + .org 0x020DFE30 ; mental 3 + .dh 0x0209 + + .org 0x020DFE40 ; nest of evil (UNUSED, REMOVE LATER?) + .dh 0x0100 + + .org 0x020DFE50 ; ghoul king + .dh 0x0710 + + .org 0x020DFE60 ; abandon greed + .dh 0x0709 + + .org 0x020DFE70 ; a rank huntor + .dh 0x032B + + .org 0x020DFE80 ; mental 4 + .dh 0x00209 + + .org 0x020DFE90 ; s rank huntor + .dh 0x0309 + + .org 0x020DFEA0 ; gambler + .dh 0x0502 + + .org 0x020DFEB0 ; time sotp + .dh 0x082E + + .org 0x020DFEC0 ; poison + .dh 0x0321 + + .org 0x020DFED0 ; str 1 + .dh 0x0208 + + .org 0x020DFEE0 ; str 2 + .dh 0x0208 + + .org 0x020DFEF0 ; stage + .dh 0x025A + + .org 0x020DFF00 ; str 3 + .dh 0x0208 + + .org 0x020DFF10 ; cross + .dh 0x080C + + .org 0x020DFF20 ; str 4 + .dh 0x0208 + + .org 0x020DFF30 ; page + .dh 0x0314 + + .org 0x020DFF40 ; tasks + .dh 0x070A + + .org 0x020DFF50 ; holy power + .dh 0x0858 + + .org 0x020DFF60 ; almighty + .dh 0x0325 + + .org 0x020DFF70 ; sage + .dh 0x071F + + .org 0x020DFF80 ; gergoth + .dh 0x084A ; Fix the map explore/suspend bug .org 0x0202E5F8 @@ -365,7 +477,7 @@ .db 0x00 @OptionFlag_EXPMult: ;02309176 - .db 0x00 + .dh 0x0000 .align 4 ;;;;;;;;;;;;;;;;;;; From 2b6d44bd750f49be2bf7d7e137546dfd4e27efff Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Thu, 30 Apr 2026 21:13:22 -0500 Subject: [PATCH 55/58] Implement EXP Multiplier --- worlds/cv_por/Rom.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/worlds/cv_por/Rom.py b/worlds/cv_por/Rom.py index 436c96bb3175..600f1c94f618 100644 --- a/worlds/cv_por/Rom.py +++ b/worlds/cv_por/Rom.py @@ -104,6 +104,7 @@ def patch_rom(world, rom, code_patch): rom.write_to_file(0x02309173, "overlay_119", bytearray([world.options.reveal_hidden_walls.value])) # Reveal hidden walls rom.write_to_file(0x02309174, "overlay_119", bytearray([world.options.start_with_change_cube.value])) # Start with Change Cube rom.write_to_file(0x02309175, "overlay_119", bytearray([world.options.reveal_map.value])) # Reveal map + rom.write_to_file(0x02309176, "overlay_119", struct.pack("H", world.options.experience_percentage.value)) # ExP Multiplier if world.options.reveal_map: rom.write_to_file(0x0202F3B0, "arm9", bytearray([0x00, 0x00, 0xA0, 0xE1])) # Nop out the instruction that hides room borders @@ -228,6 +229,11 @@ def apply_modifiers(caller: APProcedurePatch, rom: bytes) -> bytes: rom = LocalRom(rom) exp_multiplier = struct.unpack("H", rom.read_from_file(0x02309176, "overlay_119", 2))[0] # Read the multiplier exp_multiplier = exp_multiplier / 100 + + for i in range(0x9A): + address = 0x020BE568 + (0x20 * i) + enemy_exp = struct.unpack("H", rom.read_from_file(address + 16, "arm9", 2))[0] + enemy_exp = int(min(0xFFFF, (enemy_exp * exp_multiplier))) return rom.get_bytes() def get_base_rom_bytes(file_name: str = "") -> bytes: From c1816b709d925be191ca03cec1cc3839f9890482 Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Thu, 30 Apr 2026 21:16:29 -0500 Subject: [PATCH 56/58] Update patch --- worlds/cv_por/src/por_base.bsdiff4 | Bin 5183 -> 5249 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/worlds/cv_por/src/por_base.bsdiff4 b/worlds/cv_por/src/por_base.bsdiff4 index 7dd13cfd5a19550020950af3b5b801022d7688d9..d96347c608093a2b4fe025eca41b3a2d13e2b4d9 100644 GIT binary patch delta 2094 zcmV+}2+{YyD1j*wLQ_OZMn*I+o&^8^000002MDnewgrC&@!oO(09*h6|NsC0|NsC0 z|Lug_Aaw~cr`SIH1tH#>S>TY zMxL4xpkfA^G{Ovx0LaOvgGNBp)MU^MfWjM7Oc2mAdW@KcjT!)YntC8;Z9_l+(?chq zfubbRW~qOm9#ccq2GsREO_3Qi0ibE>01r^pA(}>jGZY@6(V%*OGynhwfCiX@KmZK^ zkPQF>L7^1MGHPa~jZZ-vKm=eB>NFWK7>oqS(*Pl$7zjNC#0CKYqZ2@4WW>pWdH_RA zng-Nl$)+KbPeN#EhNq-cLY^t6Lp2zNhMF`083TVo>S!7O13&-(00w{n06jne&;S4c z0gwYg0qO>W)b$7e14BRtfB*nA8UO&$00006fHVLDKmZ1S0000oWM}{Y00E!?5h)UF zN_eBvGypv#A?g9305UNE2AT(`Gynk5G#;P;&;jZV000>P00000dVl~L23Nx+=osGC zL9l=CqyVA%g#@q%2CC2q@c^KZ95=MNU}BJt@5PHk*Sp(hU}J!Z6&CaoilQztfe9ut zXbWUg1STzQgNO7iTey=2rT-s8Po5P{xo;edKk4Gu9=KZL%91ppX*vnJguqEs(Te2) zFY*L6hlwP%lj9@UIKs#Eq!f}%D=P{%<>&WNe6z*G>rilPn#QB{8~ zqJkHZKno&(pxi(KLXcHeE+UAEY;Z&qH3Ssn;x)kmcu0T>T8JT*ii(9IqBi0niE|M^ zT0j?Til_h>xQHm3n1F)S000#)AS$j>ASkBk1O!C<;;5jS1VBIr$bg_@AbbA|x6|q-ZU~L5p*qZiA*_4V{VP1WRH96jl4dIdq04OAL;32(_I24r~ zMH9CS9u)wcOr^>OYdmlQ(h+}6xs(iG7#NIZQHmKs29*jvM0VKQBt^409E3ViAcC7= zC}rm0Pgf*ZRBXSYDl?foO=bafR9h|8(pURWFuf%g0vtVE_{$LQ0EXnXkX1P8PvJVa zhJg#C!%|0*y~dMi%fR+H9wKMdt5Z6nHwOqKB$^K;nM{AmuPCu=Qia_^ zf_ysq3!BE97!+#A&P9+TaO#hIDd}2G;wFN-I>aHYQE0}OI}kxpNg5eV0T@jf0_J2> zPHSvJ101TclW2cYRUs=dNc*boOmX?2Gf783L!`5h3>rhH=g2mHBG4DR;t8m<>d7Rm z1XrYo#ZzTaj&y-4LWO^`EQX>dnS=C6VG8-$)C5UV6QesD+9I&9ZRa(D>BmrwLiu;q zX;@q*8>vg{y3gU1q179N_$EBGidQ8N*Art4CZv7xuVIfmFkYb)E2IER#&Lz;{swX~RgEK)zhy2t>TBpXS zj9Vb{B2HqEdIX1h6oVkw@sJ=ZK|3MGAoFIJ7W}y-$h5B~m(Zkkn;*q0<&wJa%JYx=?15K zS!>Bs9yqcg0Qu{oxnz^-p?%8|spMXv+zD+*zv6aA@3?=Cfa(WR*JvD>8F_^V+3b1)SS|W;3NnT5*Lc$ER53jZ1;oR}x}KHFb8{^{uPI|+-6{V!TqAl$IRHXA zFc2zai=;wehhkVqx@b)sJBNW0K@k+>tbk_9QbJ(_za~G{th5!7ma^A(HQL+q`CO-U zx#9U2_c_kKlvS*E`;)jfFk_=L8>=Kf*3pTZG*W*jJTVz>V>cI!c758r+S4j~VpH;G z@}+Nq+hV7F9OdLhLpa1{> delta 2027 zcmX|*dpy(oAICpqzL@Q7?o2t`a+y^wGryxyn=R#1HbY2aQD1&UKbNGmxg2du7q=#k z-%RF`>LeTrA=l{q*3UY}y%S2DE+S4T_S5;}_k28F@Au>Je!m~j$2-mHAz8O25OSps+&P^lQMyi($Rb+>QWZjP$kw*x8-LxThmv7BIsrqciogaaDl z3cRtT8VSHt8G(k6G_$BI=SE_=3<(~V0|1puRQYrO0~iv(K)qUGDxskT00ZESikJr` zY{54X3;?2>urc0%3T1e0*#8ED|JGoD^x(QT=$gs<%jU7{zLX6WZP~PJe#4g{zyQp~ z2O9<`gARTT$l!uP+X z|9PTrXZhm2knY;vbK4|IksIRqDR2G7?_qU56^Ta%({F~(Vz$NiQ+0u?ixZwYA8Mi5mCt*Z3G+^2yQ($o1cL2PL6Yq#E# zh|^-T%#0C16<@jLJYm!V2U;f;vl#n*2;1Wju~Q)DB#RoS{gHsG3TANB&j@NNy4bF- zG!Ri_)OSdrXQk%nGfEc94)8V7kMq)tgcvqGghWs&?;}x z)@2sT={;A2XV71x(yp()mQ}(}JylcHoQ=evsA$3s(H6qAJJEe%;X|ap)fxsoN)Pu*3BLTH5{0vD{ppQv!ja zZ{N`JgsjUMPO>>B5FmOajm?W7(Ou!qJWTKK-WwJY?TA#8m}0AUQRYIv zoacz<-|Sq+{M47`rNW+(je9MB2n-$Jy9V&>Y@-{D+{m_$-x;mlX?`ud$_qD4-!9gT zCYic>T`*#J+uumjd?x<^X{GBQ%JDeR9GFbR;ILT9N^M68-r`b~X+4T{-&Uk*7%M#% zVgD!Z>|$R#(n&gec(iZ4#%0*eLZ)lI30IstWWn5XC(%(n3>PPqy;S%&znuAiH0E3B zlLE$dGD7KE{bX5|d<=AmXk*7Pq&Z9Vi1_TxZj$q7s!c%Qep1`f5g;GBw!|5XQE?H3 zE@WGh2cF&8`kGRjZiYILpP68$-bJ`t;Csz()nUr0(=y@#+VGxjB==mo%Q{+^CEs`aoNT;T{0u+bS%)HL z?4U%?N~3CS-_qSR^tj&X zZ(q%6%Nm`#{>#BAXW8CA`bP>bPo7;$`JF1k4mg{}TK9iGD?B{k!>aK2o8X&l#mQ>E z#?Oz}->T#tk16Q#Zg8ucZek}hS{d|y!bQrTF@?{KVpf8&@3FK_xFu_(Pk=4FvMI}` zci~iYxiXmYs5jfGtTE)UVsYRtm#6n)!O2!51i!3}K@uRYuOr$BWr*J)5<4(5z9OFa zLzCME^x<5MOc{ByQyj?m{1N_8Lm$X}+a=}rSE&3(yBU``!GHDFfhhy6{r2nFlOiwV z4HJ!9+NzPyyH}t?@N%ZivJk&u@^b+G@^(@k_sgs2O-Xa(JI`B`pSx&~;Lt?kcEXMU+|4F5Gc(Q=g^kIK2c+1-HV)%kDYwTFkM8wNv{I4#Wvt96TIST+0Y&PrLI zPzB&fBqV7Qt)T5qIcajt>Ew0sR;E{!xIe1_Dt4fHT-EMnuI)QuL Hm=@@NnYm?3 From 4a5d6346e88a5f4f4997aed47e9bc143d2a0459e Mon Sep 17 00:00:00 2001 From: PinkSwitch Date: Thu, 30 Apr 2026 21:52:17 -0500 Subject: [PATCH 57/58] Fill slot stuff --- worlds/cv_por/__init__.py | 25 ++++++++----------------- worlds/cv_por/generator_main.py | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/worlds/cv_por/__init__.py b/worlds/cv_por/__init__.py index 24e0c15b2184..666670ec24d7 100644 --- a/worlds/cv_por/__init__.py +++ b/worlds/cv_por/__init__.py @@ -8,7 +8,8 @@ from .Items import item_table, get_item_names_per_category from .Options import PoROptions, por_option_groups from .static_location_data import location_ids -from .generator_main import CVPoRItem, generate_early, create_regions +from .generator_main import (CVPoRItem, generate_early, create_regions, fill_slot_data, + modify_multidata, generate_output, create_items, get_filler_item_name, set_rules) from .Client import PoRClient class PoRWeb(WebWorld): @@ -59,7 +60,13 @@ class PoRWorld(World): options_dataclass = PoROptions options: PoROptions generate_early = generate_early + create_items = create_items create_regions = create_regions + fill_slot_data = fill_slot_data + modify_multidata = modify_multidata + generate_output = generate_output + get_filler_item_name = get_filler_item_name + set_rules = set_rules # locked_locations: List[str] # ocation_cache: List[Location] @@ -314,22 +321,6 @@ def __init__(self, multiworld: MultiWorld, player: int): ] - def create_items(self): - from .generator_main import create_items - create_items(self) - - def set_rules(self): - from .generator_main import set_rules - set_rules(self) - - def generate_output(self, output_directory: str) -> None: - from .generator_main import generate_output - generate_output(self, output_directory) - - def get_filler_item_name(self): - from .generator_main import get_filler_item_name - return get_filler_item_name(self) - def create_item(self, name: str) -> CVPoRItem: from .generator_main import set_classifications data = set_classifications(self, name) diff --git a/worlds/cv_por/generator_main.py b/worlds/cv_por/generator_main.py index 63ce7f3b741e..27de0892fa5d 100644 --- a/worlds/cv_por/generator_main.py +++ b/worlds/cv_por/generator_main.py @@ -1,7 +1,9 @@ import os +import typing import pkgutil from BaseClasses import Item +from typing import Dict from .Items import item_table @@ -16,6 +18,7 @@ def generate_early(world) -> None: return passthrough = world.multiworld.re_gen_passthrough["Castlevania: Portrait of Ruin"] world.options.goal = passthrough["goal"] + world.options.start_with_change_cube = passthrough["start_with_change_cube"] world.options.brauner_portraits = passthrough["brauner_portraits"] world.options.dracula_portraits = passthrough["dracula_portraits"] world.options.nest_portraits = passthrough["nest_portraits"] @@ -104,6 +107,24 @@ def generate_output(world, output_directory: str) -> None: finally: world.rom_name_available_event.set() # make sure threading continues and errors are collected +def modify_multidata(world, multidata: dict) -> None: + # wait for self.rom_name to be available. + world.rom_name_available_event.wait() + rom_name = getattr(world, "rom_name", None) + if rom_name: + multidata["connect_names"][world.rom_name] = multidata["connect_names"][world.multiworld.player_name[world.player]] + +def fill_slot_data(world) -> Dict[str, typing.Any]: + return { + "goal": world.options.goal.value, + "start_with_change_cube": world.options.start_with_change_cube.value, + "brauner_portraits": world.options.brauner_portraits.value, + "dracula_portrtaits": world.options.dracula_portraits.value, + "nest_portrtaits": world.options.nest_portraits.value, + "nest_of_evil": world.options.nest_of_evil_state.value, + "brauner_required": world.options.brauner_required.value + } + def get_filler_item_name(world) -> str: from .Items import money_table, good_food_table, consumable_table From 35772205f36084b7eddb186147ca7047a4a0209c Mon Sep 17 00:00:00 2001 From: Emily Venezia Date: Thu, 30 Apr 2026 23:46:46 -0400 Subject: [PATCH 58/58] fix item link issue --- worlds/cv_por/generator_main.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/worlds/cv_por/generator_main.py b/worlds/cv_por/generator_main.py index 27de0892fa5d..3454edf87c0d 100644 --- a/worlds/cv_por/generator_main.py +++ b/worlds/cv_por/generator_main.py @@ -130,8 +130,7 @@ def get_filler_item_name(world) -> str: from .Items import money_table, good_food_table, consumable_table weights = {"subweapon": 5, "good_weapon": 7, "accessory": 8, "good_food": 10, "good_armor": 15, "money": 20, "weapon": 30, "armor": 40, "consumable": 60} - - filler_type = world.random.choices(list(weights), weights=list(weights.values()), k=1)[0] + weight_table = { "subweapon": world.subweapon_filler_table, "good_weapon": world.good_weapon_table, @@ -143,7 +142,11 @@ def get_filler_item_name(world) -> str: "good_food": good_food_table, "accessory": world.accessory_table } + for fill_type, table in weight_table.items(): + if not table: # Remove empty tables to prevent them from being chosen + weights[fill_type] = 0 + filler_type = world.random.choices(list(weights), weights=list(weights.values()), k=1)[0] filler_item = world.random.choice(weight_table[filler_type]) if not world.has_tried_magus_ring: world.has_tried_magus_ring = True @@ -154,9 +157,6 @@ def get_filler_item_name(world) -> str: if filler_type not in ["consumable", "good_food", "money"]: weight_table[filler_type].remove(filler_item) # Remove equipment from the corresponding table so it doesn't gen again - if not weight_table[filler_type]: # If we have exhausted the entire pool - weights[filler_type] = 0 # Make sure it won't be rolled again - return filler_item