parent
3e31502fc0
commit
9d4d3b8456
|
@ -185,7 +185,7 @@ class World(object):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def has_beaten_game(self, state):
|
def has_beaten_game(self, state):
|
||||||
if state.has('Triforce'): return True
|
if state.has('Triforce'): return True
|
||||||
if self.goal in ['triforcehunt']:
|
if self.goal in ['triforcehunt']:
|
||||||
|
@ -329,7 +329,7 @@ class CollectionState(object):
|
||||||
return item in self.prog_items
|
return item in self.prog_items
|
||||||
else:
|
else:
|
||||||
return self.item_count(item) >= count
|
return self.item_count(item) >= count
|
||||||
|
|
||||||
def item_count(self, item):
|
def item_count(self, item):
|
||||||
return len([pritem for pritem in self.prog_items if pritem == item])
|
return len([pritem for pritem in self.prog_items if pritem == item])
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,9 @@ if __name__ == '__main__':
|
||||||
parser.add_argument('--logic', default='noglitches', const='noglitches', nargs='?', choices=['noglitches', 'minorglitches'],
|
parser.add_argument('--logic', default='noglitches', const='noglitches', nargs='?', choices=['noglitches', 'minorglitches'],
|
||||||
help='''\
|
help='''\
|
||||||
Select Enforcement of Item Requirements. (default: %(default)s)
|
Select Enforcement of Item Requirements. (default: %(default)s)
|
||||||
No Glitches:
|
No Glitches:
|
||||||
Minor Glitches: May require Fake Flippers, Bunny Revival
|
Minor Glitches: May require Fake Flippers, Bunny Revival
|
||||||
and Dark Room Navigation.
|
and Dark Room Navigation.
|
||||||
''')
|
''')
|
||||||
parser.add_argument('--mode', default='open', const='open', nargs='?', choices=['standard', 'open', 'swordless'],
|
parser.add_argument('--mode', default='open', const='open', nargs='?', choices=['standard', 'open', 'swordless'],
|
||||||
help='''\
|
help='''\
|
||||||
|
@ -37,19 +37,19 @@ if __name__ == '__main__':
|
||||||
Agahnim\'s Tower barrier can be destroyed with
|
Agahnim\'s Tower barrier can be destroyed with
|
||||||
hammer. Misery Mire and Turtle Rock can be opened
|
hammer. Misery Mire and Turtle Rock can be opened
|
||||||
without a sword. Hammer damages Ganon. Ether and
|
without a sword. Hammer damages Ganon. Ether and
|
||||||
Bombos Tablet can be activated with Hammer (and Book).
|
Bombos Tablet can be activated with Hammer (and Book).
|
||||||
''')
|
''')
|
||||||
parser.add_argument('--goal', default='ganon', const='ganon', nargs='?', choices=['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'],
|
parser.add_argument('--goal', default='ganon', const='ganon', nargs='?', choices=['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'],
|
||||||
help='''\
|
help='''\
|
||||||
Select completion goal. (default: %(default)s)
|
Select completion goal. (default: %(default)s)
|
||||||
Ganon: Collect all crystals, beat Agahnim 2 then
|
Ganon: Collect all crystals, beat Agahnim 2 then
|
||||||
defeat Ganon.
|
defeat Ganon.
|
||||||
Crystals: Collect all crystals then defeat Ganon.
|
Crystals: Collect all crystals then defeat Ganon.
|
||||||
Pedestal: Places the Triforce at the Master Sword Pedestal.
|
Pedestal: Places the Triforce at the Master Sword Pedestal.
|
||||||
All Dungeons: Collect all crystals, pendants, beat both
|
All Dungeons: Collect all crystals, pendants, beat both
|
||||||
Agahnim fights and then defeat Ganon.
|
Agahnim fights and then defeat Ganon.
|
||||||
Triforce Hunt: Places 30 Triforce Pieces in the world, collect
|
Triforce Hunt: Places 30 Triforce Pieces in the world, collect
|
||||||
20 of them to beat the game.
|
20 of them to beat the game.
|
||||||
''')
|
''')
|
||||||
parser.add_argument('--difficulty', default='normal', const='normal', nargs='?', choices=['easy', 'normal', 'hard', 'expert', 'insane'],
|
parser.add_argument('--difficulty', default='normal', const='normal', nargs='?', choices=['easy', 'normal', 'hard', 'expert', 'insane'],
|
||||||
help='''\
|
help='''\
|
||||||
|
@ -58,7 +58,7 @@ if __name__ == '__main__':
|
||||||
Normal: Normal difficulty.
|
Normal: Normal difficulty.
|
||||||
Hard: A harder setting with less equipment and reduced health.
|
Hard: A harder setting with less equipment and reduced health.
|
||||||
Expert: A harder yet setting with minimum equipment and health.
|
Expert: A harder yet setting with minimum equipment and health.
|
||||||
Insane: A setting with the absolute minimum in equipment and no extra health.
|
Insane: A setting with the absolute minimum in equipment and no extra health.
|
||||||
''')
|
''')
|
||||||
parser.add_argument('--timer', default='none', const='normal', nargs='?', choices=['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'],
|
parser.add_argument('--timer', default='none', const='normal', nargs='?', choices=['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'],
|
||||||
help='''\
|
help='''\
|
||||||
|
@ -78,7 +78,7 @@ if __name__ == '__main__':
|
||||||
and the clock is permenantly at zero.
|
and the clock is permenantly at zero.
|
||||||
Timed Countdown: Starts with clock at 40 minutes. Same clocks as
|
Timed Countdown: Starts with clock at 40 minutes. Same clocks as
|
||||||
Timed mode. If time runs out, you lose (but can
|
Timed mode. If time runs out, you lose (but can
|
||||||
still keep playing).
|
still keep playing).
|
||||||
''')
|
''')
|
||||||
parser.add_argument('--progressive', default='on', const='normal', nargs='?', choices=['on', 'off', 'random'],
|
parser.add_argument('--progressive', default='on', const='normal', nargs='?', choices=['on', 'off', 'random'],
|
||||||
help='''\
|
help='''\
|
||||||
|
@ -92,7 +92,7 @@ if __name__ == '__main__':
|
||||||
be found at any time. Downgrades are not possible.
|
be found at any time. Downgrades are not possible.
|
||||||
Random: Swords, Shields, Armor, and Gloves will, per
|
Random: Swords, Shields, Armor, and Gloves will, per
|
||||||
category, be randomly progressive or not.
|
category, be randomly progressive or not.
|
||||||
Link will die in one hit.
|
Link will die in one hit.
|
||||||
''')
|
''')
|
||||||
parser.add_argument('--algorithm', default='balanced', const='balanced', nargs='?', choices=['freshness', 'flood', 'vt21', 'vt22', 'vt25', 'vt26', 'balanced'],
|
parser.add_argument('--algorithm', default='balanced', const='balanced', nargs='?', choices=['freshness', 'flood', 'vt21', 'vt22', 'vt25', 'vt26', 'balanced'],
|
||||||
help='''\
|
help='''\
|
||||||
|
@ -115,7 +115,7 @@ if __name__ == '__main__':
|
||||||
them the more often they were found unreachable.
|
them the more often they were found unreachable.
|
||||||
Flood: Push out items starting from Link\'s House and
|
Flood: Push out items starting from Link\'s House and
|
||||||
slightly biased to placing progression items with
|
slightly biased to placing progression items with
|
||||||
less restrictions.
|
less restrictions.
|
||||||
''')
|
''')
|
||||||
parser.add_argument('--shuffle', default='full', const='full', nargs='?', choices=['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'],
|
parser.add_argument('--shuffle', default='full', const='full', nargs='?', choices=['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'],
|
||||||
help='''\
|
help='''\
|
||||||
|
@ -137,7 +137,7 @@ if __name__ == '__main__':
|
||||||
discretion.
|
discretion.
|
||||||
Experimental.
|
Experimental.
|
||||||
The dungeon variants only mix up dungeons and keep the rest of
|
The dungeon variants only mix up dungeons and keep the rest of
|
||||||
the overworld vanilla.
|
the overworld vanilla.
|
||||||
''')
|
''')
|
||||||
parser.add_argument('--rom', default='Zelda no Densetsu - Kamigami no Triforce (Japan).sfc', help='Path to an ALttP JAP(1.0) rom to use as a base.')
|
parser.add_argument('--rom', default='Zelda no Densetsu - Kamigami no Triforce (Japan).sfc', help='Path to an ALttP JAP(1.0) rom to use as a base.')
|
||||||
parser.add_argument('--loglevel', default='info', const='info', nargs='?', choices=['error', 'info', 'warning', 'debug'], help='Select level of logging for output.')
|
parser.add_argument('--loglevel', default='info', const='info', nargs='?', choices=['error', 'info', 'warning', 'debug'], help='Select level of logging for output.')
|
||||||
|
@ -147,7 +147,7 @@ if __name__ == '__main__':
|
||||||
If --seed is provided, it will be used for the first seed, then
|
If --seed is provided, it will be used for the first seed, then
|
||||||
used to derive the next seed (i.e. generating 10 seeds with
|
used to derive the next seed (i.e. generating 10 seeds with
|
||||||
--seed given will produce the same 10 (different) roms each
|
--seed given will produce the same 10 (different) roms each
|
||||||
time).
|
time).
|
||||||
''', type=int)
|
''', type=int)
|
||||||
parser.add_argument('--fastmenu', help='Enable instant menu', action='store_true')
|
parser.add_argument('--fastmenu', help='Enable instant menu', action='store_true')
|
||||||
parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true')
|
parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true')
|
||||||
|
@ -157,33 +157,33 @@ if __name__ == '__main__':
|
||||||
''', action='store_true')
|
''', action='store_true')
|
||||||
parser.add_argument('--nodungeonitems', help='''\
|
parser.add_argument('--nodungeonitems', help='''\
|
||||||
Remove Maps and Compasses from Itempool, replacing them by
|
Remove Maps and Compasses from Itempool, replacing them by
|
||||||
empty slots.
|
empty slots.
|
||||||
''', action='store_true')
|
''', action='store_true')
|
||||||
parser.add_argument('--beatableonly', help='''\
|
parser.add_argument('--beatableonly', help='''\
|
||||||
Only check if the game is beatable with placement. Do not
|
Only check if the game is beatable with placement. Do not
|
||||||
ensure all locations are reachable. This only has an effect
|
ensure all locations are reachable. This only has an effect
|
||||||
on the restrictive algorithm currently.
|
on the restrictive algorithm currently.
|
||||||
''', action='store_true')
|
''', action='store_true')
|
||||||
parser.add_argument('--shuffleganon', help='''\
|
parser.add_argument('--shuffleganon', help='''\
|
||||||
If set, include the Pyramid Hole and Ganon's Tower in the
|
If set, include the Pyramid Hole and Ganon's Tower in the
|
||||||
entrance shuffle pool.
|
entrance shuffle pool.
|
||||||
''', action='store_true')
|
''', action='store_true')
|
||||||
parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['normal', 'half', 'quarter', 'off'],
|
parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['normal', 'half', 'quarter', 'off'],
|
||||||
help='''\
|
help='''\
|
||||||
Select the rate at which the heart beep sound is played at
|
Select the rate at which the heart beep sound is played at
|
||||||
low health. (default: %(default)s)
|
low health. (default: %(default)s)
|
||||||
''')
|
''')
|
||||||
parser.add_argument('--sprite', help='''\
|
parser.add_argument('--sprite', help='''\
|
||||||
Path to a sprite sheet to use for Link. Needs to be in
|
Path to a sprite sheet to use for Link. Needs to be in
|
||||||
binary format and have a length of 0x7000 (28672) bytes,
|
binary format and have a length of 0x7000 (28672) bytes,
|
||||||
or 0x7078 (28792) bytes including palette data.
|
or 0x7078 (28792) bytes including palette data.
|
||||||
Alternatively, can be a ALttP Rom patched with a Link
|
Alternatively, can be a ALttP Rom patched with a Link
|
||||||
sprite that will be extracted.
|
sprite that will be extracted.
|
||||||
''')
|
''')
|
||||||
parser.add_argument('--suppress_rom', help='Do not create an output rom file.', action='store_true')
|
parser.add_argument('--suppress_rom', help='Do not create an output rom file.', action='store_true')
|
||||||
parser.add_argument('--gui', help='Launch the GUI', action='store_true')
|
parser.add_argument('--gui', help='Launch the GUI', action='store_true')
|
||||||
parser.add_argument('--jsonout', action='store_true', help='''\
|
parser.add_argument('--jsonout', action='store_true', help='''\
|
||||||
Output .json patch to stdout instead of a patched rom. Used
|
Output .json patch to stdout instead of a patched rom. Used
|
||||||
for VT site integration, do not use otherwise.
|
for VT site integration, do not use otherwise.
|
||||||
''')
|
''')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
2
Fill.py
2
Fill.py
|
@ -167,7 +167,7 @@ def fill_restrictive(world, base_state, locations, itempool):
|
||||||
while itempool and locations:
|
while itempool and locations:
|
||||||
item_to_place = itempool.pop()
|
item_to_place = itempool.pop()
|
||||||
maximum_exploration_state = sweep_from_pool()
|
maximum_exploration_state = sweep_from_pool()
|
||||||
|
|
||||||
if world.check_beatable_only:
|
if world.check_beatable_only:
|
||||||
can_beat_without = world.has_beaten_game(maximum_exploration_state)
|
can_beat_without = world.has_beaten_game(maximum_exploration_state)
|
||||||
|
|
||||||
|
|
2
Main.py
2
Main.py
|
@ -228,4 +228,4 @@ def create_playthrough(world):
|
||||||
old_world.required_locations = [location.name for sphere in collection_spheres for location in sphere]
|
old_world.required_locations = [location.name for sphere in collection_spheres for location in sphere]
|
||||||
|
|
||||||
# we can finally output our playthrough
|
# we can finally output our playthrough
|
||||||
old_world.spoiler.playthrough = OrderedDict([(str(i + 1), {str(location): str(location.item) for location in sphere}) for i, sphere in enumerate(collection_spheres)])
|
old_world.spoiler.playthrough = OrderedDict([(str(i + 1), {str(location): str(location.item) for location in sphere}) for i, sphere in enumerate(collection_spheres)])
|
||||||
|
|
10
README.md
10
README.md
|
@ -209,7 +209,7 @@ randomized.
|
||||||
--algorithm [{freshness,flood,vt21,vt22,vt25,vt26,balanced}]
|
--algorithm [{freshness,flood,vt21,vt22,vt25,vt26,balanced}]
|
||||||
```
|
```
|
||||||
|
|
||||||
Select item filling algorithm.
|
Select item filling algorithm.
|
||||||
|
|
||||||
### Balanced (Default)
|
### Balanced (Default)
|
||||||
This is a variation of vt26 that aims to strike a balance between the overworld heavy vt25 and the dungeon heavy vt26 algorithm.
|
This is a variation of vt26 that aims to strike a balance between the overworld heavy vt25 and the dungeon heavy vt26 algorithm.
|
||||||
|
@ -220,7 +220,7 @@ Items and locations are shuffled like in VT25, and dungeon items are now placed
|
||||||
shuffled it includes a slight deliberate bias against having too many desireable items in Ganon's Tower to help counterbalance
|
shuffled it includes a slight deliberate bias against having too many desireable items in Ganon's Tower to help counterbalance
|
||||||
the sheer number of chests in that single location.
|
the sheer number of chests in that single location.
|
||||||
|
|
||||||
### VT25
|
### VT25
|
||||||
Items and locations are shuffled and placed from the top of the lists. The only thing preventing an item from being placed into a spot
|
Items and locations are shuffled and placed from the top of the lists. The only thing preventing an item from being placed into a spot
|
||||||
is if is absolutely impossible to be there given the previous made placement choices. Leads to very uniform but guaranteed solvable distributions.
|
is if is absolutely impossible to be there given the previous made placement choices. Leads to very uniform but guaranteed solvable distributions.
|
||||||
|
|
||||||
|
@ -243,7 +243,7 @@ staleness, decreasing the likelihood of receiving a progress item.
|
||||||
--shuffle [{default,simple,restricted,full,madness,insanity,dungeonsfull,dungeonssimple}]
|
--shuffle [{default,simple,restricted,full,madness,insanity,dungeonsfull,dungeonssimple}]
|
||||||
```
|
```
|
||||||
|
|
||||||
Select Entrance Shuffling Algorithm.
|
Select Entrance Shuffling Algorithm.
|
||||||
|
|
||||||
### Default
|
### Default
|
||||||
|
|
||||||
|
@ -256,7 +256,7 @@ on the overworld. On Death Mountain, entrances are connected more freely.
|
||||||
|
|
||||||
### Full (Default)
|
### Full (Default)
|
||||||
|
|
||||||
Mixes cave and dungeon entrances freely.
|
Mixes cave and dungeon entrances freely.
|
||||||
|
|
||||||
### Restricted
|
### Restricted
|
||||||
|
|
||||||
|
@ -296,7 +296,7 @@ Define seed number to generate. (default: None) Using the same seed with same se
|
||||||
--count COUNT
|
--count COUNT
|
||||||
```
|
```
|
||||||
|
|
||||||
Use to batch generate multiple seeds with same settings.
|
Use to batch generate multiple seeds with same settings.
|
||||||
If --seed is provided, it will be used for the first seed, then used to derive the next seed (i.e. generating 10 seeds with --seed given will produce the same 10 (different) roms each time). (default: None)
|
If --seed is provided, it will be used for the first seed, then used to derive the next seed (i.e. generating 10 seeds with --seed given will produce the same 10 (different) roms each time). (default: None)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
8
Rom.py
8
Rom.py
|
@ -21,10 +21,10 @@ class JsonRom(object):
|
||||||
|
|
||||||
def write_bytes(self, startaddress, values):
|
def write_bytes(self, startaddress, values):
|
||||||
self.patches[str(startaddress)] = list(values)
|
self.patches[str(startaddress)] = list(values)
|
||||||
|
|
||||||
def write_int16_to_rom(self, address, value):
|
def write_int16_to_rom(self, address, value):
|
||||||
self.write_bytes(address, int16_as_bytes(value))
|
self.write_bytes(address, int16_as_bytes(value))
|
||||||
|
|
||||||
def write_int32_to_rom(self, address, value):
|
def write_int32_to_rom(self, address, value):
|
||||||
self.write_bytes(address, int32_as_bytes(value))
|
self.write_bytes(address, int32_as_bytes(value))
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ class LocalRom(object):
|
||||||
|
|
||||||
def write_int16_to_rom(self, address, value):
|
def write_int16_to_rom(self, address, value):
|
||||||
self.write_bytes(address, int16_as_bytes(value))
|
self.write_bytes(address, int16_as_bytes(value))
|
||||||
|
|
||||||
def write_int32_to_rom(self, address, value):
|
def write_int32_to_rom(self, address, value):
|
||||||
self.write_bytes(address, int32_as_bytes(value))
|
self.write_bytes(address, int32_as_bytes(value))
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ class LocalRom(object):
|
||||||
def int16_as_bytes(value):
|
def int16_as_bytes(value):
|
||||||
value = value & 0xFFFF
|
value = value & 0xFFFF
|
||||||
return [value & 0xFF, (value >> 8) & 0xFF]
|
return [value & 0xFF, (value >> 8) & 0xFF]
|
||||||
|
|
||||||
def int32_as_bytes(value):
|
def int32_as_bytes(value):
|
||||||
value = value & 0xFFFFFFFF
|
value = value & 0xFFFFFFFF
|
||||||
return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF]
|
return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF]
|
||||||
|
|
24
Rules.py
24
Rules.py
|
@ -301,7 +301,7 @@ def global_rules(world):
|
||||||
set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase'), lambda state: state.has('Small Key (Palace of Darkness)', 5) or (state.world.get_location('Palace of Darkness - Big Key Chest').item is not None and (state.world.get_location('Palace of Darkness - Big Key Chest').item.name in ['Small Key (Palace of Darkness)'])))
|
set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase'), lambda state: state.has('Small Key (Palace of Darkness)', 5) or (state.world.get_location('Palace of Darkness - Big Key Chest').item is not None and (state.world.get_location('Palace of Darkness - Big Key Chest').item.name in ['Small Key (Palace of Darkness)'])))
|
||||||
set_rule(world.get_entrance('Palace of Darkness (North)'), lambda state: state.has('Small Key (Palace of Darkness)', 4))
|
set_rule(world.get_entrance('Palace of Darkness (North)'), lambda state: state.has('Small Key (Palace of Darkness)', 4))
|
||||||
set_rule(world.get_location('Palace of Darkness - Big Chest'), lambda state: state.has('Big Key (Palace of Darkness)'))
|
set_rule(world.get_location('Palace of Darkness - Big Chest'), lambda state: state.has('Big Key (Palace of Darkness)'))
|
||||||
|
|
||||||
if world.keysanity:
|
if world.keysanity:
|
||||||
set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door'), lambda state: state.has('Small Key (Palace of Darkness)', 6) or (state.world.get_location('Palace of Darkness - Harmless Hellway').item is not None and (state.world.get_location('Palace of Darkness - Harmless Hellway').item.name in ['Small Key (Palace of Darkness)'])))
|
set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door'), lambda state: state.has('Small Key (Palace of Darkness)', 6) or (state.world.get_location('Palace of Darkness - Harmless Hellway').item is not None and (state.world.get_location('Palace of Darkness - Harmless Hellway').item.name in ['Small Key (Palace of Darkness)'])))
|
||||||
set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase'), lambda state: state.has('Small Key (Palace of Darkness)', 6) or (state.world.get_location('Palace of Darkness - Big Key Chest').item is not None and (state.world.get_location('Palace of Darkness - Big Key Chest').item.name in ['Small Key (Palace of Darkness)'])))
|
set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase'), lambda state: state.has('Small Key (Palace of Darkness)', 6) or (state.world.get_location('Palace of Darkness - Big Key Chest').item is not None and (state.world.get_location('Palace of Darkness - Big Key Chest').item.name in ['Small Key (Palace of Darkness)'])))
|
||||||
|
@ -310,7 +310,7 @@ def global_rules(world):
|
||||||
set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door'), lambda state: state.has('Small Key (Palace of Darkness)', 5) or (state.world.get_location('Palace of Darkness - Harmless Hellway').item is not None and (state.world.get_location('Palace of Darkness - Harmless Hellway').item.name in ['Small Key (Palace of Darkness)'])))
|
set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door'), lambda state: state.has('Small Key (Palace of Darkness)', 5) or (state.world.get_location('Palace of Darkness - Harmless Hellway').item is not None and (state.world.get_location('Palace of Darkness - Harmless Hellway').item.name in ['Small Key (Palace of Darkness)'])))
|
||||||
set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase'), lambda state: state.has('Small Key (Palace of Darkness)', 5) or (state.world.get_location('Palace of Darkness - Big Key Chest').item is not None and (state.world.get_location('Palace of Darkness - Big Key Chest').item.name in ['Small Key (Palace of Darkness)'])))
|
set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase'), lambda state: state.has('Small Key (Palace of Darkness)', 5) or (state.world.get_location('Palace of Darkness - Big Key Chest').item is not None and (state.world.get_location('Palace of Darkness - Big Key Chest').item.name in ['Small Key (Palace of Darkness)'])))
|
||||||
set_rule(world.get_entrance('Palace of Darkness Maze Door'), lambda state: state.has('Small Key (Palace of Darkness)', 5))
|
set_rule(world.get_entrance('Palace of Darkness Maze Door'), lambda state: state.has('Small Key (Palace of Darkness)', 5))
|
||||||
|
|
||||||
for location in ['Palace of Darkness - Big Chest', 'Palace of Darkness - Helmasaur']:
|
for location in ['Palace of Darkness - Big Chest', 'Palace of Darkness - Helmasaur']:
|
||||||
forbid_item(world.get_location(location), 'Big Key (Palace of Darkness)')
|
forbid_item(world.get_location(location), 'Big Key (Palace of Darkness)')
|
||||||
|
|
||||||
|
@ -320,7 +320,7 @@ def global_rules(world):
|
||||||
# these key rules are conservative, you might be able to get away with more lenient rules
|
# 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']
|
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']
|
||||||
compass_room_chests = ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right', 'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right']
|
compass_room_chests = ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right', 'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right']
|
||||||
|
|
||||||
set_rule(world.get_location('Ganons Tower - Bob\'s Torch'), lambda state: state.has_Boots())
|
set_rule(world.get_location('Ganons Tower - Bob\'s Torch'), lambda state: state.has_Boots())
|
||||||
set_rule(world.get_entrance('Ganons Tower (Tile Room)'), lambda state: state.has('Cane of Somaria'))
|
set_rule(world.get_entrance('Ganons Tower (Tile Room)'), lambda state: state.has('Cane of Somaria'))
|
||||||
set_rule(world.get_entrance('Ganons Tower (Hookshot Room)'), lambda state: state.has('Hammer'))
|
set_rule(world.get_entrance('Ganons Tower (Hookshot Room)'), lambda state: state.has('Hammer'))
|
||||||
|
@ -328,24 +328,24 @@ def global_rules(world):
|
||||||
set_rule(world.get_entrance('Ganons Tower (Map Room)'), lambda state: state.has('Small Key (Ganons Tower)', 4) or (state.world.get_location('Ganons Tower - Map Chest').item is not None and state.world.get_location('Ganons Tower - Map Chest').item.name == 'Big Key (Ganons Tower)' and state.has('Small Key (Ganons Tower)', 3)) or (state.world.get_location('Ganons Tower - Map Chest').item is not None and state.world.get_location('Ganons Tower - Map Chest').item.name == 'Small Key (Ganons Tower)'))
|
set_rule(world.get_entrance('Ganons Tower (Map Room)'), lambda state: state.has('Small Key (Ganons Tower)', 4) or (state.world.get_location('Ganons Tower - Map Chest').item is not None and state.world.get_location('Ganons Tower - Map Chest').item.name == 'Big Key (Ganons Tower)' and state.has('Small Key (Ganons Tower)', 3)) or (state.world.get_location('Ganons Tower - Map Chest').item is not None and state.world.get_location('Ganons Tower - Map Chest').item.name == 'Small Key (Ganons Tower)'))
|
||||||
else:
|
else:
|
||||||
set_rule(world.get_entrance('Ganons Tower (Map Room)'), lambda state: state.has('Small Key (Ganons Tower)', 3) or (state.world.get_location('Ganons Tower - Map Chest').item is not None and state.world.get_location('Ganons Tower - Map Chest').item.name == 'Small Key (Ganons Tower)'))
|
set_rule(world.get_entrance('Ganons Tower (Map Room)'), lambda state: state.has('Small Key (Ganons Tower)', 3) or (state.world.get_location('Ganons Tower - Map Chest').item is not None and state.world.get_location('Ganons Tower - Map Chest').item.name == 'Small Key (Ganons Tower)'))
|
||||||
|
|
||||||
# It is possible to need more than 2 keys to get through this entance if you spend keys elsewhere We reflect this in the chest requirements.
|
# It is possible to need more than 2 keys to get through this entance if you spend keys elsewhere We reflect this in the chest requirements.
|
||||||
# However we need to leave these at the lower values derive that with 3 keys it is always possible to reach Bob and Ice Armos.
|
# However we need to leave these at the lower values derive that with 3 keys it is always possible to reach Bob and Ice Armos.
|
||||||
set_rule(world.get_entrance('Ganons Tower (Double Switch Room)'), lambda state: state.has('Small Key (Ganons Tower)', 2))
|
set_rule(world.get_entrance('Ganons Tower (Double Switch Room)'), lambda state: state.has('Small Key (Ganons Tower)', 2))
|
||||||
# It is possible to need more than 3 keys ....
|
# It is possible to need more than 3 keys ....
|
||||||
set_rule(world.get_entrance('Ganons Tower (Firesnake Room)'), lambda state: state.has('Small Key (Ganons Tower)', 3))
|
set_rule(world.get_entrance('Ganons Tower (Firesnake Room)'), lambda state: state.has('Small Key (Ganons Tower)', 3))
|
||||||
|
|
||||||
#The actual requirements for these rooms to avoid key-lock
|
#The actual requirements for these rooms to avoid key-lock
|
||||||
set_rule(world.get_location('Ganons Tower - Firesnake Room'), lambda state: state.has('Small Key (Ganons Tower)', 3) or (item_in_locations(state, 'Big Key (Ganons Tower)', randomizer_room_chests) and state.has('Small Key (Ganons Tower)', 2)))
|
set_rule(world.get_location('Ganons Tower - Firesnake Room'), lambda state: state.has('Small Key (Ganons Tower)', 3) or (item_in_locations(state, 'Big Key (Ganons Tower)', randomizer_room_chests) and state.has('Small Key (Ganons Tower)', 2)))
|
||||||
for location in randomizer_room_chests:
|
for location in randomizer_room_chests:
|
||||||
set_rule(world.get_location(location), lambda state: state.has('Small Key (Ganons Tower)', 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', randomizer_room_chests) and state.has('Small Key (Ganons Tower)', 3)))
|
set_rule(world.get_location(location), lambda state: state.has('Small Key (Ganons Tower)', 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', randomizer_room_chests) and state.has('Small Key (Ganons Tower)', 3)))
|
||||||
|
|
||||||
# Once again it is possible to need more than 3 keys...
|
# Once again it is possible to need more than 3 keys...
|
||||||
set_rule(world.get_entrance('Ganons Tower (Tile Room) Key Door'), lambda state: state.has('Small Key (Ganons Tower)', 3) and state.has('Fire Rod'))
|
set_rule(world.get_entrance('Ganons Tower (Tile Room) Key Door'), lambda state: state.has('Small Key (Ganons Tower)', 3) and state.has('Fire Rod'))
|
||||||
# Actual requirements
|
# Actual requirements
|
||||||
for location in compass_room_chests:
|
for location in compass_room_chests:
|
||||||
set_rule(world.get_location(location), lambda state: state.has('Fire Rod') and (state.has('Small Key (Ganons Tower)', 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', compass_room_chests) and state.has('Small Key (Ganons Tower)', 3))))
|
set_rule(world.get_location(location), lambda state: state.has('Fire Rod') and (state.has('Small Key (Ganons Tower)', 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', compass_room_chests) and state.has('Small Key (Ganons Tower)', 3))))
|
||||||
|
|
||||||
set_rule(world.get_location('Ganons Tower - Big Chest'), lambda state: state.has('Big Key (Ganons Tower)'))
|
set_rule(world.get_location('Ganons Tower - Big Chest'), lambda state: state.has('Big Key (Ganons Tower)'))
|
||||||
set_rule(world.get_location('Ganons Tower - Big Key Room - Left'), lambda state: state.has('Bow') or state.has_blunt_weapon())
|
set_rule(world.get_location('Ganons Tower - Big Key Room - Left'), lambda state: state.has('Bow') or state.has_blunt_weapon())
|
||||||
set_rule(world.get_location('Ganons Tower - Big Key Chest'), lambda state: state.has('Bow') or state.has_blunt_weapon())
|
set_rule(world.get_location('Ganons Tower - Big Key Chest'), lambda state: state.has('Bow') or state.has_blunt_weapon())
|
||||||
|
@ -429,7 +429,7 @@ def open_rules(world):
|
||||||
# softlock protection as you can reach the sewers small key door with a guard drop key
|
# softlock protection as you can reach the sewers small key door with a guard drop key
|
||||||
forbid_item(world.get_location('Hyrule Castle - Boomerang Chest'), 'Small Key (Escape)')
|
forbid_item(world.get_location('Hyrule Castle - Boomerang Chest'), 'Small Key (Escape)')
|
||||||
forbid_item(world.get_location('Hyrule Castle - Zelda\'s Chest'), 'Small Key (Escape)')
|
forbid_item(world.get_location('Hyrule Castle - Zelda\'s Chest'), 'Small Key (Escape)')
|
||||||
|
|
||||||
# to prevent key-lock in keysanity we need to prevent these chests from having an item that
|
# to prevent key-lock in keysanity we need to prevent these chests from having an item that
|
||||||
# blocks the small key
|
# blocks the small key
|
||||||
if (world.keysanity):
|
if (world.keysanity):
|
||||||
|
@ -443,7 +443,7 @@ def swordless_rules(world):
|
||||||
# should there ever be fixes that apply to open mode but not swordless, this
|
# should there ever be fixes that apply to open mode but not swordless, this
|
||||||
# can be revisited.
|
# can be revisited.
|
||||||
open_rules(world)
|
open_rules(world)
|
||||||
|
|
||||||
set_rule(world.get_entrance('Agahnims Tower'), lambda state: state.has('Cape') or state.has('Hammer') or state.has('Beat Agahnim 1')) # barrier gets removed after killing agahnim, relevant for entrance shuffle
|
set_rule(world.get_entrance('Agahnims Tower'), lambda state: state.has('Cape') or state.has('Hammer') or state.has('Beat Agahnim 1')) # barrier gets removed after killing agahnim, relevant for entrance shuffle
|
||||||
set_rule(world.get_entrance('Agahnim 1'), lambda state: state.has('Hammer') or state.has('Bug Catching Net') and state.has('Small Key (Agahnims Tower)', 2))
|
set_rule(world.get_entrance('Agahnim 1'), lambda state: state.has('Hammer') or state.has('Bug Catching Net') and state.has('Small Key (Agahnims Tower)', 2))
|
||||||
set_rule(world.get_location('Ether Tablet'), lambda state: state.has('Book of Mudora') and state.has('Hammer'))
|
set_rule(world.get_location('Ether Tablet'), lambda state: state.has('Book of Mudora') and state.has('Hammer'))
|
||||||
|
@ -479,7 +479,7 @@ def set_trock_key_rules(world):
|
||||||
|
|
||||||
# if we have backdoor access we can waste a key on the trinexx door, then have no lamp to reverse traverse the maze room. We simply require an additional key just to be super safe then. The backdoor access to the chest is otherwise free
|
# if we have backdoor access we can waste a key on the trinexx door, then have no lamp to reverse traverse the maze room. We simply require an additional key just to be super safe then. The backdoor access to the chest is otherwise free
|
||||||
if not can_reach_back:
|
if not can_reach_back:
|
||||||
set_rule(world.get_entrance('Turtle Rock Pokey Room'), lambda state: state.has('Small Key (Turtle Rock)', 1))
|
set_rule(world.get_entrance('Turtle Rock Pokey Room'), lambda state: state.has('Small Key (Turtle Rock)', 1))
|
||||||
else:
|
else:
|
||||||
set_rule(world.get_entrance('Turtle Rock Pokey Room'), lambda state: state.has('Small Key (Turtle Rock)', 2))
|
set_rule(world.get_entrance('Turtle Rock Pokey Room'), lambda state: state.has('Small Key (Turtle Rock)', 2))
|
||||||
|
|
||||||
|
@ -496,8 +496,8 @@ def set_trock_key_rules(world):
|
||||||
# however in keysanity being able to reach all other chests while only having three keys does not imply this contains
|
# however in keysanity being able to reach all other chests while only having three keys does not imply this contains
|
||||||
# a key, so we again need all four keys unless it contains the big key
|
# a key, so we again need all four keys unless it contains the big key
|
||||||
if can_reach_back:
|
if can_reach_back:
|
||||||
set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has('Small Key (Turtle Rock)', 4) or (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Small Key (Turtle Rock)'])))
|
set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has('Small Key (Turtle Rock)', 4) or (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Small Key (Turtle Rock)'])))
|
||||||
elif world.keysanity:
|
elif world.keysanity:
|
||||||
set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has('Small Key (Turtle Rock)', 2) if (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Big Key (Turtle Rock)'])) else state.has('Small Key (Turtle Rock)', 4) or (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Small Key (Turtle Rock)'])))
|
set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has('Small Key (Turtle Rock)', 2) if (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Big Key (Turtle Rock)'])) else state.has('Small Key (Turtle Rock)', 4) or (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Small Key (Turtle Rock)'])))
|
||||||
else:
|
else:
|
||||||
set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has('Small Key (Turtle Rock)', 2) if (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Big Key (Turtle Rock)'])) else state.has('Small Key (Turtle Rock)', 3) or (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Small Key (Turtle Rock)'])))
|
set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has('Small Key (Turtle Rock)', 2) if (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Big Key (Turtle Rock)'])) else state.has('Small Key (Turtle Rock)', 3) or (state.world.get_location('Turtle Rock - Big Key Chest').item is not None and (state.world.get_location('Turtle Rock - Big Key Chest').item.name in ['Small Key (Turtle Rock)'])))
|
||||||
|
|
10
Text.py
10
Text.py
|
@ -141,11 +141,11 @@ class Credits(object):
|
||||||
|
|
||||||
class CreditLine(object):
|
class CreditLine(object):
|
||||||
"""Base class of credit lines"""
|
"""Base class of credit lines"""
|
||||||
|
|
||||||
def __init__(self, text, align='center'):
|
def __init__(self, text, align='center'):
|
||||||
self.text = text
|
self.text = text
|
||||||
self.align = align
|
self.align = align
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def x(self):
|
def x(self):
|
||||||
x = 0
|
x = 0
|
||||||
|
@ -157,7 +157,7 @@ class CreditLine(object):
|
||||||
x = (32 - len(self.text)) // 2
|
x = (32 - len(self.text)) // 2
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
class SceneCreditLine(CreditLine):
|
class SceneCreditLine(CreditLine):
|
||||||
"""Base class for credit lines for the scene portion of the credits"""
|
"""Base class for credit lines for the scene portion of the credits"""
|
||||||
def __init__(self, y, text, align='center'):
|
def __init__(self, y, text, align='center'):
|
||||||
|
@ -214,7 +214,7 @@ class SceneLargeCreditLine(SceneCreditLine):
|
||||||
buf += LargeCreditBottomMapper.convert(self.text)
|
buf += LargeCreditBottomMapper.convert(self.text)
|
||||||
return buf
|
return buf
|
||||||
|
|
||||||
|
|
||||||
def string_to_alttp_text(s, maxbytes=256):
|
def string_to_alttp_text(s, maxbytes=256):
|
||||||
lines = s.upper().split('\n')
|
lines = s.upper().split('\n')
|
||||||
outbuf = bytearray()
|
outbuf = bytearray()
|
||||||
|
@ -489,7 +489,7 @@ class GreenCreditMapper(TextMapper):
|
||||||
char_map = {' ': 0x9F,
|
char_map = {' ': 0x9F,
|
||||||
'.': 0x52}
|
'.': 0x52}
|
||||||
alpha_offset = -0x29
|
alpha_offset = -0x29
|
||||||
|
|
||||||
class RedCreditMapper(TextMapper):
|
class RedCreditMapper(TextMapper):
|
||||||
char_map = {' ': 0x9F} #fixme
|
char_map = {' ': 0x9F} #fixme
|
||||||
alpha_offset= -0x61
|
alpha_offset= -0x61
|
||||||
|
|
Loading…
Reference in New Issue