Skip to content

Commit

Permalink
ER 0.6.0 Release Version
Browse files Browse the repository at this point in the history
ER 0.6.0 Release Version
  • Loading branch information
AmazingAmpharos authored Mar 3, 2018
2 parents 9f76ab0 + 58b5c6f commit 67fe623
Show file tree
Hide file tree
Showing 17 changed files with 2,565 additions and 884 deletions.
2 changes: 2 additions & 0 deletions Adjuster.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ def main():
Select the rate at which the heart beep sound is played at
low health. (default: %(default)s)
''')
parser.add_argument('--heartcolor', default='red', const='red', nargs='?', choices=['red', 'blue', 'green', 'yellow'],
help='Select the color of Link\'s heart meter. (default: %(default)s)')
parser.add_argument('--sprite', help='''\
Path to a sprite sheet to use for Link. Needs to be in
binary format and have a length of 0x7000 (28672) bytes,
Expand Down
2 changes: 1 addition & 1 deletion AdjusterMain.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def adjust(args):
else:
raise RuntimeError('Provided Rom is not a valid Link to the Past Randomizer Rom. Please provide one for adjusting.')

apply_rom_settings(rom, args.heartbeep, args.quickswap, args.fastmenu, args.disablemusic, sprite)
apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.fastmenu, args.disablemusic, sprite)

rom.write_to_file(output_path('%s.sfc' % outfilebase))

Expand Down
73 changes: 54 additions & 19 deletions BaseClasses.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import copy
from enum import Enum, unique
import logging
import json
from collections import OrderedDict


class World(object):

