Partial implementation of many V31 features
Partial support for Progressive bow - Still needs to be added to item pool - Silver hint handling remains TBD even for VT Added weapons selection. - Vanilla needs to be implemented - Assured needs to be implemented - Inverted swordless is almost certainly messed up. - Swordless standard mode will likely softlock - Random weapon standard mode is currently treated as uncle assured Deleted removed difficulties - Remaining difficulties still need to be adjusted Added locked property to locations: - This is used for preplaced items etc so that multiworld balancing knows they cannot be moved. Made a few of the difficulty changes from V31, but not all. Added required text changes to handle crystals requirements - More changes will likely me made in future - Currently there is is no way to tell ganon requirement in Inverted mode
This commit is contained in:
parent
d4f1bb7091
commit
b0f4fa8cec
|
@ -12,3 +12,4 @@ README.html
|
|||
*multidata
|
||||
*multisave
|
||||
EnemizerCLI/
|
||||
.mypy_cache/
|
|
@ -8,11 +8,12 @@ from Utils import int16_as_bytes
|
|||
|
||||
class World(object):
|
||||
|
||||
def __init__(self, players, shuffle, logic, mode, difficulty, timer, progressive, goal, algorithm, place_dungeon_items, check_beatable_only, shuffle_ganon, quickswap, fastmenu, disable_music, keysanity, retro, custom, customitemarray, boss_shuffle, hints):
|
||||
def __init__(self, players, shuffle, logic, mode, swords, difficulty, timer, progressive, goal, algorithm, place_dungeon_items, check_beatable_only, shuffle_ganon, quickswap, fastmenu, disable_music, keysanity, retro, custom, customitemarray, boss_shuffle, hints):
|
||||
self.players = players
|
||||
self.shuffle = shuffle
|
||||
self.logic = logic
|
||||
self.mode = mode
|
||||
self.swords = swords
|
||||
self.difficulty = difficulty
|
||||
self.timer = timer
|
||||
self.progressive = progressive
|
||||
|
@ -161,6 +162,13 @@ class World(object):
|
|||
ret.prog_items.add(('Red Shield', item.player))
|
||||
elif self.difficulty_requirements.progressive_shield_limit >= 1:
|
||||
ret.prog_items.add(('Blue Shield', item.player))
|
||||
elif 'Bow' in item.name:
|
||||
if ret.has('Silver Arrows', item.player):
|
||||
pass
|
||||
elif ret.has('Bow', item.player):
|
||||
ret.prog_items.add(('Silver Arrows', item.player))
|
||||
else:
|
||||
ret.prog_items.add(('Bow', item.player))
|
||||
elif item.name.startswith('Bottle'):
|
||||
if ret.bottle_count(item.player) < self.difficulty_requirements.progressive_bottle_limit:
|
||||
ret.prog_items.add((item.name, item.player))
|
||||
|
@ -409,8 +417,6 @@ class CollectionState(object):
|
|||
basemagic = basemagic + int(basemagic * 0.5 * self.bottle_count(player))
|
||||
elif self.world.difficulty == 'expert' and not fullrefill:
|
||||
basemagic = basemagic + int(basemagic * 0.25 * self.bottle_count(player))
|
||||
elif self.world.difficulty == 'insane' and not fullrefill:
|
||||
basemagic = basemagic
|
||||
else:
|
||||
basemagic = basemagic + basemagic * self.bottle_count(player)
|
||||
return basemagic >= smallmagic
|
||||
|
@ -524,6 +530,15 @@ class CollectionState(object):
|
|||
elif self.world.difficulty_requirements.progressive_shield_limit >= 1:
|
||||
self.prog_items.add(('Blue Shield', item.player))
|
||||
changed = True
|
||||
elif 'Bow' in item.name:
|
||||
if self.has('Silver Arrows', item.player):
|
||||
pass
|
||||
elif self.has('Bow', item.player):
|
||||
self.prog_items.add(('Silver Arrows', item.player))
|
||||
changed = True
|
||||
else:
|
||||
self.prog_items.add(('Bow', item.player))
|
||||
changed = True
|
||||
elif item.name.startswith('Bottle'):
|
||||
if self.bottle_count(item.player) < self.world.difficulty_requirements.progressive_bottle_limit:
|
||||
self.prog_items.add((item.name, item.player))
|
||||
|
@ -560,6 +575,22 @@ class CollectionState(object):
|
|||
to_remove = 'Power Glove'
|
||||
else:
|
||||
to_remove = None
|
||||
elif 'Shield' in item.name:
|
||||
if self.has('Mirror Shield', item.player):
|
||||
to_remove = 'Mirror Shield'
|
||||
elif self.has('Red Shield', item.player):
|
||||
to_remove = 'Red Shield'
|
||||
elif self.has('Blue Shield', item.player):
|
||||
to_remove = 'Blue Shield'
|
||||
else:
|
||||
to_remove = 'None'
|
||||
elif 'Bow' in item.name:
|
||||
if self.has('Silver Arrows', item.player):
|
||||
to_remove = 'Silver Arrows'
|
||||
elif self.has('Bow', item.player):
|
||||
to_remove = 'Bow'
|
||||
else:
|
||||
to_remove = None
|
||||
|
||||
if to_remove is not None:
|
||||
try:
|
||||
|
@ -742,6 +773,7 @@ class Location(object):
|
|||
self.recursion_count = 0
|
||||
self.staleness_count = 0
|
||||
self.event = False
|
||||
self.locked = True
|
||||
self.always_allow = lambda item, state: False
|
||||
self.access_rule = lambda state: True
|
||||
self.item_rule = lambda item: True
|
||||
|
@ -975,6 +1007,7 @@ class Spoiler(object):
|
|||
'seed': self.world.seed,
|
||||
'logic': self.world.logic,
|
||||
'mode': self.world.mode,
|
||||
'swords': self.world.swords,
|
||||
'goal': self.world.goal,
|
||||
'shuffle': self.world.shuffle,
|
||||
'algorithm': self.world.algorithm,
|
||||
|
|
12
Bosses.py
12
Bosses.py
|
@ -72,18 +72,18 @@ def KholdstareDefeatRule(state, player):
|
|||
state.has('Fire Rod', player) or
|
||||
(
|
||||
state.has('Bombos', player) and
|
||||
# FIXME: the following only actually works for the vanilla location for swordless mode
|
||||
(state.has_sword(player) or state.world.mode == 'swordless')
|
||||
# FIXME: the following only actually works for the vanilla location for swordless
|
||||
(state.has_sword(player) or state.world.swords == 'swordless')
|
||||
)
|
||||
) and
|
||||
(
|
||||
state.has_blunt_weapon(player) or
|
||||
(state.has('Fire Rod', player) and state.can_extend_magic(player, 20)) or
|
||||
# FIXME: this actually only works for the vanilla location for swordless mode
|
||||
# FIXME: this actually only works for the vanilla location for swordless
|
||||
(
|
||||
state.has('Fire Rod', player) and
|
||||
state.has('Bombos', player) and
|
||||
state.world.mode == 'swordless' and
|
||||
state.world.swords == 'swordless' and
|
||||
state.can_extend_magic(player, 16)
|
||||
)
|
||||
)
|
||||
|
@ -116,7 +116,7 @@ boss_table = {
|
|||
}
|
||||
|
||||
def can_place_boss(world, boss, dungeon_name, level=None):
|
||||
if world.mode in ['swordless'] and boss == 'Kholdstare' and dungeon_name != 'Ice Palace':
|
||||
if world.swords in ['swordless'] and boss == 'Kholdstare' and dungeon_name != 'Ice Palace':
|
||||
return False
|
||||
|
||||
if dungeon_name == 'Ganons Tower' and level == 'top':
|
||||
|
@ -161,7 +161,7 @@ def place_bosses(world, player):
|
|||
|
||||
if world.boss_shuffle in ["basic", "normal"]:
|
||||
# temporary hack for swordless kholdstare:
|
||||
if world.mode == 'swordless':
|
||||
if world.swords == 'swordless':
|
||||
world.get_dungeon('Ice Palace', player).boss = BossFactory('Kholdstare', player)
|
||||
logging.getLogger('').debug('Placing boss Kholdstare at Ice Palace')
|
||||
boss_locations.remove(['Ice Palace', None])
|
||||
|
|
21
Dungeons.py
21
Dungeons.py
|
@ -46,11 +46,13 @@ def fill_dungeons(world):
|
|||
all_state_base = world.get_all_state()
|
||||
|
||||
for player in range(1, world.players + 1):
|
||||
pinball_room = world.get_location('Skull Woods - Pinball Room', player)
|
||||
if world.retro:
|
||||
world.push_item(world.get_location('Skull Woods - Pinball Room', player), ItemFactory('Small Key (Universal)', player), False)
|
||||
world.push_item(pinball_room, ItemFactory('Small Key (Universal)', player), False)
|
||||
else:
|
||||
world.push_item(world.get_location('Skull Woods - Pinball Room', player), ItemFactory('Small Key (Skull Woods)', player), False)
|
||||
world.get_location('Skull Woods - Pinball Room', player).event = True
|
||||
world.push_item(pinball_room, ItemFactory('Small Key (Skull Woods)', player), False)
|
||||
pinball_room.event = True
|
||||
pinball_room.locked = True
|
||||
|
||||
dungeons = [(list(dungeon.regions), dungeon.big_key, list(dungeon.small_keys), list(dungeon.dungeon_items)) for dungeon in world.dungeons]
|
||||
|
||||
|
@ -77,6 +79,7 @@ def fill_dungeons(world):
|
|||
|
||||
world.push_item(bk_location, big_key, False)
|
||||
bk_location.event = True
|
||||
bk_location.locked = True
|
||||
dungeon_locations.remove(bk_location)
|
||||
big_key = None
|
||||
|
||||
|
@ -102,6 +105,7 @@ def fill_dungeons(world):
|
|||
|
||||
world.push_item(sk_location, small_key, False)
|
||||
sk_location.event = True
|
||||
sk_location.locked = True
|
||||
dungeon_locations.remove(sk_location)
|
||||
|
||||
if small_keys:
|
||||
|
@ -122,13 +126,14 @@ def fill_dungeons_restrictive(world, shuffled_locations):
|
|||
all_state_base = world.get_all_state()
|
||||
|
||||
for player in range(1, world.players + 1):
|
||||
skull_woods_big_chest = world.get_location('Skull Woods - Pinball Room', player)
|
||||
pinball_room = world.get_location('Skull Woods - Pinball Room', player)
|
||||
if world.retro:
|
||||
world.push_item(skull_woods_big_chest, ItemFactory('Small Key (Universal)', player), False)
|
||||
world.push_item(pinball_room, ItemFactory('Small Key (Universal)', player), False)
|
||||
else:
|
||||
world.push_item(skull_woods_big_chest, ItemFactory('Small Key (Skull Woods)', player), False)
|
||||
skull_woods_big_chest.event = True
|
||||
shuffled_locations.remove(skull_woods_big_chest)
|
||||
world.push_item(pinball_room, ItemFactory('Small Key (Skull Woods)', player), False)
|
||||
pinball_room.event = True
|
||||
pinball_room.locked = True
|
||||
shuffled_locations.remove(pinball_room)
|
||||
|
||||
if world.keysanity:
|
||||
#in keysanity dungeon items are distributed as part of the normal item pool
|
||||
|
|
|
@ -28,7 +28,7 @@ def start():
|
|||
No Logic: Distribute items without regard for
|
||||
item requirements.
|
||||
''')
|
||||
parser.add_argument('--mode', default='open', const='open', nargs='?', choices=['standard', 'open', 'swordless', 'inverted'],
|
||||
parser.add_argument('--mode', default='open', const='open', nargs='?', choices=['standard', 'open', 'inverted'],
|
||||
help='''\
|
||||
Select game mode. (default: %(default)s)
|
||||
Open: World starts with Zelda rescued.
|
||||
|
@ -36,17 +36,25 @@ def start():
|
|||
but may lead to weird rain state issues if you exit
|
||||
through the Hyrule Castle side exits before rescuing
|
||||
Zelda in a full shuffle.
|
||||
Swordless: Like Open, but with no swords. Curtains in
|
||||
Skull Woods and Agahnims Tower are removed,
|
||||
Agahnim\'s Tower barrier can be destroyed with
|
||||
hammer. Misery Mire and Turtle Rock can be opened
|
||||
without a sword. Hammer damages Ganon. Ether and
|
||||
Bombos Tablet can be activated with Hammer (and Book).
|
||||
Inverted: Starting locations are Dark Sanctuary in West Dark
|
||||
World or at Link's House, which is shuffled freely.
|
||||
Requires the moon pearl to be Link in the Light World
|
||||
instead of a bunny.
|
||||
''')
|
||||
parser.add_argument('--swords', default='random', const='random', nargs='?', choices= ['random', 'assured', 'swordless', 'vanilla'],
|
||||
help='''\
|
||||
Select sword placement. (default: %(default)s)
|
||||
Random: All swords placed randomly.
|
||||
Assured: Start game with a sword already.
|
||||
Swordless: No swords. Curtains in Skull Woods and Agahnim\'s
|
||||
Tower are removed, Agahnim\'s Tower barrier can be
|
||||
destroyed with hammer. Misery Mire and Turtle Rock
|
||||
can be opened without a sword. Hammer damages Ganon.
|
||||
Ether and Bombos Tablet can be activated with Hammer
|
||||
(and Book). Bombos pads have been added in Ice
|
||||
Palace, to allow for an alternative to firerod.
|
||||
Vanilla: Swords are in vanilla locations.
|
||||
''')
|
||||
parser.add_argument('--goal', default='ganon', const='ganon', nargs='?', choices=['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'],
|
||||
help='''\
|
||||
Select completion goal. (default: %(default)s)
|
||||
|
@ -59,14 +67,12 @@ def start():
|
|||
Triforce Hunt: Places 30 Triforce Pieces in the world, collect
|
||||
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=['normal', 'hard', 'expert'],
|
||||
help='''\
|
||||
Select game difficulty. Affects available itempool. (default: %(default)s)
|
||||
Easy: An easy setting with extra equipment.
|
||||
Normal: Normal difficulty.
|
||||
Hard: A harder setting with less equipment and reduced health.
|
||||
Expert: A harder yet setting with minimum equipment and 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'],
|
||||
help='''\
|
||||
|
|
5
Fill.py
5
Fill.py
|
@ -374,6 +374,9 @@ def balance_multiworld_progression(world):
|
|||
|
||||
reducing_state.sweep_for_events(locations=locations_to_test)
|
||||
|
||||
if testing.locked:
|
||||
continue
|
||||
|
||||
if world.has_beaten_game(balancing_state):
|
||||
if not world.has_beaten_game(reducing_state):
|
||||
items_to_replace.append(testing)
|
||||
|
@ -383,7 +386,7 @@ def balance_multiworld_progression(world):
|
|||
items_to_replace.append(testing)
|
||||
|
||||
replaced_items = False
|
||||
locations_for_replacing = [l for l in checked_locations if not l.event]
|
||||
locations_for_replacing = [l for l in checked_locations if not l.event and not l.locked]
|
||||
while locations_for_replacing and items_to_replace:
|
||||
new_location = locations_for_replacing.pop()
|
||||
old_location = items_to_replace.pop()
|
||||
|
|
4
Gui.py
4
Gui.py
|
@ -145,7 +145,7 @@ def guiMain(args=None):
|
|||
modeFrame = Frame(drowDownFrame)
|
||||
modeVar = StringVar()
|
||||
modeVar.set('open')
|
||||
modeOptionMenu = OptionMenu(modeFrame, modeVar, 'standard', 'open', 'swordless', 'inverted')
|
||||
modeOptionMenu = OptionMenu(modeFrame, modeVar, 'standard', 'open', 'inverted')
|
||||
modeOptionMenu.pack(side=RIGHT)
|
||||
modeLabel = Label(modeFrame, text='Game Mode')
|
||||
modeLabel.pack(side=LEFT)
|
||||
|
@ -169,7 +169,7 @@ def guiMain(args=None):
|
|||
difficultyFrame = Frame(drowDownFrame)
|
||||
difficultyVar = StringVar()
|
||||
difficultyVar.set('normal')
|
||||
difficultyOptionMenu = OptionMenu(difficultyFrame, difficultyVar, 'easy', 'normal', 'hard', 'expert', 'insane')
|
||||
difficultyOptionMenu = OptionMenu(difficultyFrame, difficultyVar, 'normal', 'hard', 'expert')
|
||||
difficultyOptionMenu.pack(side=RIGHT)
|
||||
difficultyLabel = Label(difficultyFrame, text='Game difficulty')
|
||||
difficultyLabel.pack(side=LEFT)
|
||||
|
|
130
ItemList.py
130
ItemList.py
|
@ -29,17 +29,6 @@ normalthird10extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 3 + ['Arrows (10)',
|
|||
normalfourth5extra = ['Arrows (10)'] * 2 + ['Rupees (20)'] * 2 + ['Rupees (5)']
|
||||
normalfinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2
|
||||
|
||||
|
||||
easybaseitems = (['Sanctuary Heart Container'] + ['Rupees (300)'] * 4 + ['Magic Upgrade (1/2)'] * 2 + ['Lamp'] * 2 + ['Silver Arrows'] * 2 +
|
||||
['Boss Heart Container'] * 10 + ['Piece of Heart'] * 12)
|
||||
easyextra = ['Piece of Heart'] * 12 + ['Rupees (300)']
|
||||
easylimitedextra = ['Boss Heart Container'] * 3 # collapsing down the 12 pieces of heart
|
||||
easyfirst15extra = ['Rupees (100)'] + ['Arrows (10)'] * 7 + ['Bombs (3)'] * 7
|
||||
easysecond10extra = ['Bombs (3)'] * 7 + ['Rupee (1)', 'Rupees (50)', 'Bombs (10)']
|
||||
easythird5extra = ['Rupees (50)'] * 2 + ['Bombs (3)'] * 2 + ['Arrows (10)']
|
||||
easyfinal25extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 14 + ['Rupee (1)'] + ['Arrows (10)'] * 4 + ['Rupees (5)'] * 2
|
||||
easytimedotherextra = ['Red Clock'] * 5
|
||||
|
||||
hardbaseitems = ['Silver Arrows', 'Single Arrow', 'Bombs (10)'] + ['Rupees (300)'] * 4 + ['Boss Heart Container'] * 6 + ['Piece of Heart'] * 20 + ['Rupees (5)'] * 7 + ['Bombs (3)'] * 4
|
||||
hardfirst20extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Bombs (3)'] * 5 + ['Rupees (5)'] * 10 + ['Arrows (10)', 'Rupee (1)']
|
||||
hardsecond10extra = ['Rupees (5)'] * 5 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)']
|
||||
|
@ -55,13 +44,6 @@ expertthird10extra = ['Rupees (50)'] * 4 + ['Rupees (5)'] * 2 + ['Arrows (10)']
|
|||
expertfourth5extra = ['Rupees (5)'] * 5
|
||||
expertfinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2
|
||||
|
||||
insanebaseitems = ['Rupees (300)'] * 4 + ['Single Arrow', 'Bombs (10)', 'Rupee (1)'] + ['Rupees (5)'] * 24 + ['Bombs (3)'] * 9 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupees (20)'] * 5
|
||||
insanefirst15extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Rupees (5)'] * 12
|
||||
insanesecond15extra = ['Rupees (5)'] * 10 + ['Rupees (20)'] * 5
|
||||
insanethird10extra = ['Rupees (50)'] * 4 + ['Rupees (5)'] * 2 + ['Arrows (10)'] * 3 + ['Rupee (1)']
|
||||
insanefourth5extra = ['Rupees (5)'] * 5
|
||||
insanefinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2
|
||||
|
||||
Difficulty = namedtuple('Difficulty',
|
||||
['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield',
|
||||
'basicshield', 'progressivearmor', 'basicarmor', 'swordless',
|
||||
|
@ -72,14 +54,6 @@ Difficulty = namedtuple('Difficulty',
|
|||
|
||||
total_items_to_place = 153
|
||||
|
||||
def easy_conditional_extras(timer, _goal, _mode, pool, placed_items):
|
||||
extraitems = total_items_to_place - len(pool) - len(placed_items)
|
||||
if extraitems < len(easyextra):
|
||||
return easylimitedextra
|
||||
if timer in ['timed', 'timed-countdown']:
|
||||
return easytimedotherextra
|
||||
return []
|
||||
|
||||
def no_conditional_extras(*_args):
|
||||
return []
|
||||
|
||||
|
@ -109,30 +83,6 @@ difficulties = {
|
|||
progressive_armor_limit = 2,
|
||||
progressive_bottle_limit = 4,
|
||||
),
|
||||
'easy': Difficulty(
|
||||
baseitems = easybaseitems,
|
||||
bottles = normalbottles,
|
||||
bottle_count = 8,
|
||||
same_bottle = False,
|
||||
progressiveshield = ['Progressive Shield'] * 6,
|
||||
basicshield = ['Blue Shield', 'Red Shield', 'Mirror Shield'] * 2,
|
||||
progressivearmor = ['Progressive Armor'] * 4,
|
||||
basicarmor = ['Blue Mail', 'Red Mail'] * 2,
|
||||
swordless = ['Rupees (20)'] * 8,
|
||||
progressivesword = ['Progressive Sword'] * 7,
|
||||
basicsword = ['Master Sword', 'Tempered Sword', 'Golden Sword'] *2 + ['Fighter Sword'],
|
||||
timedohko = ['Green Clock'] * 25,
|
||||
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 5, # +5 more Red Clocks if there is room
|
||||
triforcehunt = ['Triforce Piece'] * 30,
|
||||
triforce_pieces_required = 20,
|
||||
retro = ['Small Key (Universal)'] * 27,
|
||||
conditional_extras = easy_conditional_extras,
|
||||
extras = [easyextra, easyfirst15extra, easysecond10extra, easythird5extra, easyfinal25extra],
|
||||
progressive_sword_limit = 4,
|
||||
progressive_shield_limit = 3,
|
||||
progressive_armor_limit = 2,
|
||||
progressive_bottle_limit = 4,
|
||||
),
|
||||
'hard': Difficulty(
|
||||
baseitems = hardbaseitems,
|
||||
bottles = hardbottles,
|
||||
|
@ -181,34 +131,10 @@ difficulties = {
|
|||
progressive_armor_limit = 0,
|
||||
progressive_bottle_limit = 4,
|
||||
),
|
||||
'insane': Difficulty(
|
||||
baseitems = insanebaseitems,
|
||||
bottles = hardbottles,
|
||||
bottle_count = 4,
|
||||
same_bottle = False,
|
||||
progressiveshield = [],
|
||||
basicshield = [],
|
||||
progressivearmor = [],
|
||||
basicarmor = [],
|
||||
swordless = ['Rupees (20)'] * 3 + ['Silver Arrows'],
|
||||
progressivesword = ['Progressive Sword'] * 3,
|
||||
basicsword = ['Fighter Sword', 'Master Sword', 'Master Sword'],
|
||||
timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5,
|
||||
timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
|
||||
triforcehunt = ['Triforce Piece'] * 30,
|
||||
triforce_pieces_required = 20,
|
||||
retro = ['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 15,
|
||||
conditional_extras = no_conditional_extras,
|
||||
extras = [insanefirst15extra, insanesecond15extra, insanethird10extra, insanefourth5extra, insanefinal25extra],
|
||||
progressive_sword_limit = 2,
|
||||
progressive_shield_limit = 0,
|
||||
progressive_armor_limit = 0,
|
||||
progressive_bottle_limit = 4,
|
||||
),
|
||||
}
|
||||
|
||||
def generate_itempool(world, player):
|
||||
if (world.difficulty not in ['easy', 'normal', 'hard', 'expert', 'insane'] or world.goal not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals']
|
||||
if (world.difficulty not in ['normal', 'hard', 'expert'] or world.goal not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals']
|
||||
or world.mode not in ['open', 'standard', 'swordless', 'inverted'] or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'] or world.progressive not in ['on', 'off', 'random']):
|
||||
raise NotImplementedError('Not supported yet')
|
||||
|
||||
|
@ -217,29 +143,37 @@ def generate_itempool(world, player):
|
|||
|
||||
world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False)
|
||||
world.get_location('Ganon', player).event = True
|
||||
world.get_location('Ganon', player).locked = True
|
||||
world.push_item(world.get_location('Agahnim 1', player), ItemFactory('Beat Agahnim 1', player), False)
|
||||
world.get_location('Agahnim 1', player).event = True
|
||||
world.get_location('Agahnim 1', player).locked = True
|
||||
world.push_item(world.get_location('Agahnim 2', player), ItemFactory('Beat Agahnim 2', player), False)
|
||||
world.get_location('Agahnim 2', player).event = True
|
||||
world.get_location('Agahnim 2', player).locked = True
|
||||
world.push_item(world.get_location('Dark Blacksmith Ruins', player), ItemFactory('Pick Up Purple Chest', player), False)
|
||||
world.get_location('Dark Blacksmith Ruins', player).event = True
|
||||
world.get_location('Dark Blacksmith Ruins', player).locked = True
|
||||
world.push_item(world.get_location('Frog', player), ItemFactory('Get Frog', player), False)
|
||||
world.get_location('Frog', player).event = True
|
||||
world.get_location('Frog', player).locked = True
|
||||
world.push_item(world.get_location('Missing Smith', player), ItemFactory('Return Smith', player), False)
|
||||
world.get_location('Missing Smith', player).event = True
|
||||
world.get_location('Missing Smith', player).locked = True
|
||||
world.push_item(world.get_location('Floodgate', player), ItemFactory('Open Floodgate', player), False)
|
||||
world.get_location('Floodgate', player).event = True
|
||||
world.get_location('Floodgate', player).locked = True
|
||||
|
||||
# set up item pool
|
||||
if world.custom:
|
||||
(pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.retro, world.customitemarray)
|
||||
(pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.swords, world.retro, world.customitemarray)
|
||||
world.rupoor_cost = min(world.customitemarray[67], 9999)
|
||||
else:
|
||||
(pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.retro)
|
||||
(pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.swords, world.retro)
|
||||
world.itempool += ItemFactory(pool, player)
|
||||
for (location, item) in placed_items:
|
||||
world.push_item(world.get_location(location, player), ItemFactory(item, player), False)
|
||||
world.get_location(location, player).event = True
|
||||
world.get_location(location, player).locked = True
|
||||
world.lamps_needed_for_dark_rooms = lamps_needed_for_dark_rooms
|
||||
if clock_mode is not None:
|
||||
world.clock_mode = clock_mode
|
||||
|
@ -254,7 +188,7 @@ def generate_itempool(world, player):
|
|||
# logic has some branches where having 4 hearts is one possible requirement (of several alternatives)
|
||||
# rather than making all hearts/heart pieces progression items (which slows down generation considerably)
|
||||
# We mark one random heart container as an advancement item (or 4 heart pieces in expert mode)
|
||||
if world.difficulty in ['easy', 'normal', 'hard'] and not (world.custom and world.customitemarray[30] == 0):
|
||||
if world.difficulty in ['normal', 'hard'] and not (world.custom and world.customitemarray[30] == 0):
|
||||
[item for item in world.itempool if item.name == 'Boss Heart Container' and item.player == player][0].advancement = True
|
||||
elif world.difficulty in ['expert'] and not (world.custom and world.customitemarray[29] < 4):
|
||||
adv_heart_pieces = [item for item in world.itempool if item.name == 'Piece of Heart' and item.player == player][0:4]
|
||||
|
@ -341,6 +275,7 @@ def create_dynamic_shop_locations(world, player):
|
|||
|
||||
world.push_item(loc, ItemFactory(item['item'], player), False)
|
||||
loc.event = True
|
||||
loc.locked = True
|
||||
|
||||
|
||||
def fill_prizes(world, attempts=15):
|
||||
|
@ -393,7 +328,7 @@ def set_up_shops(world, player):
|
|||
|
||||
#special shop types
|
||||
|
||||
def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, retro):
|
||||
def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro):
|
||||
pool = []
|
||||
placed_items = []
|
||||
clock_mode = None
|
||||
|
@ -411,8 +346,6 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, retro):
|
|||
pool.extend(basicgloves)
|
||||
|
||||
lamps_needed_for_dark_rooms = 1
|
||||
if difficulty == 'easy':
|
||||
lamps_needed_for_dark_rooms = 3
|
||||
|
||||
# insanity shuffle doesn't have fake LW/DW logic so for now guaranteed Mirror and Moon Pearl at the start
|
||||
if shuffle == 'insanity_legacy':
|
||||
|
@ -448,7 +381,7 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, retro):
|
|||
else:
|
||||
pool.extend(diff.basicarmor)
|
||||
|
||||
if mode == 'swordless':
|
||||
if swords == 'swordless':
|
||||
pool.extend(diff.swordless)
|
||||
elif mode == 'standard':
|
||||
if want_progressives():
|
||||
|
@ -505,7 +438,7 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, retro):
|
|||
pool.extend(['Small Key (Universal)'])
|
||||
return (pool, placed_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, retro, customitemarray):
|
||||
def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, customitemarray):
|
||||
pool = []
|
||||
placed_items = []
|
||||
clock_mode = None
|
||||
|
@ -589,8 +522,6 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, r
|
|||
diff = difficulties[difficulty]
|
||||
|
||||
lamps_needed_for_dark_rooms = 1
|
||||
if difficulty == 'easy':
|
||||
lamps_needed_for_dark_rooms = customitemarray[12]
|
||||
|
||||
# expert+ difficulties produce the same contents for
|
||||
# all bottles, since only one bottle is available
|
||||
|
@ -659,24 +590,25 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, r
|
|||
|
||||
# A quick test to ensure all combinations generate the correct amount of items.
|
||||
def test():
|
||||
for difficulty in ['easy', 'normal', 'hard', 'expert', 'insane']:
|
||||
for difficulty in ['normal', 'hard', 'expert']:
|
||||
for goal in ['ganon', 'triforcehunt', 'pedestal']:
|
||||
for timer in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown']:
|
||||
for mode in ['open', 'standard', 'swordless', 'inverted']:
|
||||
for progressive in ['on', 'off']:
|
||||
for shuffle in ['full', 'insane']:
|
||||
for retro in [True, False]:
|
||||
out = get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, retro)
|
||||
count = len(out[0]) + len(out[1])
|
||||
for mode in ['open', 'standard', 'inverted']:
|
||||
for swords in ['random', 'assured', 'swordless', 'vanilla']:
|
||||
for progressive in ['on', 'off']:
|
||||
for shuffle in ['full', 'insanity_legacy']:
|
||||
for retro in [True, False]:
|
||||
out = get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro)
|
||||
count = len(out[0]) + len(out[1])
|
||||
|
||||
correct_count = total_items_to_place
|
||||
if goal in ['pedestal']:
|
||||
# pedestal goals generate one extra item
|
||||
correct_count += 1
|
||||
if retro:
|
||||
correct_count += 28
|
||||
correct_count = total_items_to_place
|
||||
if goal in ['pedestal']:
|
||||
# pedestal goals generate one extra item
|
||||
correct_count += 1
|
||||
if retro:
|
||||
correct_count += 28
|
||||
|
||||
assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, retro))
|
||||
assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, retro))
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
|
|
1
Items.py
1
Items.py
|
@ -24,6 +24,7 @@ def ItemFactory(items, player):
|
|||
|
||||
# Format: Name: (Advancement, Priority, Type, ItemCode, Pedestal Hint Text, Pedestal Credit Text, Sick Kid Credit Text, Zora Credit Text, Witch Credit Text, Flute Boy Credit Text, Hint Text)
|
||||
item_table = {'Bow': (True, False, None, 0x0B, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'the Bow'),
|
||||
'Progressive Bow': (True, False, None, 0x64, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'),
|
||||
'Book of Mudora': (True, False, None, 0x1D, 'This is a\nparadox?!', 'and the story book', 'the scholarly kid', 'moon runes for sale', 'drugs for literacy', 'book-worm boy can read again', 'the Book'),
|
||||
'Hammer': (True, False, None, 0x09, 'stop\nhammer time!', 'and m c hammer', 'hammer-smashing kid', 'm c hammer for sale', 'stop... hammer time', 'stop, hammer time', 'the hammer'),
|
||||
'Hookshot': (True, False, None, 0x0A, 'BOING!!!\nBOING!!!\nBOING!!!', 'and the tickle beam', 'tickle-monster kid', 'tickle beam for sale', 'witch and tickle boy', 'beam boy tickles again', 'the Hookshot'),
|
||||
|
|
6
Main.py
6
Main.py
|
@ -24,7 +24,7 @@ def main(args, seed=None):
|
|||
start = time.clock()
|
||||
|
||||
# initialize the world
|
||||
world = World(args.multi, args.shuffle, args.logic, args.mode, args.difficulty, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.beatableonly, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity, args.retro, args.custom, args.customitemarray, args.shufflebosses, args.hints)
|
||||
world = World(args.multi, args.shuffle, args.logic, args.mode, args.swords, args.difficulty, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.beatableonly, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity, args.retro, args.custom, args.customitemarray, args.shufflebosses, args.hints)
|
||||
logger = logging.getLogger('')
|
||||
if seed is None:
|
||||
random.seed(None)
|
||||
|
@ -180,7 +180,7 @@ def gt_filler(world):
|
|||
|
||||
def copy_world(world):
|
||||
# ToDo: Not good yet
|
||||
ret = World(world.players, world.shuffle, world.logic, world.mode, world.difficulty, world.timer, world.progressive, world.goal, world.algorithm, world.place_dungeon_items, world.check_beatable_only, world.shuffle_ganon, world.quickswap, world.fastmenu, world.disable_music, world.keysanity, world.retro, world.custom, world.customitemarray, world.boss_shuffle, world.hints)
|
||||
ret = World(world.players, world.shuffle, world.logic, world.mode, world.swords, world.difficulty, world.timer, world.progressive, world.goal, world.algorithm, world.place_dungeon_items, world.check_beatable_only, world.shuffle_ganon, world.quickswap, world.fastmenu, world.disable_music, world.keysanity, world.retro, world.custom, world.customitemarray, world.boss_shuffle, world.hints)
|
||||
ret.required_medallions = world.required_medallions.copy()
|
||||
ret.swamp_patch_required = world.swamp_patch_required.copy()
|
||||
ret.ganon_at_pyramid = world.ganon_at_pyramid.copy()
|
||||
|
@ -238,6 +238,8 @@ def copy_world(world):
|
|||
item.location = ret.get_location(location.name, location.player)
|
||||
if location.event:
|
||||
ret.get_location(location.name, location.player).event = True
|
||||
if location.locked:
|
||||
ret.get_location(location.name, location.player).locked = True
|
||||
|
||||
# copy remaining itempool. No item in itempool should have an assigned location
|
||||
for item in world.itempool:
|
||||
|
|
13
README.md
13
README.md
|
@ -95,12 +95,6 @@ This is only noticeably different if the the Ganon shuffle option is enabled.
|
|||
|
||||
## Game Difficulty
|
||||
|
||||
### Easy
|
||||
|
||||
This setting doubles the number of swords, shields, armors, bottles, and silver arrows in the item pool.
|
||||
This setting will also triple the number of Lamps available, and all will be obtainable before dark rooms.
|
||||
Within dungeons, the number of items found will be displayed on screen if there is no timer.
|
||||
|
||||
### Normal
|
||||
|
||||
This is the default setting that has an item pool most similar to the original
|
||||
|
@ -118,11 +112,6 @@ the player from having fairies in bottles.
|
|||
This setting is a more extreme version of the Hard setting. Potions are further nerfed, the item
|
||||
pool is less helpful, and the player can find no armor, only a Master Sword, and only a single bottle.
|
||||
|
||||
### Insane
|
||||
|
||||
This setting is a modest step up from Expert. The main difference is that the player will never find any
|
||||
additional health.
|
||||
|
||||
## Timer Setting
|
||||
|
||||
### None
|
||||
|
@ -358,7 +347,7 @@ Select the game mode. (default: open)
|
|||
Select the game completion goal. (default: ganon)
|
||||
|
||||
```
|
||||
--difficulty [{easy,normal,hard,expert,insane}]
|
||||
--difficulty [{normal,hard,expert}]
|
||||
```
|
||||
|
||||
Select the game difficulty. Affects available itempool. (default: normal)
|
||||
|
|
75
Rom.py
75
Rom.py
|
@ -549,7 +549,7 @@ def patch_rom(world, player, rom):
|
|||
rom.write_byte(0x51DE, 0x00)
|
||||
|
||||
# set open mode:
|
||||
if world.mode in ['open', 'swordless', 'inverted']:
|
||||
if world.mode in ['open', 'inverted']:
|
||||
rom.write_byte(0x180032, 0x01) # open mode
|
||||
|
||||
# disable sword sprite from uncle
|
||||
|
@ -587,7 +587,7 @@ def patch_rom(world, player, rom):
|
|||
# potion magic restore amount
|
||||
rom.write_byte(0x180085, 0x40) # Half Magic
|
||||
#Cape magic cost
|
||||
rom.write_bytes(0x3ADA7, [0x02, 0x02, 0x02])
|
||||
rom.write_bytes(0x3ADA7, [0x02, 0x04, 0x08])
|
||||
# Byrna Invulnerability: off
|
||||
rom.write_byte(0x18004F, 0x00)
|
||||
#Disable catching fairies
|
||||
|
@ -603,31 +603,11 @@ def patch_rom(world, player, rom):
|
|||
# Powdered Fairies Prize
|
||||
rom.write_byte(0x36DD0, 0xD8) # One Heart
|
||||
# potion heal amount
|
||||
rom.write_byte(0x180084, 0x08) # One Heart
|
||||
rom.write_byte(0x180084, 0x20) # 4 Hearts
|
||||
# potion magic restore amount
|
||||
rom.write_byte(0x180085, 0x20) # Quarter Magic
|
||||
#Cape magic cost
|
||||
rom.write_bytes(0x3ADA7, [0x01, 0x01, 0x01])
|
||||
# Byrna Invulnerability: off
|
||||
rom.write_byte(0x18004F, 0x00)
|
||||
#Disable catching fairies
|
||||
rom.write_byte(0x34FD6, 0x80)
|
||||
overflow_replacement = GREEN_TWENTY_RUPEES
|
||||
# Rupoor negative value
|
||||
rom.write_int16(0x180036, world.rupoor_cost)
|
||||
# Set stun items
|
||||
rom.write_byte(0x180180, 0x00) # Nothing
|
||||
# Make silver arrows only usable against Ganon
|
||||
rom.write_byte(0x180181, 0x01)
|
||||
elif world.difficulty == 'insane':
|
||||
# Powdered Fairies Prize
|
||||
rom.write_byte(0x36DD0, 0x79) # Bees
|
||||
# potion heal amount
|
||||
rom.write_byte(0x180084, 0x00) # No healing
|
||||
# potion magic restore amount
|
||||
rom.write_byte(0x180085, 0x00) # No healing
|
||||
#Cape magic cost
|
||||
rom.write_bytes(0x3ADA7, [0x01, 0x01, 0x01])
|
||||
rom.write_bytes(0x3ADA7, [0x02, 0x04, 0x08])
|
||||
# Byrna Invulnerability: off
|
||||
rom.write_byte(0x18004F, 0x00)
|
||||
#Disable catching fairies
|
||||
|
@ -664,12 +644,7 @@ def patch_rom(world, player, rom):
|
|||
else:
|
||||
overflow_replacement = GREEN_TWENTY_RUPEES
|
||||
|
||||
if world.difficulty in ['easy']:
|
||||
rom.write_byte(0x180182, 0x03) # auto equip silvers on pickup and at ganon
|
||||
elif world.retro and world.difficulty in ['hard', 'expert', 'insane']: #FIXME: this is temporary for v29 baserom (perhaps no so temporary?)
|
||||
rom.write_byte(0x180182, 0x03) # auto equip silvers on pickup and at ganon
|
||||
else:
|
||||
rom.write_byte(0x180182, 0x01) # auto equip silvers on pickup
|
||||
rom.write_byte(0x180182, 0x01) # auto equip silvers on pickup
|
||||
|
||||
#Byrna residual magic cost
|
||||
rom.write_bytes(0x45C42, [0x04, 0x02, 0x01])
|
||||
|
@ -709,7 +684,7 @@ def patch_rom(world, player, rom):
|
|||
random.shuffle(packs)
|
||||
prizes[:56] = [drop for pack in packs for drop in pack]
|
||||
|
||||
if world.difficulty in ['hard', 'expert', 'insane']:
|
||||
if world.difficulty in ['hard', 'expert']:
|
||||
prize_replacements = {0xE0: 0xDF, # Fairy -> heart
|
||||
0xE3: 0xD8} # Big magic -> small magic
|
||||
prizes = [prize_replacements.get(prize, prize) for prize in prizes]
|
||||
|
@ -753,26 +728,16 @@ def patch_rom(world, player, rom):
|
|||
rom.write_byte(address, prize)
|
||||
|
||||
# Fill in item substitutions table
|
||||
if world.difficulty in ['easy']:
|
||||
rom.write_bytes(0x184000, [
|
||||
# original_item, limit, replacement_item, filler
|
||||
0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees
|
||||
0x58, 0x01, 0x43, 0xFF, # silver arrows -> 1 arrow
|
||||
0x51, 0x06, 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade
|
||||
0x53, 0x06, 0x54, 0xFF, # 6 +5 arrow upgrades -> +10 arrow upgrade
|
||||
0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel
|
||||
])
|
||||
else:
|
||||
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
|
||||
0x53, 0x06, 0x54, 0xFF, # 6 +5 arrow upgrades -> +10 arrow upgrade
|
||||
0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel
|
||||
])
|
||||
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
|
||||
0x53, 0x06, 0x54, 0xFF, # 6 +5 arrow upgrades -> +10 arrow upgrade
|
||||
0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel
|
||||
])
|
||||
|
||||
# set Fountain bottle exchange items
|
||||
if world.difficulty in ['hard', 'expert', 'insane']:
|
||||
if world.difficulty in ['hard', 'expert']:
|
||||
rom.write_byte(0x348FF, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x48][random.randint(0, 5)])
|
||||
rom.write_byte(0x3493B, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x48][random.randint(0, 5)])
|
||||
else:
|
||||
|
@ -837,9 +802,7 @@ def patch_rom(world, player, rom):
|
|||
rom.write_int32(0x180200, -100 * 60 * 60 * 60) # red clock adjustment time (in frames, sint32)
|
||||
rom.write_int32(0x180204, 2 * 60 * 60) # blue clock adjustment time (in frames, sint32)
|
||||
rom.write_int32(0x180208, 4 * 60 * 60) # green clock adjustment time (in frames, sint32)
|
||||
if world.difficulty == 'easy':
|
||||
rom.write_int32(0x18020C, (20 + ERtimeincrease) * 60 * 60) # starting time (in frames, sint32)
|
||||
elif world.difficulty == 'normal':
|
||||
if world.difficulty == 'normal':
|
||||
rom.write_int32(0x18020C, (10 + ERtimeincrease) * 60 * 60) # starting time (in frames, sint32)
|
||||
else:
|
||||
rom.write_int32(0x18020C, int((5 + ERtimeincrease / 2) * 60 * 60)) # starting time (in frames, sint32)
|
||||
|
@ -866,7 +829,7 @@ def patch_rom(world, player, rom):
|
|||
rom.write_byte(0x180211, 0x06) #Game type, we set the Entrance and item randomization flags
|
||||
|
||||
# assorted fixes
|
||||
rom.write_byte(0x1800A2, 0x01) # remain in real dark world when dying in dark word dungion before killing aga1
|
||||
rom.write_byte(0x1800A2, 0x01) # remain in real dark world when dying in dark world dungeon before killing aga1
|
||||
rom.write_byte(0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence.
|
||||
if world.mode == 'inverted':
|
||||
rom.write_byte(0x180169, 0x02) # lock aga/ganon tower door with crystals in inverted
|
||||
|
@ -878,6 +841,7 @@ def patch_rom(world, player, rom):
|
|||
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
|
||||
rom.write_byte(0x18008B, 0x00) # Pyramid Hole not pre-opened
|
||||
rom.write_byte(0xF5D73, 0xF0) # bees are catchable
|
||||
rom.write_byte(0xF5F10, 0xF0) # bees are catchable
|
||||
rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness
|
||||
|
@ -928,8 +892,6 @@ def patch_rom(world, player, rom):
|
|||
# compasses showing dungeon count
|
||||
if world.clock_mode != 'off':
|
||||
rom.write_byte(0x18003C, 0x00) # Currently must be off if timer is on, because they use same HUD location
|
||||
elif world.difficulty == 'easy':
|
||||
rom.write_byte(0x18003C, 0x02) # always on
|
||||
elif world.keysanity:
|
||||
rom.write_byte(0x18003C, 0x01) # show on pickup
|
||||
else:
|
||||
|
@ -1311,6 +1273,9 @@ def write_strings(rom, world, player):
|
|||
greenpendant = world.find_items('Green Pendant', player)[0]
|
||||
tt['sahasrahla_bring_courage'] = 'I lost my family heirloom in %s' % greenpendant.hint_text
|
||||
|
||||
tt['sign_ganons_tower'] = ('You need %d crystal to enter.' if world.crystals_needed_for_gt == 1 else 'You need %d crystals to enter.') % world.crystals_needed_for_gt
|
||||
tt['sign_ganon'] = ('You need %d crystal to beat Ganon.' if world.crystals_needed_for_ganon == 1 else 'You need %d crystals to beat Ganon.') % world.crystals_needed_for_ganon
|
||||
|
||||
tt['uncle_leaving_text'] = Uncle_texts[random.randint(0, len(Uncle_texts) - 1)]
|
||||
tt['end_triforce'] = "{NOBORDER}\n" + Triforce_texts[random.randint(0, len(Triforce_texts) - 1)]
|
||||
tt['bomb_shop_big_bomb'] = BombShop2_texts[random.randint(0, len(BombShop2_texts) - 1)]
|
||||
|
|
16
Rules.py
16
Rules.py
|
@ -26,14 +26,16 @@ def set_rules(world, player):
|
|||
open_rules(world, player)
|
||||
elif world.mode == 'standard':
|
||||
standard_rules(world, player)
|
||||
elif world.mode == 'swordless':
|
||||
swordless_rules(world, player)
|
||||
elif world.mode == 'inverted':
|
||||
open_rules(world, player)
|
||||
inverted_rules(world, player)
|
||||
else:
|
||||
raise NotImplementedError('Not implemented yet')
|
||||
|
||||
if world.swords == 'swordless':
|
||||
# FIXME: !!! Does not handle inverted properly
|
||||
swordless_rules(world, player)
|
||||
|
||||
if world.logic == 'noglitches':
|
||||
no_glitches_rules(world, player)
|
||||
elif world.logic == 'minorglitches':
|
||||
|
@ -792,8 +794,7 @@ def inverted_rules(world, player):
|
|||
'Ganons Tower - Pre-Moldorm Chest', 'Ganons Tower - Validation Chest']:
|
||||
forbid_item(world.get_location(location, player), 'Big Key (Ganons Tower)', player)
|
||||
|
||||
set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has('Crystal 1', player) and state.has('Crystal 2', player)
|
||||
and state.has('Crystal 3', player) and state.has('Crystal 4', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player) and state.has('Crystal 7', player)
|
||||
set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_crystals(world.crystals_needed_for_ganon, player)
|
||||
and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times
|
||||
set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop
|
||||
|
||||
|
@ -801,7 +802,7 @@ def inverted_rules(world, player):
|
|||
|
||||
set_trock_key_rules(world, player)
|
||||
|
||||
set_rule(world.get_entrance('Inverted Ganons Tower', player), lambda state: state.has('Crystal 1', player) and state.has('Crystal 2', player) and state.has('Crystal 3', player) and state.has('Crystal 4', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player) and state.has('Crystal 7', player))
|
||||
set_rule(world.get_entrance('Inverted Ganons Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt, player))
|
||||
|
||||
def no_glitches_rules(world, player):
|
||||
if world.mode != 'inverted':
|
||||
|
@ -887,11 +888,6 @@ def open_rules(world, player):
|
|||
|
||||
def swordless_rules(world, player):
|
||||
|
||||
# for the time being swordless mode just inherits all fixes from open mode.
|
||||
# should there ever be fixes that apply to open mode but not swordless, this
|
||||
# can be revisited.
|
||||
open_rules(world, player)
|
||||
|
||||
set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle
|
||||
set_rule(world.get_entrance('Agahnim 1', player), lambda state: (state.has('Hammer', player) or state.has('Fire Rod', player) or state.can_shoot_arrows(player) or state.has('Cane of Somaria', player)) and state.has_key('Small Key (Agahnims Tower)', player, 2))
|
||||
set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player))
|
||||
|
|
2
Text.py
2
Text.py
|
@ -1883,5 +1883,7 @@ class TextTable(object):
|
|||
# 190
|
||||
text['sign_east_death_mountain_bridge'] = CompressedTextMapper.convert("How did you get up here?")
|
||||
text['fish_money'] = CompressedTextMapper.convert("It's a secret to everyone.")
|
||||
text['sign_ganons_tower'] = CompressedTextMapper.convert("You need all 7 crystals to enter.")
|
||||
text['sign_ganon'] = CompressedTextMapper.convert("You need all 7 crystals to beat Ganon.")
|
||||
text['end_pad_data'] = bytearray([0xfb])
|
||||
text['terminator'] = bytearray([0xFF, 0xFF])
|
||||
|
|
Loading…
Reference in New Issue