diff --git a/BaseClasses.py b/BaseClasses.py index 8b558fad..101e06cd 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -72,9 +72,10 @@ class World(object): self.fix_trock_exit = {} self.shuffle_ganon = shuffle_ganon self.dark_rooms = {} + self.damage_challenge = {} self.custom = custom self.customitemarray = customitemarray - self.can_take_damage = True + self.can_take_damage = {} self.hints = hints.copy() self.prizes = {} self.dynamic_regions = [] @@ -170,7 +171,9 @@ class World(object): set_player_attr('pot_contents', None) set_player_attr('pseudoboots', False) set_player_attr('mirrorscroll', False) + set_player_attr('can_take_damage', True) set_player_attr('dark_rooms', 'require_lamp') + set_player_attr('damage_challenge', 'normal') set_player_attr('crystal_book', False) set_player_attr('collection_rate', False) set_player_attr('colorizepots', True) @@ -3069,7 +3072,9 @@ class Spoiler(object): 'shopsanity': self.world.shopsanity, 'pseudoboots': self.world.pseudoboots, 'mirrorscroll': self.world.mirrorscroll, + 'can_take_damage': self.world.can_take_damage, 'dark_rooms': self.world.dark_rooms, + 'damage_challenge': self.world.damage_challenge, 'crystal_book': self.world.crystal_book, 'triforcegoal': self.world.treasure_hunt_count, 'triforcepool': self.world.treasure_hunt_total, @@ -3322,6 +3327,7 @@ class Spoiler(object): outfile.write('Pseudoboots:'.ljust(line_width) + '%s\n' % yn(self.metadata['pseudoboots'][player])) outfile.write('Mirror Scroll:'.ljust(line_width) + '%s\n' % yn(self.metadata['mirrorscroll'][player])) outfile.write('Dark Rooms:'.ljust(line_width) + '%s\n' % self.metadata['dark_rooms'][player]) + outfile.write('Damage Challenge:'.ljust(line_width) + '%s\n' % self.metadata['damage_challenge'][player]) outfile.write('Crystal Book:'.ljust(line_width) + '%s\n' % yn(self.metadata['crystal_book'][player])) outfile.write('Hints:'.ljust(line_width) + '%s\n' % yn(self.metadata['hints'][player])) outfile.write('Race:'.ljust(line_width) + '%s\n' % yn(self.world.settings.world_rep['meta']['race'])) diff --git a/CLI.py b/CLI.py index cb3bca05..abf5245d 100644 --- a/CLI.py +++ b/CLI.py @@ -139,7 +139,7 @@ def parse_cli(argv, no_defaults=False): 'triforce_max_difference', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', 'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'shuffletavern', 'skullwoods', 'linked_drops', - 'pseudoboots', 'mirrorscroll', 'dark_rooms', 'crystal_book', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters', + 'pseudoboots', 'mirrorscroll', 'dark_rooms', 'damage_challenge', 'crystal_book', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters', 'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots', 'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle', @@ -212,6 +212,7 @@ def parse_settings(): "pseudoboots": False, "mirrorscroll": False, "dark_rooms": "require_lamp", + "damage_challenge": "normal", "crystal_book": False, "shuffleenemies": "none", diff --git a/ItemList.py b/ItemList.py index 4befd714..693fa96d 100644 --- a/ItemList.py +++ b/ItemList.py @@ -226,7 +226,10 @@ def generate_itempool(world, player): raise NotImplementedError('Not supported yet') if world.timer in ['ohko', 'timed-ohko']: - world.can_take_damage = False + world.can_take_damage[player] = False + + if world.damage_challenge[player] in ['ohko', 'gloom']: + world.can_take_damage[player] = False if world.goal[player] in ['pedestal', 'triforcehunt', 'sanctuary']: set_event_item(world, player, 'Ganon', 'Nothing') @@ -1287,7 +1290,7 @@ def modify_pool_for_start_inventory(start_inventory, world, player): d.big_key = None -def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer, goal, mode, swords, bombbag, dark_rooms, customitemarray): +def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer, goal, mode, swords, bombbag, customitemarray): pool = [] placed_items = {} precollected_items = [] diff --git a/Main.py b/Main.py index 75d0c174..bb7a4cdf 100644 --- a/Main.py +++ b/Main.py @@ -514,6 +514,7 @@ def init_world(args, fish): world.pseudoboots = args.pseudoboots.copy() world.mirrorscroll = args.mirrorscroll.copy() world.dark_rooms = args.dark_rooms.copy() + world.damage_challenge = args.damage_challenge.copy() world.crystal_book = args.crystal_book.copy() world.overworld_map = args.overworld_map.copy() world.take_any = args.take_any.copy() @@ -619,6 +620,7 @@ def copy_world(world): ret.pseudoboots = world.pseudoboots.copy() ret.mirrorscroll = world.mirrorscroll.copy() ret.dark_rooms = world.dark_rooms.copy() + ret.damage_challenge = world.damage_challenge.copy() ret.crystal_book = world.crystal_book.copy() ret.overworld_map = world.overworld_map.copy() ret.take_any = world.take_any.copy() @@ -842,6 +844,7 @@ def copy_world_premature(world, player): ret.pseudoboots = world.pseudoboots.copy() ret.mirrorscroll = world.mirrorscroll.copy() ret.dark_rooms = world.dark_rooms.copy() + ret.damage_challenge = world.damage_challenge.copy() ret.crystal_book = world.crystal_book.copy() ret.overworld_map = world.overworld_map.copy() ret.take_any = world.take_any.copy() diff --git a/Rom.py b/Rom.py index aa162e02..2028d274 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'f68e3c6169462af6e95c56b65b40701b' +RANDOMIZERBASEHASH = 'a23774f12c16a50834097fc0a661f6c1' class JsonRom(object): @@ -1141,6 +1141,22 @@ def patch_rom(world, rom, player, team, is_mystery=False): if world.swords[player] == 'swordless': rom.initial_sram.set_swordless_curtains() # open curtains + # set up challenge modes + challenge = 0x00; + if world.damage_challenge[player] == 'ohko': + challenge |= 0x01 + rom.write_byte(0x39C6B, 0x80) # never spawn sword beams + elif world.damage_challenge[player] == 'gloom': + challenge |= 0x02 + rom.write_bytes(0x4F4AC, [0x08, + 0x08, 0x10, 0x18, 0x20, 0x28, + 0x30, 0x38, 0x40, 0x48, 0x50, + 0x58, 0x60, 0x68, 0x70, 0x78, + 0x80, 0x88, 0x90, 0x98, 0xA0]) # spawn with full health + rom.write_byte(0x39C6B, 0x80) # never spawn sword beams + rom.write_bytes(0x187033, [0x28, 0x20, 0x28, 0x20]) # heart icon + rom.write_byte(0x18002D, challenge) + # set up clocks for timed modes if world.shuffle[player] == 'vanilla': ERtimeincrease = 0 diff --git a/Rules.py b/Rules.py index b4c9ab71..6fcfbbf6 100644 --- a/Rules.py +++ b/Rules.py @@ -287,7 +287,7 @@ def global_rules(world, player): ((state.has('Cape', player) and state.can_extend_magic(player, 16, True)) or (state.has('Cane of Byrna', player) and (state.can_extend_magic(player, 12, True) or - (state.world.can_take_damage and (state.has_Boots(player) or state.has_hearts(player, 4)))))) + (state.world.can_take_damage[player] and (state.has_Boots(player) or state.has_hearts(player, 4)))))) ) # underworld rules @@ -539,10 +539,10 @@ def global_rules(world, player): set_rule(world.get_location('Ice Palace - Map Chest', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) set_rule(world.get_entrance('Ice Antechamber Hole', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) # todo: ohko rules for spike room - could split into two regions instead of these, but can_take_damage is usually true - set_rule(world.get_entrance('Ice Spike Room WS', player), lambda state: state.world.can_take_damage or state.has('Hookshot', player) or state.has('Cape', player) or state.has('Cane of Byrna', player)) - set_rule(world.get_entrance('Ice Spike Room Up Stairs', player), lambda state: state.world.can_take_damage or state.has('Hookshot', player) or state.has('Cape', player) or state.has('Cane of Byrna', player)) - set_rule(world.get_entrance('Ice Spike Room Down Stairs', player), lambda state: state.world.can_take_damage or state.has('Hookshot', player) or state.has('Cape', player) or state.has('Cane of Byrna', player)) - set_rule(world.get_location('Ice Palace - Spike Room', player), lambda state: state.world.can_take_damage or state.has('Hookshot', player) or state.has('Cape', player) or state.has('Cane of Byrna', player)) + set_rule(world.get_entrance('Ice Spike Room WS', player), lambda state: 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 Spike Room Up Stairs', player), lambda state: 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 Spike Room Down Stairs', player), lambda state: 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_location('Ice Palace - Spike Room', player), lambda state: 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_location('Ice Palace - Freezor Chest', player), lambda state: state.can_melt_things(player)) set_rule(world.get_entrance('Ice Hookshot Ledge Path', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('Ice Hookshot Balcony Path', player), lambda state: state.has('Hookshot', player)) @@ -566,11 +566,11 @@ def global_rules(world, player): # (state.has('Ice Rod', player) and state.can_use_bombs(player)) or # freeze popo and throw, bomb to finish # 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 ... # byrna could work with sufficient magic - set_rule(world.get_location('Misery Mire - Spike Chest', player), lambda state: (state.world.can_take_damage and state.has_hearts(player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player)) + set_rule(world.get_location('Misery Mire - Spike Chest', player), lambda state: (state.world.can_take_damage[player] and state.has_hearts(player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player)) loc = world.get_location('Misery Mire - Spikes Pot Key', player) if loc.pot: if loc.pot.x == 48 and loc.pot.y == 28: # pot shuffled to spike area - set_rule(loc, lambda state: (state.world.can_take_damage and state.has_hearts(player, 4)) + set_rule(loc, lambda state: (state.world.can_take_damage[player] and state.has_hearts(player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player)) set_rule(world.get_entrance('Mire Left Bridge Hook Path', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('Mire Tile Room NW', player), lambda state: state.has_fire_source(player)) @@ -734,8 +734,8 @@ def global_rules(world, player): set_rule(world.get_entrance('Swamp Crystal Switch Inner to Crystal', player), lambda state: state.can_hit_crystal(player)) set_rule(world.get_entrance('Swamp Crystal Switch Outer to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or state.has_beam_sword(player) or (state.has('Hookshot', player) and state.can_reach_blue(world.get_region('Swamp Crystal Switch Outer', player), player))) # It is the length of the sword, not the beam itself that allows this - set_rule(world.get_entrance('Swamp Crystal Switch Outer to Inner Bypass', player), lambda state: state.world.can_take_damage or state.has('Cape', player) or state.has('Cane of Byrna', player)) - set_rule(world.get_entrance('Swamp Crystal Switch Inner to Outer Bypass', player), lambda state: state.world.can_take_damage or state.has('Cape', player) or state.has('Cane of Byrna', player)) + set_rule(world.get_entrance('Swamp Crystal Switch Outer to Inner Bypass', player), lambda state: state.world.can_take_damage[player] or state.has('Cape', player) or state.has('Cane of Byrna', player)) + set_rule(world.get_entrance('Swamp Crystal Switch Inner to Outer Bypass', player), lambda state: state.world.can_take_damage[player] or state.has('Cape', player) or state.has('Cane of Byrna', player)) set_rule(world.get_entrance('Thieves Hellway Blue Barrier', player), lambda state: state.can_reach_blue(world.get_region('Thieves Hellway', player), player)) set_rule(world.get_entrance('Thieves Hellway Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Thieves Hellway', player), player)) @@ -778,7 +778,7 @@ def global_rules(world, player): set_rule(world.get_entrance('Mire Conveyor to Crystal', player), lambda state: state.can_hit_crystal(player)) set_rule(world.get_entrance('Mire Tall Dark and Roomy to Ranged Crystal', player), lambda state: True) # Can always throw pots - set_rule(world.get_entrance('Mire Fishbone Blue Barrier Bypass', player), lambda state: False) # (state.world.can_take_damage or state.has('Cape', player) or state.has('Cane of Byrna', player)) and state.can_tastate.can_use_bombs(player) // Easy to do but obscure. Should it be in logic? + set_rule(world.get_entrance('Mire Fishbone Blue Barrier Bypass', player), lambda state: False) # (state.world.can_take_damage[player] or state.has('Cape', player) or state.has('Cane of Byrna', player)) and state.can_tastate.can_use_bombs(player) // Easy to do but obscure. Should it be in logic? set_rule(world.get_location('Turtle Rock - Chain Chomps', player), lambda state: state.can_reach('TR Chain Chomps Top', 'Region', player) and state.can_hit_crystal_through_barrier(player)) set_rule(world.get_entrance('TR Chain Chomps Top to Bottom Barrier - Orange', player), lambda state: state.can_reach_orange(world.get_region('TR Chain Chomps Top', player), player)) @@ -998,7 +998,7 @@ def pot_rules(world, player): ((state.has('Cape', player) and state.can_extend_magic(player, 16, True)) or (state.has('Cane of Byrna', player) and (state.can_extend_magic(player, 12, True) or - (state.world.can_take_damage and (state.has_Boots(player) or state.has_hearts(player, 4))))))) + (state.world.can_take_damage[player] and (state.has_Boots(player) or state.has_hearts(player, 4))))))) for l in world.get_region('Mire Hint', player).locations: if l.type == LocationType.Pot: add_rule(l, lambda state: state.can_use_bombs(player)) @@ -1008,7 +1008,7 @@ def pot_rules(world, player): for number in ['1', '2']: loc = world.get_location_unsafe(f'Dark Lake Hylia Ledge Spike Cave Pot #{number}', player) if loc and loc.type == LocationType.Pot: - add_rule(loc, lambda state: state.world.can_take_damage or state.has('Hookshot', player) + add_rule(loc, lambda state: state.world.can_take_damage[player] or state.has('Hookshot', player) or state.has('Cape', player) or (state.has('Cane of Byrna', player) and state.world.difficulty_adjustments[player] == 'normal')) @@ -1021,7 +1021,7 @@ def pot_rules(world, player): set_rule(loc, lambda state: state.has('Hammer', player) and state.can_lift_rocks(player)) loc = world.get_location_unsafe('Mire Spikes Pot #3', player) if loc: - set_rule(loc, lambda state: (state.world.can_take_damage and state.has_hearts(player, 4)) + set_rule(loc, lambda state: (state.world.can_take_damage[player] and state.has_hearts(player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player)) for l in world.get_region('Ice Refill', player).locations: if l.type == LocationType.Pot: diff --git a/data/base2current.bps b/data/base2current.bps index a695e8e3..a20ee0f3 100644 Binary files a/data/base2current.bps and b/data/base2current.bps differ diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index 27e128eb..bc5a989b 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -571,6 +571,13 @@ "always_in_logic" ] }, + "damage_challenge": { + "choices": [ + "normal", + "ohko", + "gloom" + ] + }, "crystal_book": { "action": "store_true", "type": "bool"