diff --git a/test/bases.py b/test/bases.py index 2d4111d1..07a3e600 100644 --- a/test/bases.py +++ b/test/bases.py @@ -10,7 +10,7 @@ from worlds import AutoWorld from worlds.AutoWorld import World, call_all from BaseClasses import Location, MultiWorld, CollectionState, ItemClassification, Item -from worlds.alttp.Items import ItemFactory +from worlds.alttp.Items import item_factory class TestBase(unittest.TestCase): @@ -91,15 +91,15 @@ class TestBase(unittest.TestCase): items = self.multiworld.itempool[:] items = [item for item in items if item.name not in all_except and not ("Bottle" in item.name and "AnyBottle" in all_except)] - items.extend(ItemFactory(item_pool[0], 1)) + items.extend(item_factory(item_pool[0], self.multiworld.worlds[1])) else: - items = ItemFactory(item_pool[0], 1) + items = item_factory(item_pool[0], self.multiworld.worlds[1]) return self.get_state(items) def _get_items_partial(self, item_pool, missing_item): new_items = item_pool[0].copy() new_items.remove(missing_item) - items = ItemFactory(new_items, 1) + items = item_factory(new_items, self.multiworld.worlds[1]) return self.get_state(items) diff --git a/test/general/test_items.py b/test/general/test_items.py index 16129372..82b60303 100644 --- a/test/general/test_items.py +++ b/test/general/test_items.py @@ -8,7 +8,7 @@ class TestBase(unittest.TestCase): def test_create_item(self): """Test that a world can successfully create all items in its datapackage""" for game_name, world_type in AutoWorldRegister.world_types.items(): - proxy_world = world_type(None, 0) # this is identical to MultiServer.py creating worlds + proxy_world = setup_solo_multiworld(world_type, ()).worlds[1] for item_name in world_type.item_name_to_id: with self.subTest("Create Item", item_name=item_name, game_name=game_name): item = proxy_world.create_item(item_name) diff --git a/worlds/AutoWorld.py b/worlds/AutoWorld.py index 41796371..93e4a5c3 100644 --- a/worlds/AutoWorld.py +++ b/worlds/AutoWorld.py @@ -296,6 +296,7 @@ class World(metaclass=AutoWorldRegister): """path it was loaded from""" def __init__(self, multiworld: "MultiWorld", player: int): + assert multiworld is not None self.multiworld = multiworld self.player = player diff --git a/worlds/alttp/Dungeons.py b/worlds/alttp/Dungeons.py index c886fce9..f0b8c2d9 100644 --- a/worlds/alttp/Dungeons.py +++ b/worlds/alttp/Dungeons.py @@ -7,7 +7,7 @@ from BaseClasses import CollectionState, Region, MultiWorld from Fill import fill_restrictive from .Bosses import BossFactory, Boss -from .Items import ItemFactory +from .Items import item_factory from .Regions import lookup_boss_drops, key_drop_data from .Options import small_key_shuffle @@ -81,90 +81,90 @@ def create_dungeons(world: "ALTTPWorld"): return dungeon ES = make_dungeon('Hyrule Castle', None, ['Hyrule Castle', 'Sewers', 'Sewer Drop', 'Sewers (Dark)', 'Sanctuary'], - ItemFactory('Big Key (Hyrule Castle)', player), - ItemFactory(['Small Key (Hyrule Castle)'] * 4, player), - [ItemFactory('Map (Hyrule Castle)', player)]) + item_factory('Big Key (Hyrule Castle)', world), + item_factory(['Small Key (Hyrule Castle)'] * 4, world), + [item_factory('Map (Hyrule Castle)', world)]) EP = make_dungeon('Eastern Palace', 'Armos Knights', ['Eastern Palace'], - ItemFactory('Big Key (Eastern Palace)', player), - ItemFactory(['Small Key (Eastern Palace)'] * 2, player), - ItemFactory(['Map (Eastern Palace)', 'Compass (Eastern Palace)'], player)) + item_factory('Big Key (Eastern Palace)', world), + item_factory(['Small Key (Eastern Palace)'] * 2, world), + item_factory(['Map (Eastern Palace)', 'Compass (Eastern Palace)'], world)) DP = make_dungeon('Desert Palace', 'Lanmolas', ['Desert Palace North', 'Desert Palace Main (Inner)', 'Desert Palace Main (Outer)', - 'Desert Palace East'], ItemFactory('Big Key (Desert Palace)', player), - ItemFactory(['Small Key (Desert Palace)'] * 4, player), - ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)'], player)) + 'Desert Palace East'], item_factory('Big Key (Desert Palace)', world), + item_factory(['Small Key (Desert Palace)'] * 4, world), + item_factory(['Map (Desert Palace)', 'Compass (Desert Palace)'], world)) ToH = make_dungeon('Tower of Hera', 'Moldorm', ['Tower of Hera (Bottom)', 'Tower of Hera (Basement)', 'Tower of Hera (Top)'], - ItemFactory('Big Key (Tower of Hera)', player), - [ItemFactory('Small Key (Tower of Hera)', player)], - ItemFactory(['Map (Tower of Hera)', 'Compass (Tower of Hera)'], player)) + item_factory('Big Key (Tower of Hera)', world), + [item_factory('Small Key (Tower of Hera)', world)], + item_factory(['Map (Tower of Hera)', 'Compass (Tower of Hera)'], world)) PoD = make_dungeon('Palace of Darkness', 'Helmasaur King', ['Palace of Darkness (Entrance)', 'Palace of Darkness (Center)', 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness (Bonk Section)', 'Palace of Darkness (North)', 'Palace of Darkness (Maze)', 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness (Final Section)'], - ItemFactory('Big Key (Palace of Darkness)', player), - ItemFactory(['Small Key (Palace of Darkness)'] * 6, player), - ItemFactory(['Map (Palace of Darkness)', 'Compass (Palace of Darkness)'], player)) + item_factory('Big Key (Palace of Darkness)', world), + item_factory(['Small Key (Palace of Darkness)'] * 6, world), + item_factory(['Map (Palace of Darkness)', 'Compass (Palace of Darkness)'], world)) TT = make_dungeon('Thieves Town', 'Blind', ['Thieves Town (Entrance)', 'Thieves Town (Deep)', 'Blind Fight'], - ItemFactory('Big Key (Thieves Town)', player), - ItemFactory(['Small Key (Thieves Town)'] * 3, player), - ItemFactory(['Map (Thieves Town)', 'Compass (Thieves Town)'], player)) + item_factory('Big Key (Thieves Town)', world), + item_factory(['Small Key (Thieves Town)'] * 3, world), + item_factory(['Map (Thieves Town)', 'Compass (Thieves Town)'], world)) SW = make_dungeon('Skull Woods', 'Mothula', ['Skull Woods Final Section (Entrance)', 'Skull Woods First Section', 'Skull Woods Second Section', 'Skull Woods Second Section (Drop)', 'Skull Woods Final Section (Mothula)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Left)', 'Skull Woods First Section (Top)'], - ItemFactory('Big Key (Skull Woods)', player), - ItemFactory(['Small Key (Skull Woods)'] * 5, player), - ItemFactory(['Map (Skull Woods)', 'Compass (Skull Woods)'], player)) + item_factory('Big Key (Skull Woods)', world), + item_factory(['Small Key (Skull Woods)'] * 5, world), + item_factory(['Map (Skull Woods)', 'Compass (Skull Woods)'], world)) SP = make_dungeon('Swamp Palace', 'Arrghus', ['Swamp Palace (Entrance)', 'Swamp Palace (First Room)', 'Swamp Palace (Starting Area)', 'Swamp Palace (West)', 'Swamp Palace (Center)', 'Swamp Palace (North)'], - ItemFactory('Big Key (Swamp Palace)', player), - ItemFactory(['Small Key (Swamp Palace)'] * 6, player), - ItemFactory(['Map (Swamp Palace)', 'Compass (Swamp Palace)'], player)) + item_factory('Big Key (Swamp Palace)', world), + item_factory(['Small Key (Swamp Palace)'] * 6, world), + item_factory(['Map (Swamp Palace)', 'Compass (Swamp Palace)'], world)) IP = make_dungeon('Ice Palace', 'Kholdstare', ['Ice Palace (Entrance)', 'Ice Palace (Second Section)', 'Ice Palace (Main)', 'Ice Palace (East)', - 'Ice Palace (East Top)', 'Ice Palace (Kholdstare)'], ItemFactory('Big Key (Ice Palace)', player), - ItemFactory(['Small Key (Ice Palace)'] * 6, player), - ItemFactory(['Map (Ice Palace)', 'Compass (Ice Palace)'], player)) + 'Ice Palace (East Top)', 'Ice Palace (Kholdstare)'], item_factory('Big Key (Ice Palace)', world), + item_factory(['Small Key (Ice Palace)'] * 6, world), + item_factory(['Map (Ice Palace)', 'Compass (Ice Palace)'], world)) MM = make_dungeon('Misery Mire', 'Vitreous', ['Misery Mire (Entrance)', 'Misery Mire (Main)', 'Misery Mire (West)', 'Misery Mire (Final Area)', - 'Misery Mire (Vitreous)'], ItemFactory('Big Key (Misery Mire)', player), - ItemFactory(['Small Key (Misery Mire)'] * 6, player), - ItemFactory(['Map (Misery Mire)', 'Compass (Misery Mire)'], player)) + 'Misery Mire (Vitreous)'], item_factory('Big Key (Misery Mire)', world), + item_factory(['Small Key (Misery Mire)'] * 6, world), + item_factory(['Map (Misery Mire)', 'Compass (Misery Mire)'], world)) TR = make_dungeon('Turtle Rock', 'Trinexx', ['Turtle Rock (Entrance)', 'Turtle Rock (First Section)', 'Turtle Rock (Chain Chomp Room)', 'Turtle Rock (Pokey Room)', 'Turtle Rock (Second Section)', 'Turtle Rock (Big Chest)', 'Turtle Rock (Crystaroller Room)', 'Turtle Rock (Dark Room)', 'Turtle Rock (Eye Bridge)', 'Turtle Rock (Trinexx)'], - ItemFactory('Big Key (Turtle Rock)', player), - ItemFactory(['Small Key (Turtle Rock)'] * 6, player), - ItemFactory(['Map (Turtle Rock)', 'Compass (Turtle Rock)'], player)) + item_factory('Big Key (Turtle Rock)', world), + item_factory(['Small Key (Turtle Rock)'] * 6, world), + item_factory(['Map (Turtle Rock)', 'Compass (Turtle Rock)'], world)) if multiworld.mode[player] != 'inverted': AT = make_dungeon('Agahnims Tower', 'Agahnim', ['Agahnims Tower', 'Agahnim 1'], None, - ItemFactory(['Small Key (Agahnims Tower)'] * 4, player), []) + item_factory(['Small Key (Agahnims Tower)'] * 4, world), []) GT = make_dungeon('Ganons Tower', 'Agahnim2', ['Ganons Tower (Entrance)', 'Ganons Tower (Tile Room)', 'Ganons Tower (Compass Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower (Map Room)', 'Ganons Tower (Firesnake Room)', 'Ganons Tower (Teleport Room)', 'Ganons Tower (Bottom)', 'Ganons Tower (Top)', 'Ganons Tower (Before Moldorm)', 'Ganons Tower (Moldorm)', 'Agahnim 2'], - ItemFactory('Big Key (Ganons Tower)', player), - ItemFactory(['Small Key (Ganons Tower)'] * 8, player), - ItemFactory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], player)) + item_factory('Big Key (Ganons Tower)', world), + item_factory(['Small Key (Ganons Tower)'] * 8, world), + item_factory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], world)) else: AT = make_dungeon('Inverted Agahnims Tower', 'Agahnim', ['Inverted Agahnims Tower', 'Agahnim 1'], None, - ItemFactory(['Small Key (Agahnims Tower)'] * 4, player), []) + item_factory(['Small Key (Agahnims Tower)'] * 4, world), []) GT = make_dungeon('Inverted Ganons Tower', 'Agahnim2', ['Inverted Ganons Tower (Entrance)', 'Ganons Tower (Tile Room)', 'Ganons Tower (Compass Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower (Map Room)', 'Ganons Tower (Firesnake Room)', 'Ganons Tower (Teleport Room)', 'Ganons Tower (Bottom)', 'Ganons Tower (Top)', 'Ganons Tower (Before Moldorm)', 'Ganons Tower (Moldorm)', - 'Agahnim 2'], ItemFactory('Big Key (Ganons Tower)', player), - ItemFactory(['Small Key (Ganons Tower)'] * 8, player), - ItemFactory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], player)) + 'Agahnim 2'], item_factory('Big Key (Ganons Tower)', world), + item_factory(['Small Key (Ganons Tower)'] * 8, world), + item_factory(['Map (Ganons Tower)', 'Compass (Ganons Tower)'], world)) GT.bosses['bottom'] = BossFactory('Armos Knights', player) GT.bosses['middle'] = BossFactory('Lanmolas', player) @@ -259,7 +259,7 @@ def fill_dungeons_restrictive(multiworld: MultiWorld): if not key_drop_shuffle and player not in multiworld.groups: for key_loc in key_drop_data: key_data = key_drop_data[key_loc] - all_state_base.remove(ItemFactory(key_data[3], player)) + all_state_base.remove(item_factory(key_data[3], multiworld.worlds[player])) loc = multiworld.get_location(key_loc, player) if loc in all_state_base.events: diff --git a/worlds/alttp/ItemPool.py b/worlds/alttp/ItemPool.py index bb5bbaa6..7a82d4c6 100644 --- a/worlds/alttp/ItemPool.py +++ b/worlds/alttp/ItemPool.py @@ -9,8 +9,8 @@ from .Shops import TakeAny, total_shop_slots, set_up_shops, shop_table_by_locati from .Bosses import place_bosses from .Dungeons import get_dungeon_item_pool_player from .EntranceShuffle import connect_entrance -from .Items import ItemFactory, GetBeemizerItem, trap_replaceable, item_name_groups -from .Options import small_key_shuffle, compass_shuffle, big_key_shuffle, map_shuffle, TriforcePiecesMode +from .Items import item_factory, GetBeemizerItem, trap_replaceable, item_name_groups +from .Options import small_key_shuffle, compass_shuffle, big_key_shuffle, map_shuffle, TriforcePiecesMode, LTTPBosses from .StateHelpers import has_triforce_pieces, has_melee_weapon from .Regions import key_drop_data @@ -240,9 +240,9 @@ def generate_itempool(world): if multiworld.timer[player] in ['ohko', 'timed_ohko']: multiworld.can_take_damage[player] = False if multiworld.goal[player] in ['pedestal', 'triforce_hunt', 'local_triforce_hunt']: - multiworld.push_item(multiworld.get_location('Ganon', player), ItemFactory('Nothing', player), False) + multiworld.push_item(multiworld.get_location('Ganon', player), item_factory('Nothing', world), False) else: - multiworld.push_item(multiworld.get_location('Ganon', player), ItemFactory('Triforce', player), False) + multiworld.push_item(multiworld.get_location('Ganon', player), item_factory('Triforce', world), False) if multiworld.goal[player] in ['triforce_hunt', 'local_triforce_hunt']: region = multiworld.get_region('Light World', player) @@ -252,7 +252,7 @@ def generate_itempool(world): region.locations.append(loc) - multiworld.push_item(loc, ItemFactory('Triforce', player), False) + multiworld.push_item(loc, item_factory('Triforce', world), False) loc.event = True loc.locked = True @@ -271,7 +271,7 @@ def generate_itempool(world): ] for location_name, event_name in event_pairs: location = multiworld.get_location(location_name, player) - event = ItemFactory(event_name, player) + event = item_factory(event_name, world) multiworld.push_item(location, event, False) location.event = location.locked = True @@ -287,7 +287,7 @@ def generate_itempool(world): treasure_hunt_icon, additional_triforce_pieces = get_pool_core(multiworld, player) for item in precollected_items: - multiworld.push_precollected(ItemFactory(item, player)) + multiworld.push_precollected(item_factory(item, world)) if multiworld.mode[player] == 'standard' and not has_melee_weapon(multiworld.state, player): if "Link's Uncle" not in placed_items: @@ -326,9 +326,9 @@ def generate_itempool(world): multiworld.escape_assist[player].append('bombs') for (location, item) in placed_items.items(): - multiworld.get_location(location, player).place_locked_item(ItemFactory(item, player)) + multiworld.get_location(location, player).place_locked_item(item_factory(item, world)) - items = ItemFactory(pool, player) + items = item_factory(pool, world) # convert one Progressive Bow into Progressive Bow (Alt), in ID only, for ganon silvers hint text if multiworld.worlds[player].has_progressive_bows: for item in items: @@ -349,7 +349,7 @@ def generate_itempool(world): for key_loc in key_drop_data: key_data = key_drop_data[key_loc] - drop_item = ItemFactory(key_data[3], player) + drop_item = item_factory(key_data[3], world) if not multiworld.key_drop_shuffle[player]: if drop_item in dungeon_items: dungeon_items.remove(drop_item) @@ -370,7 +370,7 @@ def generate_itempool(world): loc.address = None elif "Small" in key_data[3] and multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal: # key drop shuffle and universal keys are on. Add universal keys in place of key drop keys. - multiworld.itempool.append(ItemFactory(GetBeemizerItem(multiworld, player, 'Small Key (Universal)'), player)) + multiworld.itempool.append(item_factory(GetBeemizerItem(multiworld, player, 'Small Key (Universal)'), world)) dungeon_item_replacements = sum(difficulties[multiworld.difficulty[player]].extras, []) * 2 multiworld.random.shuffle(dungeon_item_replacements) @@ -382,7 +382,7 @@ def generate_itempool(world): or (multiworld.map_shuffle[player] == map_shuffle.option_start_with and item.type == 'Map')): dungeon_items.pop(x) multiworld.push_precollected(item) - multiworld.itempool.append(ItemFactory(dungeon_item_replacements.pop(), player)) + multiworld.itempool.append(item_factory(dungeon_item_replacements.pop(), world)) multiworld.itempool.extend([item for item in dungeon_items]) set_up_shops(multiworld, player) @@ -394,7 +394,7 @@ def generate_itempool(world): location.shop_slot is not None] for location in shop_locations: if location.shop.inventory[location.shop_slot]["item"] == "Single Arrow": - location.place_locked_item(ItemFactory("Single Arrow", player)) + location.place_locked_item(item_factory("Single Arrow", world)) else: shop_items += 1 else: @@ -406,9 +406,9 @@ def generate_itempool(world): multiworld.small_key_shuffle[player] == small_key_shuffle.option_universal) * 0.5 for _ in range(shop_items): if multiworld.random.random() < chance_100: - items.append(ItemFactory(GetBeemizerItem(multiworld, player, "Rupees (100)"), player)) + items.append(item_factory(GetBeemizerItem(multiworld, player, "Rupees (100)"), world)) else: - items.append(ItemFactory(GetBeemizerItem(multiworld, player, "Rupees (50)"), player)) + items.append(item_factory(GetBeemizerItem(multiworld, player, "Rupees (50)"), world)) multiworld.random.shuffle(items) pool_count = len(items) @@ -431,7 +431,7 @@ def generate_itempool(world): new_items += ["Arrow Upgrade (+5)"] * 6 new_items.append("Arrow Upgrade (+5)" if progressive else "Arrow Upgrade (+10)") - items += [ItemFactory(item, player) for item in new_items] + items += [item_factory(item, world) for item in new_items] removed_filler = [] multiworld.random.shuffle(items) # Decide what gets tossed randomly. @@ -444,22 +444,22 @@ def generate_itempool(world): else: # no more junk to remove, condense progressive items def condense_items(items, small_item, big_item, rem, add): - small_item = ItemFactory(small_item, player) + small_item = item_factory(small_item, world) # while (len(items) >= pool_count + rem - 1 # minus 1 to account for the replacement item # and items.count(small_item) >= rem): if items.count(small_item) >= rem: for _ in range(rem): items.remove(small_item) - removed_filler.append(ItemFactory(small_item.name, player)) - items += [ItemFactory(big_item, player) for _ in range(add)] + removed_filler.append(item_factory(small_item.name, world)) + items += [item_factory(big_item, world) for _ in range(add)] return True return False def cut_item(items, item_to_cut, minimum_items): - item_to_cut = ItemFactory(item_to_cut, player) + item_to_cut = item_factory(item_to_cut, world) if items.count(item_to_cut) > minimum_items: items.remove(item_to_cut) - removed_filler.append(ItemFactory(item_to_cut.name, player)) + removed_filler.append(item_factory(item_to_cut.name, world)) return True return False @@ -551,7 +551,7 @@ def set_up_take_anys(world, player): if swords: sword = world.random.choice(swords) world.itempool.remove(sword) - world.itempool.append(ItemFactory('Rupees (20)', player)) + world.itempool.append(item_factory('Rupees (20)', world)) old_man_take_any.shop.add_inventory(0, sword.name, 0, 0) loc_name = "Old Man Sword Cave" location = ALttPLocation(player, loc_name, shop_table_by_location[loc_name], parent=old_man_take_any) @@ -577,7 +577,7 @@ def set_up_take_anys(world, player): location = ALttPLocation(player, take_any.name, shop_table_by_location[take_any.name], parent=take_any) location.shop_slot = 1 take_any.locations.append(location) - location.place_locked_item(ItemFactory("Boss Heart Container", player)) + location.place_locked_item(item_factory("Boss Heart Container", world)) def get_pool_core(world, player: int): diff --git a/worlds/alttp/Items.py b/worlds/alttp/Items.py index 8e513552..cb44f35d 100644 --- a/worlds/alttp/Items.py +++ b/worlds/alttp/Items.py @@ -1,6 +1,7 @@ import typing from BaseClasses import ItemClassification as IC +from worlds.AutoWorld import World def GetBeemizerItem(world, player: int, item): @@ -17,13 +18,10 @@ def GetBeemizerItem(world, player: int, item): if not world.beemizer_trap_chance[player] or world.random.random() > (world.beemizer_trap_chance[player] / 100): return "Bee" if isinstance(item, str) else world.create_item("Bee", player) else: - return "Bee Trap" if isinstance(item, str) else world.create_item("Bee Trap", player) + return "Bee Trap" if isinstance(item, str) else world.create_item("Bee Trap", player) -# should be replaced with direct world.create_item(item) call in the future -def ItemFactory(items: typing.Union[str, typing.Iterable[str]], player: int): - from worlds.alttp import ALTTPWorld - world = ALTTPWorld(None, player) +def item_factory(items: typing.Union[str, typing.Iterable[str]], world: World): ret = [] singleton = False if isinstance(items, str): diff --git a/worlds/alttp/Rom.py b/worlds/alttp/Rom.py index ff4947bb..6ef1f0db 100644 --- a/worlds/alttp/Rom.py +++ b/worlds/alttp/Rom.py @@ -34,7 +34,7 @@ from .Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmith DeathMountain_texts, \ LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts, Lumberjacks_texts, \ SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names -from .Items import ItemFactory, item_table, item_name_groups, progression_items +from .Items import item_table, item_name_groups, progression_items from .EntranceShuffle import door_addresses from .Options import small_key_shuffle @@ -996,7 +996,7 @@ def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool): rom.write_byte(0x18003A, 0x01 if world.dark_world_light_cone else 0x00) GREEN_TWENTY_RUPEES = 0x47 - GREEN_CLOCK = ItemFactory('Green Clock', player).code + GREEN_CLOCK = item_table["Green Clock"].item_code rom.write_byte(0x18004F, 0x01) # Byrna Invulnerability: on @@ -1777,13 +1777,13 @@ def write_custom_shops(rom, world, player): if item['player'] and world.game[item['player']] != "A Link to the Past": # item not native to ALTTP item_code = get_nonnative_item_sprite(world.worlds[item['player']].item_name_to_id[item['item']]) else: - item_code = ItemFactory(item['item'], player).code + item_code = item_table[item["item"]].item_code if item['item'] == 'Single Arrow' and item['player'] == 0 and world.retro_bow[player]: rom.write_byte(0x186500 + shop.sram_offset + slot, arrow_mask) item_data = [shop_id, item_code] + price_data + \ - [item['max'], ItemFactory(item['replacement'], player).code if item['replacement'] else 0xFF] + \ - replacement_price_data + [0 if item['player'] == player else min(ROM_PLAYER_LIMIT, item['player'])] + [item["max"], item_table[item["replacement"]].item_code if item["replacement"] else 0xFF] + \ + replacement_price_data + [0 if item["player"] == player else min(ROM_PLAYER_LIMIT, item["player"])] items_data.extend(item_data) rom.write_bytes(0x184800, shop_data) diff --git a/worlds/alttp/Rules.py b/worlds/alttp/Rules.py index b86a793f..c3156116 100644 --- a/worlds/alttp/Rules.py +++ b/worlds/alttp/Rules.py @@ -8,7 +8,7 @@ from worlds.generic.Rules import (add_item_rule, add_rule, forbid_item, from . import OverworldGlitchRules from .Bosses import GanonDefeatRule -from .Items import ItemFactory, item_name_groups, item_table, progression_items +from .Items import item_factory, item_name_groups, item_table, progression_items from .Options import small_key_shuffle from .OverworldGlitchRules import no_logic_rules, overworld_glitches_rules from .Regions import LTTPRegionType, location_table @@ -1181,7 +1181,7 @@ def set_trock_key_rules(world, player): forbid_item(world.get_location(location, player), 'Big Key (Turtle Rock)', player) else: # A key is required in the Big Key Chest to prevent a possible softlock. Place an extra key to ensure 100% locations still works - item = ItemFactory('Small Key (Turtle Rock)', player) + item = item_factory('Small Key (Turtle Rock)', world.worlds[player]) location = world.get_location('Turtle Rock - Big Key Chest', player) location.place_locked_item(item) location.event = True diff --git a/worlds/alttp/test/__init__.py b/worlds/alttp/test/__init__.py index 5baaa7e8..49033a6c 100644 --- a/worlds/alttp/test/__init__.py +++ b/worlds/alttp/test/__init__.py @@ -14,3 +14,4 @@ class LTTPTestBase(unittest.TestCase): for name, option in AutoWorldRegister.world_types["A Link to the Past"].options_dataclass.type_hints.items(): setattr(args, name, {1: option.from_any(getattr(option, "default"))}) self.multiworld.set_options(args) + self.world = self.multiworld.worlds[1] diff --git a/worlds/alttp/test/dungeons/TestDungeon.py b/worlds/alttp/test/dungeons/TestDungeon.py index 1f8288ac..128f8b41 100644 --- a/worlds/alttp/test/dungeons/TestDungeon.py +++ b/worlds/alttp/test/dungeons/TestDungeon.py @@ -2,7 +2,7 @@ from BaseClasses import CollectionState, ItemClassification from worlds.alttp.Dungeons import get_dungeon_item_pool from worlds.alttp.EntranceShuffle import mandatory_connections, connect_simple from worlds.alttp.ItemPool import difficulties -from worlds.alttp.Items import ItemFactory +from worlds.alttp.Items import item_factory from worlds.alttp.Regions import create_regions from worlds.alttp.Shops import create_shops from worlds.alttp.test import LTTPTestBase @@ -24,10 +24,10 @@ class TestDungeon(LTTPTestBase): connect_simple(self.multiworld, 'Big Bomb Shop', 'Big Bomb Shop', 1) self.multiworld.get_region('Menu', 1).exits = [] self.multiworld.swamp_patch_required[1] = True - self.multiworld.worlds[1].set_rules() - self.multiworld.worlds[1].create_items() + self.world.set_rules() + self.world.create_items() self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld)) - self.multiworld.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) + self.multiworld.itempool.extend(item_factory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], self.world)) def run_tests(self, access_pool): for exit in self.remove_exits: @@ -40,9 +40,9 @@ class TestDungeon(LTTPTestBase): if all_except and len(all_except) > 0: items = self.multiworld.itempool[:] items = [item for item in items if item.name not in all_except and not ("Bottle" in item.name and "AnyBottle" in all_except)] - items.extend(ItemFactory(item_pool[0], 1)) + items.extend(item_factory(item_pool[0], self.world)) else: - items = ItemFactory(items, 1) + items = item_factory(items, self.world) state = CollectionState(self.multiworld) state.reachable_regions[1].add(self.multiworld.get_region('Menu', 1)) for region_name in self.starting_regions: diff --git a/worlds/alttp/test/inverted/TestInverted.py b/worlds/alttp/test/inverted/TestInverted.py index f2c585e4..069639e8 100644 --- a/worlds/alttp/test/inverted/TestInverted.py +++ b/worlds/alttp/test/inverted/TestInverted.py @@ -2,7 +2,7 @@ from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool from worlds.alttp.EntranceShuffle import link_inverted_entrances from worlds.alttp.InvertedRegions import create_inverted_regions from worlds.alttp.ItemPool import difficulties -from worlds.alttp.Items import ItemFactory +from worlds.alttp.Items import item_factory from worlds.alttp.Regions import mark_light_world_regions from worlds.alttp.Shops import create_shops from test.TestBase import TestBase @@ -18,14 +18,14 @@ class TestInverted(TestBase, LTTPTestBase): self.multiworld.bombless_start[1].value = True self.multiworld.shuffle_capacity_upgrades[1].value = True create_inverted_regions(self.multiworld, 1) - self.multiworld.worlds[1].create_dungeons() + self.world.create_dungeons() create_shops(self.multiworld, 1) link_inverted_entrances(self.multiworld, 1) - self.multiworld.worlds[1].create_items() + self.world.create_items() self.multiworld.required_medallions[1] = ['Ether', 'Quake'] self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld)) - self.multiworld.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) + self.multiworld.itempool.extend(item_factory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], self.world)) self.multiworld.get_location('Agahnim 1', 1).item = None self.multiworld.get_location('Agahnim 2', 1).item = None mark_light_world_regions(self.multiworld, 1) - self.multiworld.worlds[1].set_rules() + self.world.set_rules() diff --git a/worlds/alttp/test/inverted_minor_glitches/TestInvertedMinor.py b/worlds/alttp/test/inverted_minor_glitches/TestInvertedMinor.py index 0219332e..21dbae69 100644 --- a/worlds/alttp/test/inverted_minor_glitches/TestInvertedMinor.py +++ b/worlds/alttp/test/inverted_minor_glitches/TestInvertedMinor.py @@ -2,7 +2,7 @@ from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool from worlds.alttp.EntranceShuffle import link_inverted_entrances from worlds.alttp.InvertedRegions import create_inverted_regions from worlds.alttp.ItemPool import difficulties -from worlds.alttp.Items import ItemFactory +from worlds.alttp.Items import item_factory from worlds.alttp.Regions import mark_light_world_regions from worlds.alttp.Shops import create_shops from test.TestBase import TestBase @@ -19,14 +19,14 @@ class TestInvertedMinor(TestBase, LTTPTestBase): self.multiworld.shuffle_capacity_upgrades[1].value = True self.multiworld.difficulty_requirements[1] = difficulties['normal'] create_inverted_regions(self.multiworld, 1) - self.multiworld.worlds[1].create_dungeons() + self.world.create_dungeons() create_shops(self.multiworld, 1) link_inverted_entrances(self.multiworld, 1) - self.multiworld.worlds[1].create_items() + self.world.create_items() self.multiworld.required_medallions[1] = ['Ether', 'Quake'] self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld)) - self.multiworld.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) + self.multiworld.itempool.extend(item_factory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], self.world)) self.multiworld.get_location('Agahnim 1', 1).item = None self.multiworld.get_location('Agahnim 2', 1).item = None mark_light_world_regions(self.multiworld, 1) - self.multiworld.worlds[1].set_rules() + self.world.set_rules() diff --git a/worlds/alttp/test/inverted_owg/TestInvertedOWG.py b/worlds/alttp/test/inverted_owg/TestInvertedOWG.py index 849f0609..138324a9 100644 --- a/worlds/alttp/test/inverted_owg/TestInvertedOWG.py +++ b/worlds/alttp/test/inverted_owg/TestInvertedOWG.py @@ -2,7 +2,7 @@ from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool from worlds.alttp.EntranceShuffle import link_inverted_entrances from worlds.alttp.InvertedRegions import create_inverted_regions from worlds.alttp.ItemPool import difficulties -from worlds.alttp.Items import ItemFactory +from worlds.alttp.Items import item_factory from worlds.alttp.Regions import mark_light_world_regions from worlds.alttp.Shops import create_shops from test.TestBase import TestBase @@ -19,16 +19,16 @@ class TestInvertedOWG(TestBase, LTTPTestBase): self.multiworld.shuffle_capacity_upgrades[1].value = True self.multiworld.difficulty_requirements[1] = difficulties['normal'] create_inverted_regions(self.multiworld, 1) - self.multiworld.worlds[1].create_dungeons() + self.world.create_dungeons() create_shops(self.multiworld, 1) link_inverted_entrances(self.multiworld, 1) - self.multiworld.worlds[1].create_items() + self.world.create_items() self.multiworld.required_medallions[1] = ['Ether', 'Quake'] self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld)) - self.multiworld.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) + self.multiworld.itempool.extend(item_factory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], self.world)) self.multiworld.get_location('Agahnim 1', 1).item = None self.multiworld.get_location('Agahnim 2', 1).item = None self.multiworld.precollected_items[1].clear() - self.multiworld.itempool.append(ItemFactory('Pegasus Boots', 1)) + self.multiworld.itempool.append(item_factory('Pegasus Boots', self.world)) mark_light_world_regions(self.multiworld, 1) - self.multiworld.worlds[1].set_rules() + self.world.set_rules() diff --git a/worlds/alttp/test/minor_glitches/TestMinor.py b/worlds/alttp/test/minor_glitches/TestMinor.py index c7de74d3..547509d5 100644 --- a/worlds/alttp/test/minor_glitches/TestMinor.py +++ b/worlds/alttp/test/minor_glitches/TestMinor.py @@ -1,7 +1,7 @@ from worlds.alttp.Dungeons import get_dungeon_item_pool from worlds.alttp.InvertedRegions import mark_dark_world_regions from worlds.alttp.ItemPool import difficulties -from worlds.alttp.Items import ItemFactory +from worlds.alttp.Items import item_factory from test.TestBase import TestBase from worlds.alttp.test import LTTPTestBase @@ -14,15 +14,15 @@ class TestMinor(TestBase, LTTPTestBase): self.multiworld.bombless_start[1].value = True self.multiworld.shuffle_capacity_upgrades[1].value = True self.multiworld.difficulty_requirements[1] = difficulties['normal'] - self.multiworld.worlds[1].er_seed = 0 - self.multiworld.worlds[1].create_regions() - self.multiworld.worlds[1].create_items() + self.world.er_seed = 0 + self.world.create_regions() + self.world.create_items() self.multiworld.required_medallions[1] = ['Ether', 'Quake'] self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld)) - self.multiworld.itempool.extend(ItemFactory( + self.multiworld.itempool.extend(item_factory( ['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', - 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) + 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], self.world)) self.multiworld.get_location('Agahnim 1', 1).item = None self.multiworld.get_location('Agahnim 2', 1).item = None mark_dark_world_regions(self.multiworld, 1) - self.multiworld.worlds[1].set_rules() + self.world.set_rules() diff --git a/worlds/alttp/test/options/TestOpenPyramid.py b/worlds/alttp/test/options/TestOpenPyramid.py index 895ecb95..c7912c43 100644 --- a/worlds/alttp/test/options/TestOpenPyramid.py +++ b/worlds/alttp/test/options/TestOpenPyramid.py @@ -1,5 +1,5 @@ -from test.TestBase import WorldTestBase -from ...Items import ItemFactory +from test.bases import WorldTestBase +from ...Items import item_factory class PyramidTestBase(WorldTestBase): @@ -32,6 +32,6 @@ class GoalPyramidTest(PyramidTestBase): self.assertFalse(self.can_reach_entrance("Pyramid Hole")) self.collect_by_name(["Hammer", "Progressive Glove", "Moon Pearl"]) self.assertFalse(self.can_reach_entrance("Pyramid Hole")) - self.multiworld.state.collect(ItemFactory("Beat Agahnim 2", 1)) + self.collect(item_factory("Beat Agahnim 2", self.multiworld.worlds[1])) self.assertTrue(self.can_reach_entrance("Pyramid Hole")) diff --git a/worlds/alttp/test/owg/TestVanillaOWG.py b/worlds/alttp/test/owg/TestVanillaOWG.py index 1f8f2707..aafc04a7 100644 --- a/worlds/alttp/test/owg/TestVanillaOWG.py +++ b/worlds/alttp/test/owg/TestVanillaOWG.py @@ -1,7 +1,7 @@ from worlds.alttp.Dungeons import get_dungeon_item_pool from worlds.alttp.InvertedRegions import mark_dark_world_regions from worlds.alttp.ItemPool import difficulties -from worlds.alttp.Items import ItemFactory +from worlds.alttp.Items import item_factory from test.TestBase import TestBase from worlds.alttp.test import LTTPTestBase @@ -19,10 +19,10 @@ class TestVanillaOWG(TestBase, LTTPTestBase): self.multiworld.worlds[1].create_items() self.multiworld.required_medallions[1] = ['Ether', 'Quake'] self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld)) - self.multiworld.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) + self.multiworld.itempool.extend(item_factory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], self.world)) self.multiworld.get_location('Agahnim 1', 1).item = None self.multiworld.get_location('Agahnim 2', 1).item = None self.multiworld.precollected_items[1].clear() - self.multiworld.itempool.append(ItemFactory('Pegasus Boots', 1)) + self.multiworld.itempool.append(item_factory('Pegasus Boots', self.world)) mark_dark_world_regions(self.multiworld, 1) - self.multiworld.worlds[1].set_rules() \ No newline at end of file + self.world.set_rules() diff --git a/worlds/alttp/test/vanilla/TestVanilla.py b/worlds/alttp/test/vanilla/TestVanilla.py index 3f4fbad8..e79b4f2e 100644 --- a/worlds/alttp/test/vanilla/TestVanilla.py +++ b/worlds/alttp/test/vanilla/TestVanilla.py @@ -1,7 +1,7 @@ from worlds.alttp.Dungeons import get_dungeon_item_pool from worlds.alttp.InvertedRegions import mark_dark_world_regions from worlds.alttp.ItemPool import difficulties -from worlds.alttp.Items import ItemFactory +from worlds.alttp.Items import item_factory from test.TestBase import TestBase from worlds.alttp.test import LTTPTestBase @@ -18,8 +18,8 @@ class TestVanilla(TestBase, LTTPTestBase): self.multiworld.worlds[1].create_items() self.multiworld.required_medallions[1] = ['Ether', 'Quake'] self.multiworld.itempool.extend(get_dungeon_item_pool(self.multiworld)) - self.multiworld.itempool.extend(ItemFactory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], 1)) + self.multiworld.itempool.extend(item_factory(['Green Pendant', 'Red Pendant', 'Blue Pendant', 'Beat Agahnim 1', 'Beat Agahnim 2', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'], self.world)) self.multiworld.get_location('Agahnim 1', 1).item = None self.multiworld.get_location('Agahnim 2', 1).item = None mark_dark_world_regions(self.multiworld, 1) - self.multiworld.worlds[1].set_rules() \ No newline at end of file + self.world.set_rules()