diff --git a/BaseClasses.py b/BaseClasses.py index 057c8272..f499a4ab 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -15,9 +15,6 @@ from worlds.alttp.Items import item_name_groups from worlds.generic import PlandoItem, PlandoConnection -class World(): - pass - class MultiWorld(): debug_types = False player_names: Dict[int, List[str]] diff --git a/test/TestBase.py b/test/TestBase.py index 24fc7d89..140b5de7 100644 --- a/test/TestBase.py +++ b/test/TestBase.py @@ -1,11 +1,12 @@ import unittest -from BaseClasses import CollectionState, World +from BaseClasses import CollectionState +from BaseClasses import MultiWorld from worlds.alttp.Items import ItemFactory class TestBase(unittest.TestCase): - world: World + world: MultiWorld _state_cache = {} def get_state(self, items): diff --git a/test/inverted_minor_glitches/TestInvertedMinor.py b/test/inverted_minor_glitches/TestInvertedMinor.py index 30f27dce..9f24d7be 100644 --- a/test/inverted_minor_glitches/TestInvertedMinor.py +++ b/test/inverted_minor_glitches/TestInvertedMinor.py @@ -1,18 +1,19 @@ -from BaseClasses import World -from Dungeons import create_dungeons, get_dungeon_item_pool -from EntranceShuffle import link_inverted_entrances -from InvertedRegions import create_inverted_regions -from ItemPool import generate_itempool, difficulties -from Items import ItemFactory -from Regions import mark_light_world_regions, create_shops -from Rules import set_rules +from BaseClasses import MultiWorld +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 generate_itempool, difficulties +from worlds.alttp.Items import ItemFactory +from worlds.alttp.Regions import mark_light_world_regions, create_shops +from worlds.alttp.Rules import set_rules from test.TestBase import TestBase class TestInvertedMinor(TestBase): def setUp(self): - self.world = World(1, {1:'vanilla'}, {1:'minorglitches'}, {1:'inverted'}, {1:'random'}, {1:'normal'}, {1:'normal'}, {1:False}, {1:'on'}, {1:'ganon'}, 'balanced', {1:'items'}, - True, {1:False}, False, None, {1:False}) + self.world = MultiWorld(1, {1: 'vanilla'}, {1: 'minorglitches'}, {1: 'inverted'}, {1: 'random'}, {1: 'normal'}, + {1: 'normal'}, {1: False}, {1: 'on'}, {1: 'ganon'}, 'balanced', {1: 'items'}, + True, {1: False}, False, None, {1: False}) self.world.difficulty_requirements[1] = difficulties['normal'] create_inverted_regions(self.world, 1) create_dungeons(self.world, 1) diff --git a/test/minor_glitches/TestMinor.py b/test/minor_glitches/TestMinor.py index 23dd387c..99cb8332 100644 --- a/test/minor_glitches/TestMinor.py +++ b/test/minor_glitches/TestMinor.py @@ -1,18 +1,19 @@ -from BaseClasses import World -from Dungeons import create_dungeons, get_dungeon_item_pool -from EntranceShuffle import link_entrances -from InvertedRegions import mark_dark_world_regions -from ItemPool import difficulties, generate_itempool -from Items import ItemFactory -from Regions import create_regions, create_shops -from Rules import set_rules +from BaseClasses import MultiWorld +from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool +from worlds.alttp.EntranceShuffle import link_entrances +from worlds.alttp.InvertedRegions import mark_dark_world_regions +from worlds.alttp.ItemPool import difficulties, generate_itempool +from worlds.alttp.Items import ItemFactory +from worlds.alttp.Regions import create_regions, create_shops +from worlds.alttp.Rules import set_rules from test.TestBase import TestBase class TestMinor(TestBase): def setUp(self): - self.world = World(1, {1:'vanilla'}, {1:'minorglitches'}, {1:'open'}, {1:'random'}, {1:'normal'}, {1:'normal'}, {1:False}, {1:'on'}, {1:'ganon'}, 'balanced', {1:'items'}, - True, {1:False}, False, None, {1:False}) + self.world = MultiWorld(1, {1: 'vanilla'}, {1: 'minorglitches'}, {1: 'open'}, {1: 'random'}, {1: 'normal'}, + {1: 'normal'}, {1: False}, {1: 'on'}, {1: 'ganon'}, 'balanced', {1: 'items'}, + True, {1: False}, False, None, {1: False}) self.world.difficulty_requirements[1] = difficulties['normal'] create_regions(self.world, 1) create_dungeons(self.world, 1) diff --git a/worlds/alttp/Rules.py b/worlds/alttp/Rules.py index a8e4b1f7..6c287955 100644 --- a/worlds/alttp/Rules.py +++ b/worlds/alttp/Rules.py @@ -26,6 +26,7 @@ def set_rules(world, player): world.progression_balancing[player] = False global_rules(world, player) + dungeon_boss_rules(world, player) if world.mode[player] != 'inverted': default_rules(world, player) @@ -189,6 +190,29 @@ non_crossover_items = (item_name_groups["Small Keys"] | item_name_groups["Big Ke "Small Key (Universal)"} +def dungeon_boss_rules(world, player): + boss_locations = { + 'Agahnim 1', + 'Tower of Hera - Boss', + 'Tower of Hera - Prize', + 'Swamp Palace - Boss', + 'Swamp Palace - Prize', + 'Thieves\' Town - Boss', + 'Thieves\' Town - Prize', + 'Skull Woods - Boss', + 'Skull Woods - Prize', + 'Ice Palace - Boss', + 'Ice Palace - Prize', + 'Misery Mire - Boss', + 'Misery Mire - Prize', + 'Turtle Rock - Boss', + 'Turtle Rock - Prize', + 'Palace of Darkness - Boss', + 'Palace of Darkness - Prize', + } + for location in boss_locations: + set_defeat_dungeon_boss_rule(world.get_location(location, player)) + def global_rules(world, player): # ganon can only carry triforce add_item_rule(world.get_location('Ganon', player), lambda item: item.name == 'Triforce' and item.player == player) @@ -237,7 +261,7 @@ def global_rules(world, player): lambda state: state.has_key('Small Key (Hyrule Castle)', player)) set_rule(world.get_entrance('Agahnim 1', player), lambda state: state.has_sword(player) and state.has_key('Small Key (Agahnims Tower)', player, 2)) - set_defeat_dungeon_boss_rule(world.get_location('Agahnim 1', player)) + set_rule(world.get_location('Castle Tower - Room 03', player), lambda state: state.can_kill_most_things(player, 8)) set_rule(world.get_location('Castle Tower - Dark Maze', player), lambda state: state.can_kill_most_things(player, 8) and state.has_key('Small Key (Agahnims Tower)', @@ -271,8 +295,6 @@ def global_rules(world, player): set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: state.has_fire_source(player)) if world.accessibility[player] != 'locations': set_always_allow(world.get_location('Tower of Hera - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Tower of Hera)' and item.player == player) - set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Boss', player)) - set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Prize', player)) set_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Flippers', player) and state.has('Open Floodgate', player)) set_rule(world.get_entrance('Swamp Palace Small Key Door', player), lambda state: state.has_key('Small Key (Swamp Palace)', player)) @@ -281,15 +303,11 @@ def global_rules(world, player): if world.accessibility[player] != 'locations': set_always_allow(world.get_location('Swamp Palace - Big Chest', player), lambda state, item: item.name == 'Big Key (Swamp Palace)' and item.player == player) set_rule(world.get_entrance('Swamp Palace (North)', player), lambda state: state.has('Hookshot', player)) - set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Boss', player)) - set_defeat_dungeon_boss_rule(world.get_location('Swamp Palace - Prize', player)) if not world.keyshuffle[player] and world.logic[player] != 'nologic': forbid_item(world.get_location('Swamp Palace - Entrance', player), 'Big Key (Swamp Palace)', player) set_rule(world.get_entrance('Thieves Town Big Key Door', player), lambda state: state.has('Big Key (Thieves Town)', player)) set_rule(world.get_entrance('Blind Fight', player), lambda state: state.has_key('Small Key (Thieves Town)', player)) - set_defeat_dungeon_boss_rule(world.get_location('Thieves\' Town - Boss', player)) - set_defeat_dungeon_boss_rule(world.get_location('Thieves\' Town - Prize', player)) set_rule(world.get_location('Thieves\' Town - Big Chest', player), lambda state: (state.has_key('Small Key (Thieves Town)', player) or item_name(state, 'Thieves\' Town - Big Chest', player) == ('Small Key (Thieves Town)', player)) and state.has('Hammer', player)) if world.accessibility[player] != 'locations': set_always_allow(world.get_location('Thieves\' Town - Big Chest', player), lambda state, item: item.name == 'Small Key (Thieves Town)' and item.player == player and state.has('Hammer', player)) @@ -303,8 +321,6 @@ def global_rules(world, player): if world.accessibility[player] != 'locations': set_always_allow(world.get_location('Skull Woods - Big Chest', player), lambda state, item: item.name == 'Big Key (Skull Woods)' and item.player == player) set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state.has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player) and state.has_sword(player)) # sword required for curtain - set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Boss', player)) - set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Prize', player)) set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.can_melt_things(player)) set_rule(world.get_location('Ice Palace - Big Chest', player), lambda state: state.has('Big Key (Ice Palace)', player)) @@ -312,8 +328,6 @@ def global_rules(world, player): # TODO: investigate change from VT. Changed to hookshot or 2 keys (no checking for big key in specific chests) set_rule(world.get_entrance('Ice Palace (East)', player), lambda state: (state.has('Hookshot', player) or (item_in_locations(state, 'Big Key (Ice Palace)', player, [('Ice Palace - Spike Room', player), ('Ice Palace - Big Key Chest', player), ('Ice Palace - Map Chest', player)]) and state.has_key('Small Key (Ice Palace)', player))) and (state.world.can_take_damage[player] or state.has('Hookshot', player) or state.has('Cape', player) or state.has('Cane of Byrna', player))) set_rule(world.get_entrance('Ice Palace (East Top)', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) - set_defeat_dungeon_boss_rule(world.get_location('Ice Palace - Boss', player)) - set_defeat_dungeon_boss_rule(world.get_location('Ice Palace - Prize', player)) set_rule(world.get_entrance('Misery Mire Entrance Gap', player), lambda state: (state.has_Boots(player) or state.has('Hookshot', player)) and (state.has_sword(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Hammer', player) or state.has('Cane of Somaria', player) or state.can_shoot_arrows(player))) # need to defeat wizzrobes, bombs don't work ... set_rule(world.get_location('Misery Mire - Big Chest', player), lambda state: state.has('Big Key (Misery Mire)', player)) @@ -329,8 +343,6 @@ def global_rules(world, player): set_rule(world.get_location('Misery Mire - Compass Chest', player), lambda state: state.has_fire_source(player)) set_rule(world.get_location('Misery Mire - Big Key Chest', player), lambda state: state.has_fire_source(player)) set_rule(world.get_entrance('Misery Mire (Vitreous)', player), lambda state: state.has('Cane of Somaria', player)) - set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Boss', player)) - set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Prize', player)) set_rule(world.get_entrance('Turtle Rock Entrance Gap', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('Turtle Rock Entrance Gap Reverse', player), lambda state: state.has('Cane of Somaria', player)) @@ -347,8 +359,6 @@ def global_rules(world, player): set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) set_rule(world.get_entrance('Turtle Rock (Trinexx)', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 4) and state.has('Big Key (Turtle Rock)', player) and state.has('Cane of Somaria', player)) - set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Boss', player)) - set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Prize', player)) if not world.enemy_shuffle[player]: set_rule(world.get_entrance('Palace of Darkness Bonk Wall', player), lambda state: state.can_shoot_arrows(player)) @@ -367,8 +377,6 @@ def global_rules(world, player): set_always_allow(world.get_location('Palace of Darkness - Harmless Hellway', player), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and item.player == player and state.has_key('Small Key (Palace of Darkness)', player, 5)) set_rule(world.get_entrance('Palace of Darkness Maze Door', player), lambda state: state.has_key('Small Key (Palace of Darkness)', player, 6)) - set_defeat_dungeon_boss_rule(world.get_location('Palace of Darkness - Boss', player)) - set_defeat_dungeon_boss_rule(world.get_location('Palace of Darkness - Prize', player)) # these key rules are conservative, you might be able to get away with more lenient rules randomizer_room_chests = ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right', 'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right'] diff --git a/worlds/alttp/__init__.py b/worlds/alttp/__init__.py index 573a44b2..f3823e97 100644 --- a/worlds/alttp/__init__.py +++ b/worlds/alttp/__init__.py @@ -1,7 +1,8 @@ -from BaseClasses import World +from worlds.generic import World class ALTTPWorld(World): + """WIP""" def __init__(self, options, slot: int): self._region_cache = {} self.slot = slot diff --git a/worlds/generic/__init__.py b/worlds/generic/__init__.py index 52c51b2a..ec6a229f 100644 --- a/worlds/generic/__init__.py +++ b/worlds/generic/__init__.py @@ -12,3 +12,7 @@ class PlandoConnection(NamedTuple): entrance: str exit: str direction: str # entrance, exit or both + + +class World(): + pass