diff --git a/worlds/alttp/Bosses.py b/worlds/alttp/Bosses.py index 965a86db..02970edb 100644 --- a/worlds/alttp/Bosses.py +++ b/worlds/alttp/Bosses.py @@ -119,7 +119,9 @@ def KholdstareDefeatRule(state, player: int) -> bool: def VitreousDefeatRule(state, player: int) -> bool: - return can_shoot_arrows(state, player) or has_melee_weapon(state, player) + return ((can_shoot_arrows(state, player) and can_use_bombs(state, player, 10)) + or can_shoot_arrows(state, player, 35) or state.has("Silver Bow", player) + or has_melee_weapon(state, player)) def TrinexxDefeatRule(state, player: int) -> bool: diff --git a/worlds/alttp/ItemPool.py b/worlds/alttp/ItemPool.py index c6c17704..77d02f97 100644 --- a/worlds/alttp/ItemPool.py +++ b/worlds/alttp/ItemPool.py @@ -484,8 +484,7 @@ def generate_itempool(world): if multiworld.randomize_cost_types[player]: # Heart and Arrow costs require all Heart Container/Pieces and Arrow Upgrades to be advancement items for logic for item in items: - if (item.name in ("Boss Heart Container", "Sanctuary Heart Container", "Piece of Heart") - or "Arrow Upgrade" in item.name): + if item.name in ("Boss Heart Container", "Sanctuary Heart Container", "Piece of Heart"): item.classification = ItemClassification.progression else: # Otherwise, logic has some branches where having 4 hearts is one possible requirement (of several alternatives) @@ -713,7 +712,7 @@ def get_pool_core(world, player: int): pool.remove("Rupees (20)") if retro_bow: - replace = {'Single Arrow', 'Arrows (10)', 'Arrow Upgrade (+5)', 'Arrow Upgrade (+10)', 'Arrow Upgrade (50)'} + replace = {'Single Arrow', 'Arrows (10)', 'Arrow Upgrade (+5)', 'Arrow Upgrade (+10)', 'Arrow Upgrade (70)'} pool = ['Rupees (5)' if item in replace else item for item in pool] if world.small_key_shuffle[player] == small_key_shuffle.option_universal: pool.extend(diff.universal_keys) diff --git a/worlds/alttp/Items.py b/worlds/alttp/Items.py index cb44f35d..7e36d7a3 100644 --- a/worlds/alttp/Items.py +++ b/worlds/alttp/Items.py @@ -110,9 +110,9 @@ item_table = {'Bow': ItemData(IC.progression, None, 0x0B, 'You have\nchosen the\ 'Crystal 7': ItemData(IC.progression, 'Crystal', (0x08, 0x34, 0x64, 0x40, 0x7C, 0x06), None, None, None, None, None, None, "a blue crystal"), 'Single Arrow': ItemData(IC.filler, None, 0x43, 'a lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'), 'Arrows (10)': ItemData(IC.filler, None, 0x44, 'This will give\nyou ten shots\nwith your bow!', 'and the arrow pack','stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again','ten arrows'), - 'Arrow Upgrade (+10)': ItemData(IC.useful, None, 0x54, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), - 'Arrow Upgrade (+5)': ItemData(IC.useful, None, 0x53, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), - 'Arrow Upgrade (70)': ItemData(IC.useful, None, 0x4D, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), + 'Arrow Upgrade (+10)': ItemData(IC.progression_skip_balancing, None, 0x54, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), + 'Arrow Upgrade (+5)': ItemData(IC.progression_skip_balancing, None, 0x53, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), + 'Arrow Upgrade (70)': ItemData(IC.progression_skip_balancing, None, 0x4D, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), 'Single Bomb': ItemData(IC.filler, None, 0x27, 'I make things\ngo BOOM! But\njust once.', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again', 'a bomb'), 'Bombs (3)': ItemData(IC.filler, None, 0x28, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'), 'Bombs (10)': ItemData(IC.filler, None, 0x31, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'), diff --git a/worlds/alttp/Shops.py b/worlds/alttp/Shops.py index db2b5b68..055eb2da 100644 --- a/worlds/alttp/Shops.py +++ b/worlds/alttp/Shops.py @@ -170,7 +170,8 @@ def push_shop_inventories(multiworld): # Retro Bow arrows will already have been pushed if (not multiworld.retro_bow[location.player]) or ((item_name, location.item.player) != ("Single Arrow", location.player)): - location.shop.push_inventory(location.shop_slot, item_name, location.shop_price, + location.shop.push_inventory(location.shop_slot, item_name, + round(location.shop_price * get_price_modifier(location.item)), 1, location.item.player if location.item.player != location.player else 0, location.shop_price_type) location.shop_price = location.shop.inventory[location.shop_slot]["price"] = min(location.shop_price, diff --git a/worlds/alttp/StateHelpers.py b/worlds/alttp/StateHelpers.py index 964a77fe..8661632b 100644 --- a/worlds/alttp/StateHelpers.py +++ b/worlds/alttp/StateHelpers.py @@ -15,18 +15,18 @@ def can_bomb_clip(state: CollectionState, region: LTTPRegion, player: int) -> bo def can_buy_unlimited(state: CollectionState, item: str, player: int) -> bool: return any(shop.region.player == player and shop.has_unlimited(item) and shop.region.can_reach(state) for - shop in state.multiworld.shops) + shop in state.multiworld.shops) def can_buy(state: CollectionState, item: str, player: int) -> bool: return any(shop.region.player == player and shop.has(item) and shop.region.can_reach(state) for - shop in state.multiworld.shops) + shop in state.multiworld.shops) -def can_shoot_arrows(state: CollectionState, player: int) -> bool: +def can_shoot_arrows(state: CollectionState, player: int, count: int = 0) -> bool: if state.multiworld.retro_bow[player]: return (state.has('Bow', player) or state.has('Silver Bow', player)) and can_buy(state, 'Single Arrow', player) - return state.has('Bow', player) or state.has('Silver Bow', player) + return (state.has('Bow', player) or state.has('Silver Bow', player)) and can_hold_arrows(state, player, count) def has_triforce_pieces(state: CollectionState, player: int) -> bool: @@ -61,13 +61,13 @@ def heart_count(state: CollectionState, player: int) -> int: # Warning: This only considers items that are marked as advancement items diff = state.multiworld.worlds[player].difficulty_requirements return min(state.count('Boss Heart Container', player), diff.boss_heart_container_limit) \ - + state.count('Sanctuary Heart Container', player) \ + + state.count('Sanctuary Heart Container', player) \ + min(state.count('Piece of Heart', player), diff.heart_piece_limit) // 4 \ - + 3 # starting hearts + + 3 # starting hearts def can_extend_magic(state: CollectionState, player: int, smallmagic: int = 16, - fullrefill: bool = False): # This reflects the total magic Link has, not the total extra he has. + fullrefill: bool = False): # This reflects the total magic Link has, not the total extra he has. basemagic = 8 if state.has('Magic Upgrade (1/4)', player): basemagic = 32 @@ -84,11 +84,18 @@ def can_extend_magic(state: CollectionState, player: int, smallmagic: int = 16, def can_hold_arrows(state: CollectionState, player: int, quantity: int): - arrows = 30 + ((state.count("Arrow Upgrade (+5)", player) * 5) + (state.count("Arrow Upgrade (+10)", player) * 10) - + (state.count("Bomb Upgrade (50)", player) * 50)) - # Arrow Upgrade (+5) beyond the 6th gives +10 - arrows += max(0, ((state.count("Arrow Upgrade (+5)", player) - 6) * 10)) - return min(70, arrows) >= quantity + if state.multiworld.worlds[player].options.shuffle_capacity_upgrades: + if quantity == 0: + return True + if state.has("Arrow Upgrade (70)", player): + arrows = 70 + else: + arrows = (30 + (state.count("Arrow Upgrade (+5)", player) * 5) + + (state.count("Arrow Upgrade (+10)", player) * 10)) + # Arrow Upgrade (+5) beyond the 6th gives +10 + arrows += max(0, ((state.count("Arrow Upgrade (+5)", player) - 6) * 10)) + return min(70, arrows) >= quantity + return quantity <= 30 or state.has("Capacity Upgrade Shop", player) def can_use_bombs(state: CollectionState, player: int, quantity: int = 1) -> bool: @@ -146,19 +153,19 @@ def can_get_good_bee(state: CollectionState, player: int) -> bool: def can_retrieve_tablet(state: CollectionState, player: int) -> bool: return state.has('Book of Mudora', player) and (has_beam_sword(state, player) or (state.multiworld.swordless[player] and - state.has("Hammer", player))) + state.has("Hammer", player))) def has_sword(state: CollectionState, player: int) -> bool: return state.has('Fighter Sword', player) \ - or state.has('Master Sword', player) \ - or state.has('Tempered Sword', player) \ - or state.has('Golden Sword', player) + or state.has('Master Sword', player) \ + or state.has('Tempered Sword', player) \ + or state.has('Golden Sword', player) def has_beam_sword(state: CollectionState, player: int) -> bool: return state.has('Master Sword', player) or state.has('Tempered Sword', player) or state.has('Golden Sword', - player) + player) def has_melee_weapon(state: CollectionState, player: int) -> bool: @@ -171,9 +178,9 @@ def has_fire_source(state: CollectionState, player: int) -> bool: def can_melt_things(state: CollectionState, player: int) -> bool: return state.has('Fire Rod', player) or \ - (state.has('Bombos', player) and - (state.multiworld.swordless[player] or - has_sword(state, player))) + (state.has('Bombos', player) and + (state.multiworld.swordless[player] or + has_sword(state, player))) def has_misery_mire_medallion(state: CollectionState, player: int) -> bool: diff --git a/worlds/alttp/test/dungeons/TestMiseryMire.py b/worlds/alttp/test/dungeons/TestMiseryMire.py index ca74e936..90b7055b 100644 --- a/worlds/alttp/test/dungeons/TestMiseryMire.py +++ b/worlds/alttp/test/dungeons/TestMiseryMire.py @@ -77,5 +77,5 @@ class TestMiseryMire(TestDungeon): ["Misery Mire - Boss", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']], ["Misery Mire - Boss", True, ['Bomb Upgrade (+5)', 'Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Sword', 'Pegasus Boots']], ["Misery Mire - Boss", True, ['Bomb Upgrade (+5)', 'Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Hammer', 'Pegasus Boots']], - ["Misery Mire - Boss", True, ['Bomb Upgrade (+5)', 'Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Bow', 'Pegasus Boots']], + ["Misery Mire - Boss", True, ['Bomb Upgrade (+5)', 'Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Bow', 'Arrow Upgrade (+5)', 'Pegasus Boots']], ]) \ No newline at end of file