diff --git a/BaseClasses.py b/BaseClasses.py index 4cf3a2b5..d979f3ba 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -92,6 +92,9 @@ class World(object): self.retro[player] = True def set_player_attr(attr, val): self.__dict__.setdefault(attr, {})[player] = val + # Futuro doesn't support standard start + if self.futuro[player] and self.mode[player] == "standard": + self.mode[player] == "open" set_player_attr('_region_cache', {}) set_player_attr('player_names', []) set_player_attr('remote_items', False) @@ -451,6 +454,9 @@ class World(object): return False +# Items to test for ability to use magic in has() +magic_items = ['Magic Powder', 'Fire Rod', 'Ice Rod', 'Bombos', 'Ether', 'Quake', 'Cane of Somaria', 'Cane of Byrna', 'Cape'] + class CollectionState(object): def __init__(self, parent): @@ -597,6 +603,9 @@ class CollectionState(object): return True def has(self, item, player, count=1): + if self.world.futuro[player]: + if item in magic_items and self.prog_items['Magic Upgrade (1/2)', player] == 0 and self.prog_items['Magic Upgrade (1/4)', player] == 0: + return False if count == 1: return (item, player) in self.prog_items return self.prog_items[item, player] >= count @@ -650,11 +659,17 @@ class CollectionState(object): return self.has('Titans Mitts', player) def can_extend_magic(self, player, smallmagic=16, fullrefill=False): #This reflects the total magic Link has, not the total extra he has. - basemagic = 8 if self.has('Magic Upgrade (1/4)', player): basemagic = 32 elif self.has('Magic Upgrade (1/2)', player): basemagic = 16 + else: + basemagic = 8 + if self.world.futuro[player]: + if basemagic == 8: + basemagic = 0 + else: + basemagic = int(basemagic/2) if self.can_buy_unlimited('Green Potion', player) or self.can_buy_unlimited('Blue Potion', player): if self.world.difficulty_adjustments[player] == 'hard' and not fullrefill: basemagic = basemagic + int(basemagic * 0.5 * self.bottle_count(player)) @@ -670,8 +685,24 @@ class CollectionState(object): or (self.has('Cane of Byrna', player) and (enemies < 6 or self.can_extend_magic(player))) or self.can_shoot_arrows(player) or self.has('Fire Rod', player) + or (self.can_use_bombs(player) and enemies < 6) ) + def can_use_bombs(self, player): + return (self.has('Bomb Upgrade (+10)', player) or not self.world.futuro[player]) + + def can_hit_switch(self, player): + return (self.can_use_bombs(player) + or self.can_shoot_arrows(player) + or self.has_blunt_weapon(player) + or self.has('Blue Boomerang', player) + or self.has('Red Boomerang', player) + or self.has('Hookshot', player) + or self.has('Fire Rod', player) + or self.has('Ice Rod', player) + or self.has('Cane of Somaria', player) + or self.has('Cane of Byrna', player)) + def can_shoot_arrows(self, player): if self.world.retro[player]: #todo: Non-progressive silvers grant wooden arrows, but progressive bows do not. Always require shop arrows to be safe diff --git a/ItemList.py b/ItemList.py index 8b2c998c..8c3a2980 100644 --- a/ItemList.py +++ b/ItemList.py @@ -604,6 +604,25 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r pool.extend(['Small Key (Universal)']) else: pool.extend(['Small Key (Universal)']) + + if futuro: + magic_count = 0 + bomb_count = 0 + for item in pool: + if item == 'Magic Upgrade (1/2)': + magic_count += 1 + if item == 'Bomb Upgrade (+10)': + bomb_count += 1 + if magic_count == 0: + pool.append('Magic Upgrade (1/2)') + if 'Magic Upgrade (1/4)' not in pool and magic_count < 2: + pool.append('Magic Upgrade (1/2)') + if bomb_count == 0: + pool.append('Bomb Upgrade (+10)') + pool.append('Bomb Upgrade (+10)') + elif bomb_count == 1: + pool.append('Bomb Upgrade (+10)') + return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, futuro, customitemarray): diff --git a/Mystery.py b/Mystery.py index e0a19467..514f5750 100644 --- a/Mystery.py +++ b/Mystery.py @@ -174,6 +174,8 @@ def roll_settings(weights): ret.mode = 'open' ret.retro = True + ret.futuro = get_choice('futuro', weights) + ret.hints = get_choice('hints') == 'on' ret.swords = {'randomized': 'random', diff --git a/Rom.py b/Rom.py index 7cef05f5..635848a1 100644 --- a/Rom.py +++ b/Rom.py @@ -830,6 +830,21 @@ def patch_rom(world, rom, player, team, enemized): rom.write_byte(0x18004F, 0x01) # Byrna Invulnerability: on + # Default magic consumption costs + magic_cost = { + 'Fire and Ice': [0x3B070, [0x10, 0x08, 0x04]], + 'Medallions': [0x3B073, [0x20, 0x10, 0x08]], + 'Magic Powder': [0x3B076, [0x08, 0x04, 0x02]], + 'Unknown 1': [0x3B079, [0x08, 0x04, 0x02]], + 'Cane of Somaria': [0x3B07C, [0x08, 0x04, 0x02]], + 'Unknown 2': [0x3B07F, [0x10, 0x08, 0x04]], + 'Lamp': [0x3B082, [0x04, 0x02, 0x02]], + 'Unknown 3': [0x3B085, [0x08, 0x04, 0x02]], + 'Cane of Byrna Activation': [0x3B088, [0x10, 0x08, 0x04]], + 'Magic Cape Residual': [0x3ADA7, [0x04, 0x08, 0x10]], + 'Cane of Byrna Residual': [0x45C42, [0x04, 0x02, 0x01]] + } + # handle difficulty_adjustments if world.difficulty_adjustments[player] == 'hard': rom.write_byte(0x180181, 0x01) # Make silver arrows work only on ganon @@ -841,7 +856,7 @@ def patch_rom(world, rom, player, team, enemized): # potion magic restore amount rom.write_byte(0x180085, 0x40) # Half Magic #Cape magic cost - rom.write_bytes(0x3ADA7, [0x02, 0x04, 0x08]) + magic_cost['Magic Cape Residual'][1] = [0x02, 0x04, 0x08] # Byrna Invulnerability: off rom.write_byte(0x18004F, 0x00) #Disable catching fairies @@ -861,7 +876,7 @@ def patch_rom(world, rom, player, team, enemized): # potion magic restore amount rom.write_byte(0x180085, 0x20) # Quarter Magic #Cape magic cost - rom.write_bytes(0x3ADA7, [0x02, 0x04, 0x08]) + magic_cost['Magic Cape Residual'][1] = [0x02, 0x04, 0x08] # Byrna Invulnerability: off rom.write_byte(0x18004F, 0x00) #Disable catching fairies @@ -881,7 +896,7 @@ def patch_rom(world, rom, player, team, enemized): # potion magic restore amount rom.write_byte(0x180085, 0x80) # full #Cape magic cost - rom.write_bytes(0x3ADA7, [0x04, 0x08, 0x10]) + magic_cost['Magic Cape Residual'][1] = [0x04, 0x08, 0x10] # Byrna Invulnerability: on rom.write_byte(0x18004F, 0x01) #Enable catching fairies @@ -896,11 +911,21 @@ def patch_rom(world, rom, player, team, enemized): else: overflow_replacement = GREEN_TWENTY_RUPEES - #Byrna residual magic cost - rom.write_bytes(0x45C42, [0x04, 0x02, 0x01]) - difficulty = world.difficulty_requirements[player] + #Shift magic consumption costs if futuro + if world.futuro[player]: + for item in magic_cost: + magic_cost[item][1][2] = magic_cost[item][1][1] + magic_cost[item][1][1] = magic_cost[item][1][0] + magic_cost[item][1][0] = 0x81 + # Cape consumpion is a cost/X tick thing + magic_cost['Magic Cape Residual'][1][0] = 0x01 + + #Write magic consumption rates to rom + for _,values in magic_cost.items(): + rom.write_bytes(values[0], values[1]) + #Set overflow items for progressive equipment rom.write_bytes(0x180090, [difficulty.progressive_sword_limit if world.swords[player] != 'swordless' else 0, overflow_replacement, @@ -990,7 +1015,7 @@ def patch_rom(world, rom, player, team, enemized): rom.write_bytes(0x184000, [ # original_item, limit, replacement_item, filler 0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees - 0x51, 0x06, 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade + 0x51, 0x00 if world.futuro[player] else 0x06, 0x31 if world.futuro[player] else 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade. If bomb-futuro, turns into Bombs (10) 0x53, 0x06, 0x54, 0xFF, # 6 +5 arrow upgrades -> +10 arrow upgrade 0x58, 0x01, 0x36 if world.retro[player] else 0x43, 0xFF, # silver arrows -> single arrow (red 20 in retro mode) 0x3E, difficulty.boss_heart_container_limit, 0x47, 0xff, # boss heart -> green 20 @@ -1103,8 +1128,12 @@ def patch_rom(world, rom, player, team, enemized): rom.write_byte(0x180171, 0x01 if world.ganon_at_pyramid[player] else 0x00) # Enable respawning on pyramid after ganon death rom.write_byte(0x180173, 0x01) # Bob is enabled rom.write_byte(0x180168, 0x08) # Spike Cave Damage - rom.write_bytes(0x18016B, [0x04, 0x02, 0x01]) #Set spike cave and MM spike room Cape usage - rom.write_bytes(0x18016E, [0x04, 0x08, 0x10]) #Set spike cave and MM spike room Cape usage + if world.futuro[player]: + rom.write_bytes(0x18016B, [0x81, 0x04, 0x02]) # Set spike cave and MM spike room Byrna usage + rom.write_bytes(0x18016E, [0x01, 0x04, 0x08]) # Set spike cave and MM spike room Cape usage + else: + rom.write_bytes(0x18016B, [0x04, 0x02, 0x01]) # Set spike cave and MM spike room Byrna usage + rom.write_bytes(0x18016E, [0x04, 0x08, 0x10]) # Set spike cave and MM spike room Cape usage rom.write_bytes(0x50563, [0x3F, 0x14]) # disable below ganon chest rom.write_byte(0x50599, 0x00) # disable below ganon chest rom.write_bytes(0xE9A5, [0x7E, 0x00, 0x24]) # disable below ganon chest @@ -1123,7 +1152,10 @@ def patch_rom(world, rom, player, team, enemized): equip[0x36C] = 0x18 equip[0x36D] = 0x18 equip[0x379] = 0x68 - starting_max_bombs = 10 + if world.futuro[player]: + starting_max_bombs = 0 + else: + starting_max_bombs = 10 starting_max_arrows = 30 startingstate = CollectionState(world) @@ -1259,6 +1291,12 @@ def patch_rom(world, rom, player, team, enemized): else: raise RuntimeError(f'Unsupported item in starting equipment: {item.name}') + # Set basepatch switches for the futuro mode + if world.futuro[player]: + rom.write_byte(0x18008D, 0x00) + else: + rom.write_byte(0x18008E, 0x01) + equip[0x343] = min(equip[0x343], starting_max_bombs) rom.write_byte(0x180034, starting_max_bombs) equip[0x377] = min(equip[0x377], starting_max_arrows)