def __init__(self, shuffle, logic, mode, difficulty, timer, progressive, goal, algorithm, place_dungeon_items, check_beatable_only, shuffle_ganon, quickswap, fastmenu, disable_music, keysanity):
def __init__(self, shuffle, logic, mode, difficulty, timer, progressive, goal, algorithm, place_dungeon_items, check_beatable_only, shuffle_ganon, quickswap, fastmenu, disable_music, keysanity, custom, customitemarray):
self.shuffle = shuffle
self.logic = logic
self.mode = mode
Expand All @@ -30,13 +31,16 @@ def __init__(self, shuffle, logic, mode, difficulty, timer, progressive, goal, a
self.place_dungeon_items = place_dungeon_items # configurable in future
self.shuffle_bonk_prizes = False
self.swamp_patch_required = False
self.powder_patch_required = False
self.ganon_at_pyramid = True
self.ganonstower_vanilla = True
self.sewer_light_cone = mode == 'standard'
self.light_world_light_cone = False
self.dark_world_light_cone = False
self.treasure_hunt_count = 0
self.treasure_hunt_icon = 'Triforce Piece'
self.clock_mode = 'off'
self.rupoor_cost = 10
self.aga_randomness = True
self.lock_aga_door_in_escape = False
self.fix_trock_doors = self.shuffle != 'vanilla'
Expand All @@ -52,9 +56,13 @@ def __init__(self, shuffle, logic, mode, difficulty, timer, progressive, goal, a
self.fastmenu = fastmenu
self.disable_music = disable_music
self.keysanity = keysanity
self.custom = custom
self.customitemarray = customitemarray
self.can_take_damage = True
self.difficulty_requirements = None
self.fix_fake_world = True
self.spoiler = Spoiler(self)
self.lamps_needed_for_dark_rooms = 1

def intialize_regions(self):
for region in self.regions:
Expand Down Expand Up @@ -247,19 +255,31 @@ def can_beat_game(self, starting_state=None):

@property
def option_identifier(self):
logic = 0 if self.logic == 'noglitches' else 1
mode = ['standard', 'open', 'swordless'].index(self.mode)
dungeonitems = 0 if self.place_dungeon_items else 1
goal = ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'].index(self.goal)
shuffle = ['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'].index(self.shuffle)
difficulty = ['easy', 'normal', 'hard', 'expert', 'insane'].index(self.difficulty)
timer = ['none', 'display', 'timed', 'timed-ohko', 'timed-countdown', 'ohko'].index(self.timer)
progressive = ['on', 'off', 'random'].index(self.progressive)
algorithm = ['freshness', 'flood', 'vt21', 'vt22', 'vt25', 'vt26', 'balanced'].index(self.algorithm)
beatableonly = 1 if self.check_beatable_only else 0
shuffleganon = 1 if self.shuffle_ganon else 0
keysanity = 1 if self.keysanity else 0
return logic | (beatableonly << 1) | (dungeonitems << 2) | (shuffleganon << 3) | (goal << 4) | (shuffle << 7) | (difficulty << 11) | (algorithm << 13) | (mode << 16) | (keysanity << 18) | (timer << 19) | (progressive << 21)
id_value = 0
id_value_max = 1

def markbool(value):
nonlocal id_value, id_value_max
id_value += id_value_max * bool(value)
id_value_max *= 2
def marksequence(options, value):
nonlocal id_value, id_value_max
id_value += id_value_max * options.index(value)
id_value_max *= len(options)
markbool(self.logic == 'noglitches')
marksequence(['standard', 'open', 'swordless'], self.mode)
markbool(self.place_dungeon_items)
marksequence(['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'], self.goal)
marksequence(['vanilla', 'simple', 'restricted', 'full', 'crossed', 'insanity', 'restricted_legacy', 'full_legacy', 'madness_legacy', 'insanity_legacy', 'dungeonsfull', 'dungeonssimple'], self.shuffle)
marksequence(['easy', 'normal', 'hard', 'expert', 'insane'], self.difficulty)
marksequence(['none', 'display', 'timed', 'timed-ohko', 'timed-countdown', 'ohko'], self.timer)
marksequence(['on', 'off', 'random'], self.progressive)
marksequence(['freshness', 'flood', 'vt21', 'vt22', 'vt25', 'vt26', 'balanced'], self.algorithm)
markbool(self.check_beatable_only)
markbool(self.shuffle_ganon)
markbool(self.keysanity)
assert id_value_max <= 0xFFFFFFFF
return id_value


class CollectionState(object):
Expand Down Expand Up @@ -383,17 +403,17 @@ def heart_count(self):
def can_lift_heavy_rocks(self):
return self.has('Titans Mitts')

def can_extend_magic(self, smallmagic=8): #This reflects the total magic Link has, not the total extra he has.
def can_extend_magic(self, smallmagic=8, fullrefill=False): #This reflects the total magic Link has, not the total extra he has.
basemagic = 8
if self.has('Quarter Magic'):
basemagic = 32
elif self.has('Half Magic'):
basemagic = 16
if self.world.difficulty == 'hard':
if self.world.difficulty == 'hard' and not fullrefill:
basemagic = basemagic + int(basemagic * 0.5 * self.bottle_count())
elif self.world.difficulty == 'expert':
elif self.world.difficulty == 'expert' and not fullrefill:
basemagic = basemagic + int(basemagic * 0.25 * self.bottle_count())
elif self.world.difficulty == 'insane':
elif self.world.difficulty == 'insane' and not fullrefill:
basemagic = basemagic
else:
basemagic = basemagic + basemagic * self.bottle_count()
Expand Down Expand Up @@ -532,17 +552,32 @@ def __getattr__(self, item):

raise RuntimeError('Cannot parse %s.' % item)

@unique
class RegionType(Enum):
LightWorld = 1
DarkWorld = 2
Cave = 3 # Also includes Houses
Dungeon = 4

@property
def is_indoors(self):
"""Shorthand for checking if Cave or Dungeon"""
return self in (RegionType.Cave, RegionType.Dungeon)



class Region(object):

def __init__(self, name):
def __init__(self, name, type):
self.name = name
self.type = type
self.entrances = []
self.exits = []
self.locations = []
self.dungeon = None
self.world = None
self.is_light_world = False # will be set aftermaking connections.
self.is_dark_world = False
self.spot_type = 'Region'
self.hint_text = 'Hyrule'
self.recursion_count = 0
Expand Down
4 changes: 2 additions & 2 deletions Dungeons.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ def make_dungeon(name, dungeon_regions, big_key, small_keys, dungeon_items):
world.get_region(region).dungeon = dungeon
return dungeon

ES = make_dungeon('Hyrule Castle', ['Hyrule Castle', 'Sewers', 'Sewers (Dark)', 'Sanctuary'], None, [ItemFactory('Small Key (Escape)')], [ItemFactory('Map (Escape)')])
ES = make_dungeon('Hyrule Castle', ['Hyrule Castle', 'Sewers', 'Sewer Drop', 'Sewers (Dark)', 'Sanctuary'], None, [ItemFactory('Small Key (Escape)')], [ItemFactory('Map (Escape)')])
EP = make_dungeon('Eastern Palace', ['Eastern Palace'], ItemFactory('Big Key (Eastern Palace)'), [], ItemFactory(['Map (Eastern Palace)', 'Compass (Eastern Palace)']))
DP = make_dungeon('Desert Palace', ['Desert Palace North', 'Desert Palace Main', 'Desert Palace East'], ItemFactory('Big Key (Desert Palace)'), [ItemFactory('Small Key (Desert Palace)')], ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)']))
DP = make_dungeon('Desert Palace', ['Desert Palace North', 'Desert Palace Main (Inner)', 'Desert Palace Main (Outer)', 'Desert Palace East'], ItemFactory('Big Key (Desert Palace)'), [ItemFactory('Small Key (Desert Palace)')], ItemFactory(['Map (Desert Palace)', 'Compass (Desert Palace)']))
ToH = make_dungeon('Tower of Hera', ['Tower of Hera (Bottom)', 'Tower of Hera (Basement)', 'Tower of Hera (Top)'], ItemFactory('Big Key (Tower of Hera)'), [ItemFactory('Small Key (Tower of Hera)')], ItemFactory(['Map (Tower of Hera)', 'Compass (Tower of Hera)']))
AT = make_dungeon('Agahnims Tower', ['Agahnims Tower', 'Agahnim 1'], None, ItemFactory(['Small Key (Agahnims Tower)'] * 2), [])
PoD = make_dungeon('Palace of Darkness', ['Palace of Darkness (Entrance)', 'Palace of Darkness (Center)', 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness (Bonk Section)', 'Palace of Darkness (North)', 'Palace of Darkness (Maze)', 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness (Final Section)'], ItemFactory('Big Key (Palace of Darkness)'), ItemFactory(['Small Key (Palace of Darkness)'] * 6), ItemFactory(['Map (Palace of Darkness)', 'Compass (Palace of Darkness)']))
Expand Down
40 changes: 24 additions & 16 deletions EntranceRandomizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,25 +120,27 @@ def start():
slightly biased to placing progression items with
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', 'crossed', 'insanity', 'restricted_legacy', 'full_legacy', 'madness_legacy', 'insanity_legacy', 'dungeonsfull', 'dungeonssimple'],
help='''\
Select Entrance Shuffling Algorithm. (default: %(default)s)
Full: Mix cave and dungeon entrances freely.
Full: Mix cave and dungeon entrances freely while limiting
multi-entrance caves to one world.
Simple: Shuffle Dungeon Entrances/Exits between each other
and keep all 4-entrance dungeons confined to one
location. All caves outside of death mountain are
shuffled in pairs.
shuffled in pairs and matched by original type.
Restricted: Use Dungeons shuffling from Simple but freely
connect remaining entrances.
Madness: Decouple entrances and exits from each other and
shuffle them freely, only ensuring that no fake
Light/Dark World happens and all locations are
reachable.
Insanity: Madness without the world restrictions. Mirror and
Pearl are provided early to ensure Filling algorithm
works properly. Deal with Fake LW/DW at your
discretion.
Experimental.
Crossed: Mix cave and dungeon entrances freely while allowing
caves to cross between worlds.
Insanity: Decouple entrances and exits from each other and
shuffle them freely. Caves that used to be single
entrance will still exit to the same location from
which they are entered.
Vanilla: All entrances are in the same locations they were
in the base game.
Legacy shuffles preserve behavior from older versions of the
entrance randomizer including significant technical limitations.
The dungeon variants only mix up dungeons and keep the rest of
the overworld vanilla.
''')
Expand All @@ -163,6 +165,8 @@ def start():
Keys (and other dungeon items) are no longer restricted to
their dungeons, but can be anywhere
''', action='store_true')
parser.add_argument('--custom', default=False, help='Not supported.')
parser.add_argument('--customitemarray', default=False, help='Not supported.')
parser.add_argument('--nodungeonitems', help='''\
Remove Maps and Compasses from Itempool, replacing them by
empty slots.
Expand All @@ -172,15 +176,19 @@ def start():
ensure all locations are reachable. This only has an effect
on the restrictive algorithm currently.
''', action='store_true')
parser.add_argument('--shuffleganon', help='''\
If set, include the Pyramid Hole and Ganon's Tower in the
entrance shuffle pool.
''', action='store_true')
# included for backwards compatibility
parser.add_argument('--shuffleganon', help=argparse.SUPPRESS, action='store_true', default=True)
parser.add_argument('--no-shuffleganon', help='''\
If set, the Pyramid Hole and Ganon's Tower are not
included entrance shuffle pool.
''', action='store_false', dest='shuffleganon')
parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['normal', 'half', 'quarter', 'off'],
help='''\
Select the rate at which the heart beep sound is played at
low health. (default: %(default)s)
''')
parser.add_argument('--heartcolor', default='red', const='red', nargs='?', choices=['red', 'blue', 'green', 'yellow'],
help='Select the color of Link\'s heart meter. (default: %(default)s)')
parser.add_argument('--sprite', help='''\
Path to a sprite sheet to use for Link. Needs to be in
binary format and have a length of 0x7000 (28672) bytes,
Expand Down
Loading

0 comments on commit 67fe623

Please sign in to comment.