Damage Challenge modes: OHKO and Gloom

This commit is contained in:
2025-05-18 01:00:09 -05:00
parent 04270990cb
commit 53be043af4
8 changed files with 54 additions and 18 deletions

View File

@@ -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']))

3
CLI.py
View File

@@ -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",

View File

@@ -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 = []

View File

@@ -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()

18
Rom.py
View File

@@ -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

View File

@@ -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:

Binary file not shown.

View File

@@ -571,6 +571,13 @@
"always_in_logic"
]
},
"damage_challenge": {
"choices": [
"normal",
"ohko",
"gloom"
]
},
"crystal_book": {
"action": "store_true",
"type": "bool"