Merged in DR v0.5.1.5
This commit is contained in:
@@ -110,6 +110,7 @@ class World(object):
|
|||||||
set_player_attr('owwhirlpools', [])
|
set_player_attr('owwhirlpools', [])
|
||||||
set_player_attr('remote_items', False)
|
set_player_attr('remote_items', False)
|
||||||
set_player_attr('required_medallions', ['Ether', 'Quake'])
|
set_player_attr('required_medallions', ['Ether', 'Quake'])
|
||||||
|
set_player_attr('bottle_refills', ['Bottle (Green Potion)', 'Bottle (Green Potion)'])
|
||||||
set_player_attr('swamp_patch_required', False)
|
set_player_attr('swamp_patch_required', False)
|
||||||
set_player_attr('powder_patch_required', False)
|
set_player_attr('powder_patch_required', False)
|
||||||
set_player_attr('ganon_at_pyramid', True)
|
set_player_attr('ganon_at_pyramid', True)
|
||||||
@@ -2561,6 +2562,7 @@ class Spoiler(object):
|
|||||||
self.doorTypes = {}
|
self.doorTypes = {}
|
||||||
self.lobbies = {}
|
self.lobbies = {}
|
||||||
self.medallions = {}
|
self.medallions = {}
|
||||||
|
self.bottles = {}
|
||||||
self.playthrough = {}
|
self.playthrough = {}
|
||||||
self.unreachables = []
|
self.unreachables = []
|
||||||
self.startinventory = []
|
self.startinventory = []
|
||||||
@@ -2660,6 +2662,15 @@ class Spoiler(object):
|
|||||||
self.medallions[f'Misery Mire ({self.world.get_player_names(player)})'] = self.world.required_medallions[player][0]
|
self.medallions[f'Misery Mire ({self.world.get_player_names(player)})'] = self.world.required_medallions[player][0]
|
||||||
self.medallions[f'Turtle Rock ({self.world.get_player_names(player)})'] = self.world.required_medallions[player][1]
|
self.medallions[f'Turtle Rock ({self.world.get_player_names(player)})'] = self.world.required_medallions[player][1]
|
||||||
|
|
||||||
|
self.bottles = OrderedDict()
|
||||||
|
if self.world.players == 1:
|
||||||
|
self.bottles['Waterfall Bottle'] = self.world.bottle_refills[1][0]
|
||||||
|
self.bottles['Pyramid Bottle'] = self.world.bottle_refills[1][1]
|
||||||
|
else:
|
||||||
|
for player in range(1, self.world.players + 1):
|
||||||
|
self.bottles[f'Waterfall Bottle ({self.world.get_player_names(player)})'] = self.world.bottle_refills[player][0]
|
||||||
|
self.bottles[f'Pyramid Bottle ({self.world.get_player_names(player)})'] = self.world.bottle_refills[player][1]
|
||||||
|
|
||||||
self.locations = OrderedDict()
|
self.locations = OrderedDict()
|
||||||
listed_locations = set()
|
listed_locations = set()
|
||||||
|
|
||||||
@@ -2742,6 +2753,7 @@ class Spoiler(object):
|
|||||||
out.update(self.locations)
|
out.update(self.locations)
|
||||||
out['Starting Inventory'] = self.startinventory
|
out['Starting Inventory'] = self.startinventory
|
||||||
out['Special'] = self.medallions
|
out['Special'] = self.medallions
|
||||||
|
out['Bottles'] = self.bottles
|
||||||
if self.hashes:
|
if self.hashes:
|
||||||
out['Hashes'] = {f"{self.world.player_names[player][team]} (Team {team+1})": hash for (player, team), hash in self.hashes.items()}
|
out['Hashes'] = {f"{self.world.player_names[player][team]} (Team {team+1})": hash for (player, team), hash in self.hashes.items()}
|
||||||
if self.shops:
|
if self.shops:
|
||||||
@@ -2824,6 +2836,7 @@ class Spoiler(object):
|
|||||||
if len(self.hashes) > 0:
|
if len(self.hashes) > 0:
|
||||||
for team in range(self.world.teams):
|
for team in range(self.world.teams):
|
||||||
outfile.write('%s%s\n' % (f"Hash - {self.world.player_names[player][team]} (Team {team+1}): " if self.world.teams > 1 else 'Hash: ', self.hashes[player, team]))
|
outfile.write('%s%s\n' % (f"Hash - {self.world.player_names[player][team]} (Team {team+1}): " if self.world.teams > 1 else 'Hash: ', self.hashes[player, team]))
|
||||||
|
|
||||||
outfile.write('\n\nRequirements:\n\n')
|
outfile.write('\n\nRequirements:\n\n')
|
||||||
for dungeon, medallion in self.medallions.items():
|
for dungeon, medallion in self.medallions.items():
|
||||||
outfile.write(f'{dungeon}:'.ljust(line_width) + '%s Medallion\n' % medallion)
|
outfile.write(f'{dungeon}:'.ljust(line_width) + '%s Medallion\n' % medallion)
|
||||||
@@ -2834,6 +2847,10 @@ class Spoiler(object):
|
|||||||
if self.world.crystals_ganon_orig[player] == 'random':
|
if self.world.crystals_ganon_orig[player] == 'random':
|
||||||
outfile.write(str('Crystals Required for Ganon' + player_name + ':').ljust(line_width) + '%s\n' % (str(self.metadata['ganon_crystals'][player])))
|
outfile.write(str('Crystals Required for Ganon' + player_name + ':').ljust(line_width) + '%s\n' % (str(self.metadata['ganon_crystals'][player])))
|
||||||
|
|
||||||
|
outfile.write('\n\nBottle Refills:\n\n')
|
||||||
|
for fairy, bottle in self.bottles.items():
|
||||||
|
outfile.write(f'\n{fairy}: {bottle}')
|
||||||
|
|
||||||
if self.overworlds:
|
if self.overworlds:
|
||||||
# overworlds: overworld transitions;
|
# overworlds: overworld transitions;
|
||||||
outfile.write('\n\nOverworld:\n\n')
|
outfile.write('\n\nOverworld:\n\n')
|
||||||
@@ -3012,7 +3029,7 @@ access_mode = {"items": 0, "locations": 1, "none": 2}
|
|||||||
boss_mode = {"none": 0, "simple": 1, "full": 2, "random": 3, "chaos": 3}
|
boss_mode = {"none": 0, "simple": 1, "full": 2, "random": 3, "chaos": 3}
|
||||||
enemy_mode = {"none": 0, "shuffled": 1, "random": 2, "chaos": 2, "legacy": 3}
|
enemy_mode = {"none": 0, "shuffled": 1, "random": 2, "chaos": 2, "legacy": 3}
|
||||||
|
|
||||||
# byte 7: HHHD DP?? (enemy_health, enemy_dmg, potshuffle, ?)
|
# byte 7: HHHD DPBS (enemy_health, enemy_dmg, potshuffle, bomb logic, shuffle links)
|
||||||
e_health = {"default": 0, "easy": 1, "normal": 2, "hard": 3, "expert": 4}
|
e_health = {"default": 0, "easy": 1, "normal": 2, "hard": 3, "expert": 4}
|
||||||
e_dmg = {"default": 0, "shuffled": 1, "random": 2}
|
e_dmg = {"default": 0, "shuffled": 1, "random": 2}
|
||||||
|
|
||||||
@@ -3043,7 +3060,8 @@ class Settings(object):
|
|||||||
| (0x20 if w.mapshuffle[p] else 0) | (0x10 if w.compassshuffle[p] else 0)
|
| (0x20 if w.mapshuffle[p] else 0) | (0x10 if w.compassshuffle[p] else 0)
|
||||||
| (boss_mode[w.boss_shuffle[p]] << 2) | (enemy_mode[w.enemy_shuffle[p]]),
|
| (boss_mode[w.boss_shuffle[p]] << 2) | (enemy_mode[w.enemy_shuffle[p]]),
|
||||||
|
|
||||||
(e_health[w.enemy_health[p]] << 5) | (e_dmg[w.enemy_damage[p]] << 3) | (0x4 if w.potshuffle[p] else 0)])
|
(e_health[w.enemy_health[p]] << 5) | (e_dmg[w.enemy_damage[p]] << 3) | (0x4 if w.potshuffle[p] else 0)
|
||||||
|
| (0x2 if w.bombbag[p] else 0) | (1 if w.shufflelinks[p] else 0)])
|
||||||
return base64.b64encode(code, "+-".encode()).decode()
|
return base64.b64encode(code, "+-".encode()).decode()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -3088,6 +3106,8 @@ class Settings(object):
|
|||||||
args.enemy_health[p] = r(e_health)[(settings[7] & 0xE0) >> 5]
|
args.enemy_health[p] = r(e_health)[(settings[7] & 0xE0) >> 5]
|
||||||
args.enemy_damage[p] = r(e_dmg)[(settings[7] & 0x18) >> 3]
|
args.enemy_damage[p] = r(e_dmg)[(settings[7] & 0x18) >> 3]
|
||||||
args.shufflepots[p] = True if settings[7] & 0x4 else False
|
args.shufflepots[p] = True if settings[7] & 0x4 else False
|
||||||
|
args.bombbag[p] = True if settings[7] & 0x2 else False
|
||||||
|
args.shufflelinks[p] = True if settings[7] & 0x1 else False
|
||||||
|
|
||||||
|
|
||||||
class KeyRuleType(FastEnum):
|
class KeyRuleType(FastEnum):
|
||||||
|
|||||||
@@ -284,6 +284,8 @@ def link_entrances(world, player):
|
|||||||
dw_entrances = list()
|
dw_entrances = list()
|
||||||
caves = list(Cave_Exits + Cave_Three_Exits + Old_Man_House)
|
caves = list(Cave_Exits + Cave_Three_Exits + Old_Man_House)
|
||||||
for e in entrance_pool:
|
for e in entrance_pool:
|
||||||
|
if world.mode[player] == 'standard' and e == 'Bonk Fairy (Light)':
|
||||||
|
continue
|
||||||
region = world.get_entrance(e, player).parent_region
|
region = world.get_entrance(e, player).parent_region
|
||||||
if region.type == RegionType.LightWorld:
|
if region.type == RegionType.LightWorld:
|
||||||
lw_entrances.append(e)
|
lw_entrances.append(e)
|
||||||
@@ -338,6 +340,8 @@ def link_entrances(world, player):
|
|||||||
lw_entrances = list()
|
lw_entrances = list()
|
||||||
dw_entrances = list()
|
dw_entrances = list()
|
||||||
for e in entrance_pool:
|
for e in entrance_pool:
|
||||||
|
if world.mode[player] == 'standard' and e == 'Bonk Fairy (Light)':
|
||||||
|
continue
|
||||||
if e not in list(zip(*drop_connections + dropexit_connections))[0]:
|
if e not in list(zip(*drop_connections + dropexit_connections))[0]:
|
||||||
region = world.get_entrance(e, player).parent_region
|
region = world.get_entrance(e, player).parent_region
|
||||||
if region.type == RegionType.LightWorld:
|
if region.type == RegionType.LightWorld:
|
||||||
@@ -539,20 +543,25 @@ def link_entrances(world, player):
|
|||||||
place_blacksmith(world, links_house, player)
|
place_blacksmith(world, links_house, player)
|
||||||
|
|
||||||
# place connectors in inaccessible regions
|
# place connectors in inaccessible regions
|
||||||
connect_inaccessible_regions(world, list(entrance_pool), [], caves, player)
|
pool = list(entrance_pool)
|
||||||
|
if world.mode[player] == 'standard' and 'Bonk Fairy (Light)' in pool:
|
||||||
|
pool.remove('Bonk Fairy (Light)')
|
||||||
|
connect_inaccessible_regions(world, pool, [], caves, player)
|
||||||
|
|
||||||
# place old man, has limited options
|
# place old man, has limited options
|
||||||
place_old_man(world, list(entrance_pool), player)
|
pool = [e for e in pool if e in entrance_pool]
|
||||||
|
place_old_man(world, pool, player)
|
||||||
|
|
||||||
# place bomb shop, has limited options
|
# place bomb shop, has limited options
|
||||||
bomb_shop_doors = list(entrance_pool)
|
bomb_shop_doors = list(entrance_pool)
|
||||||
if world.logic[player] in ['noglitches', 'minorglitches'] or (invFlag != (0x1b in world.owswaps[player][0] and world.owMixed[player])):
|
if world.logic[player] in ['noglitches', 'minorglitches'] or (invFlag != (0x1b in world.owswaps[player][0] and world.owMixed[player])):
|
||||||
bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']]
|
bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']]
|
||||||
bomb_shop = random.choice(bomb_shop_doors)
|
bomb_shop = random.choice(bomb_shop_doors)
|
||||||
|
pool.remove(bomb_shop)
|
||||||
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
|
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
|
||||||
|
|
||||||
# shuffle connectors
|
# shuffle connectors
|
||||||
connect_caves(world, list(entrance_pool), [], caves, player)
|
connect_caves(world, pool, [], caves, player)
|
||||||
|
|
||||||
# place remaining doors
|
# place remaining doors
|
||||||
connect_doors(world, list(entrance_pool), list(exit_pool), player)
|
connect_doors(world, list(entrance_pool), list(exit_pool), player)
|
||||||
@@ -616,10 +625,14 @@ def link_entrances(world, player):
|
|||||||
place_blacksmith(world, links_house, player)
|
place_blacksmith(world, links_house, player)
|
||||||
|
|
||||||
# place connectors in inaccessible regions
|
# place connectors in inaccessible regions
|
||||||
connect_inaccessible_regions(world, list(entrance_pool), [], caves, player)
|
pool = list(entrance_pool)
|
||||||
|
if world.mode[player] == 'standard' and 'Bonk Fairy (Light)' in pool:
|
||||||
|
pool.remove('Bonk Fairy (Light)')
|
||||||
|
connect_inaccessible_regions(world, pool, [], caves, player)
|
||||||
|
|
||||||
# place old man, has limited options
|
# place old man, has limited options
|
||||||
place_old_man(world, list(entrance_pool), player)
|
pool = [e for e in pool if e in entrance_pool]
|
||||||
|
place_old_man(world, pool, player)
|
||||||
caves.append('Old Man Cave Exit (West)')
|
caves.append('Old Man Cave Exit (West)')
|
||||||
|
|
||||||
# place bomb shop, has limited options
|
# place bomb shop, has limited options
|
||||||
@@ -628,10 +641,13 @@ def link_entrances(world, player):
|
|||||||
bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']]
|
bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']]
|
||||||
random.shuffle(bomb_shop_doors)
|
random.shuffle(bomb_shop_doors)
|
||||||
bomb_shop = bomb_shop_doors.pop()
|
bomb_shop = bomb_shop_doors.pop()
|
||||||
|
pool.remove(bomb_shop)
|
||||||
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
|
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
|
||||||
|
|
||||||
# shuffle connectors
|
# shuffle connectors
|
||||||
doors = list(entrance_pool)
|
doors = list(entrance_pool)
|
||||||
|
if world.mode[player] == 'standard' and 'Bonk Fairy (Light)' in doors:
|
||||||
|
doors.remove('Bonk Fairy (Light)')
|
||||||
exit_doors = [e for e in entrance_pool if e not in entrance_exits]
|
exit_doors = [e for e in entrance_pool if e not in entrance_exits]
|
||||||
random.shuffle(doors)
|
random.shuffle(doors)
|
||||||
random.shuffle(exit_doors)
|
random.shuffle(exit_doors)
|
||||||
|
|||||||
@@ -371,6 +371,15 @@ def generate_itempool(world, player):
|
|||||||
tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
|
tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
|
||||||
world.required_medallions[player] = (mm_medallion, tr_medallion)
|
world.required_medallions[player] = (mm_medallion, tr_medallion)
|
||||||
|
|
||||||
|
# shuffle bottle refills
|
||||||
|
if world.difficulty[player] in ['hard', 'expert']:
|
||||||
|
waterfall_bottle = hardbottles[random.randint(0, 5)]
|
||||||
|
pyramid_bottle = hardbottles[random.randint(0, 5)]
|
||||||
|
else:
|
||||||
|
waterfall_bottle = normalbottles[random.randint(0, 6)]
|
||||||
|
pyramid_bottle = normalbottles[random.randint(0, 6)]
|
||||||
|
world.bottle_refills[player] = (waterfall_bottle, pyramid_bottle)
|
||||||
|
|
||||||
set_up_shops(world, player)
|
set_up_shops(world, player)
|
||||||
|
|
||||||
if world.retro[player]:
|
if world.retro[player]:
|
||||||
|
|||||||
3
Main.py
3
Main.py
@@ -30,7 +30,7 @@ from Fill import sell_potions, sell_keys, balance_multiworld_progression, balanc
|
|||||||
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops
|
from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops
|
||||||
from Utils import output_path, parse_player_names
|
from Utils import output_path, parse_player_names
|
||||||
|
|
||||||
__version__ = '0.5.1.4-u'
|
__version__ = '0.5.1.5-u'
|
||||||
|
|
||||||
from source.classes.BabelFish import BabelFish
|
from source.classes.BabelFish import BabelFish
|
||||||
|
|
||||||
@@ -388,6 +388,7 @@ def copy_world(world):
|
|||||||
ret.player_names = copy.deepcopy(world.player_names)
|
ret.player_names = copy.deepcopy(world.player_names)
|
||||||
ret.remote_items = world.remote_items.copy()
|
ret.remote_items = world.remote_items.copy()
|
||||||
ret.required_medallions = world.required_medallions.copy()
|
ret.required_medallions = world.required_medallions.copy()
|
||||||
|
ret.bottle_refills = world.bottle_refills.copy()
|
||||||
ret.swamp_patch_required = world.swamp_patch_required.copy()
|
ret.swamp_patch_required = world.swamp_patch_required.copy()
|
||||||
ret.ganon_at_pyramid = world.ganon_at_pyramid.copy()
|
ret.ganon_at_pyramid = world.ganon_at_pyramid.copy()
|
||||||
ret.powder_patch_required = world.powder_patch_required.copy()
|
ret.powder_patch_required = world.powder_patch_required.copy()
|
||||||
|
|||||||
65
Mystery.py
65
Mystery.py
@@ -101,7 +101,8 @@ def get_weights(path):
|
|||||||
raise Exception(f'Failed to read weights file: {e}')
|
raise Exception(f'Failed to read weights file: {e}')
|
||||||
|
|
||||||
def roll_settings(weights):
|
def roll_settings(weights):
|
||||||
def get_choice(option, root=weights):
|
def get_choice(option, root=None):
|
||||||
|
root = weights if root is None else root
|
||||||
if option not in root:
|
if option not in root:
|
||||||
return None
|
return None
|
||||||
if type(root[option]) is not dict:
|
if type(root[option]) is not dict:
|
||||||
@@ -116,13 +117,24 @@ def roll_settings(weights):
|
|||||||
return default
|
return default
|
||||||
return choice
|
return choice
|
||||||
|
|
||||||
|
while True:
|
||||||
|
subweights = weights.get('subweights', {})
|
||||||
|
if len(subweights) == 0:
|
||||||
|
break
|
||||||
|
chances = ({k: int(v['chance']) for (k, v) in subweights.items()})
|
||||||
|
subweight_name = random.choices(list(chances.keys()), weights=list(chances.values()))[0]
|
||||||
|
subweights = weights.get('subweights', {}).get(subweight_name, {}).get('weights', {})
|
||||||
|
subweights['subweights'] = subweights.get('subweights', {})
|
||||||
|
weights = {**weights, **subweights}
|
||||||
|
|
||||||
ret = argparse.Namespace()
|
ret = argparse.Namespace()
|
||||||
|
|
||||||
glitches_required = get_choice('glitches_required')
|
glitches_required = get_choice('glitches_required')
|
||||||
if glitches_required not in ['none', 'no_logic']:
|
if glitches_required is not None:
|
||||||
print("Only NMG and No Logic supported")
|
if glitches_required not in ['none', 'owg', 'no_logic']:
|
||||||
glitches_required = 'none'
|
print("Only NMG, OWG, and No Logic supported")
|
||||||
ret.logic = {'none': 'noglitches', 'owg': 'owglitches', 'no_logic': 'nologic'}[glitches_required]
|
glitches_required = 'none'
|
||||||
|
ret.logic = {'none': 'noglitches', 'owg': 'owglitches', 'no_logic': 'nologic'}[glitches_required]
|
||||||
|
|
||||||
item_placement = get_choice('item_placement')
|
item_placement = get_choice('item_placement')
|
||||||
# not supported in ER
|
# not supported in ER
|
||||||
@@ -162,12 +174,13 @@ def roll_settings(weights):
|
|||||||
ret.standardize_palettes = get_choice('standardize_palettes') if 'standardize_palettes' in weights else 'standardize'
|
ret.standardize_palettes = get_choice('standardize_palettes') if 'standardize_palettes' in weights else 'standardize'
|
||||||
|
|
||||||
goal = get_choice('goals')
|
goal = get_choice('goals')
|
||||||
ret.goal = {'ganon': 'ganon',
|
if goal is not None:
|
||||||
'fast_ganon': 'crystals',
|
ret.goal = {'ganon': 'ganon',
|
||||||
'dungeons': 'dungeons',
|
'fast_ganon': 'crystals',
|
||||||
'pedestal': 'pedestal',
|
'dungeons': 'dungeons',
|
||||||
'triforce-hunt': 'triforcehunt'
|
'pedestal': 'pedestal',
|
||||||
}[goal]
|
'triforce-hunt': 'triforcehunt'
|
||||||
|
}[goal]
|
||||||
ret.openpyramid = goal == 'fast_ganon' if ret.shuffle in ['vanilla', 'dungeonsfull', 'dungeonssimple'] else False
|
ret.openpyramid = goal == 'fast_ganon' if ret.shuffle in ['vanilla', 'dungeonsfull', 'dungeonssimple'] else False
|
||||||
|
|
||||||
ret.shuffleganon = get_choice('shuffleganon') == 'on'
|
ret.shuffleganon = get_choice('shuffleganon') == 'on'
|
||||||
@@ -175,15 +188,15 @@ def roll_settings(weights):
|
|||||||
ret.crystals_gt = get_choice('tower_open')
|
ret.crystals_gt = get_choice('tower_open')
|
||||||
|
|
||||||
ret.crystals_ganon = get_choice('ganon_open')
|
ret.crystals_ganon = get_choice('ganon_open')
|
||||||
|
|
||||||
if ret.goal == 'triforcehunt':
|
goal_min = get_choice_default('triforce_goal_min', default=20)
|
||||||
goal_min = get_choice_default('triforce_goal_min', default=20)
|
goal_max = get_choice_default('triforce_goal_max', default=20)
|
||||||
goal_max = get_choice_default('triforce_goal_max', default=20)
|
pool_min = get_choice_default('triforce_pool_min', default=30)
|
||||||
pool_min = get_choice_default('triforce_pool_min', default=30)
|
pool_max = get_choice_default('triforce_pool_max', default=30)
|
||||||
pool_max = get_choice_default('triforce_pool_max', default=30)
|
ret.triforce_goal = random.randint(int(goal_min), int(goal_max))
|
||||||
ret.triforce_goal = random.randint(int(goal_min), int(goal_max))
|
min_diff = get_choice_default('triforce_min_difference', default=10)
|
||||||
min_diff = get_choice_default('triforce_min_difference', default=10)
|
ret.triforce_pool = random.randint(max(int(pool_min), ret.triforce_goal + int(min_diff)), int(pool_max))
|
||||||
ret.triforce_pool = random.randint(max(int(pool_min), ret.triforce_goal + int(min_diff)), int(pool_max))
|
|
||||||
ret.mode = get_choice('world_state')
|
ret.mode = get_choice('world_state')
|
||||||
if ret.mode == 'retro':
|
if ret.mode == 'retro':
|
||||||
ret.mode = 'open'
|
ret.mode = 'open'
|
||||||
@@ -194,11 +207,13 @@ def roll_settings(weights):
|
|||||||
|
|
||||||
ret.hints = get_choice('hints') == 'on'
|
ret.hints = get_choice('hints') == 'on'
|
||||||
|
|
||||||
ret.swords = {'randomized': 'random',
|
swords = get_choice('weapons')
|
||||||
'assured': 'assured',
|
if swords is not None:
|
||||||
'vanilla': 'vanilla',
|
ret.swords = {'randomized': 'random',
|
||||||
'swordless': 'swordless'
|
'assured': 'assured',
|
||||||
}[get_choice('weapons')]
|
'vanilla': 'vanilla',
|
||||||
|
'swordless': 'swordless'
|
||||||
|
}[swords]
|
||||||
|
|
||||||
ret.difficulty = get_choice('item_pool')
|
ret.difficulty = get_choice('item_pool')
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,17 @@ CLI: ```--bombbag```
|
|||||||
|
|
||||||
# Bug Fixes and Notes.
|
# Bug Fixes and Notes.
|
||||||
|
|
||||||
|
* 0.5.1.5
|
||||||
|
* Fix for hard pool capacity upgrades missing
|
||||||
|
* Bonk Fairy (Light) is no longer in logic for ER Standard and is forbidden to be a connector, so rain state isn't exitable
|
||||||
|
* Bug fix for retro + enemizer and arrows appearing under pots
|
||||||
|
* Added bombbag and shufflelinks to settings code
|
||||||
|
* Catobat fixes:
|
||||||
|
* Fairy refills in spoiler
|
||||||
|
* Subweights support in mystery
|
||||||
|
* More defaults for mystery weights
|
||||||
|
* Less camera jank for straight stair transitions
|
||||||
|
* Bug with Straight stairs with vanilla doors where Link's walking animation stopped early is fixed
|
||||||
* 0.5.1.4
|
* 0.5.1.4
|
||||||
* Revert quadrant glitch fix for baserom
|
* Revert quadrant glitch fix for baserom
|
||||||
* Fix for inverted
|
* Fix for inverted
|
||||||
|
|||||||
27
Rom.py
27
Rom.py
@@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx
|
|||||||
|
|
||||||
|
|
||||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||||
RANDOMIZERBASEHASH = '98dd1d63d05b2a3bf2989782679a6b5e'
|
RANDOMIZERBASEHASH = 'e3373be98af9d6de1cb1ab12176ecb0e'
|
||||||
|
|
||||||
|
|
||||||
class JsonRom(object):
|
class JsonRom(object):
|
||||||
@@ -1155,12 +1155,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
|
|||||||
])
|
])
|
||||||
|
|
||||||
# set Fountain bottle exchange items
|
# set Fountain bottle exchange items
|
||||||
if world.difficulty[player] in ['hard', 'expert']:
|
rom.write_byte(0x348FF, ItemFactory(world.bottle_refills[player][0], player).code)
|
||||||
rom.write_byte(0x348FF, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x48][random.randint(0, 5)])
|
rom.write_byte(0x3493B, ItemFactory(world.bottle_refills[player][1], player).code)
|
||||||
rom.write_byte(0x3493B, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x48][random.randint(0, 5)])
|
|
||||||
else:
|
|
||||||
rom.write_byte(0x348FF, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x3D, 0x48][random.randint(0, 6)])
|
|
||||||
rom.write_byte(0x3493B, [0x16, 0x2B, 0x2C, 0x2D, 0x3C, 0x3D, 0x48][random.randint(0, 6)])
|
|
||||||
|
|
||||||
#enable Fat Fairy Chests
|
#enable Fat Fairy Chests
|
||||||
rom.write_bytes(0x1FC16, [0xB1, 0xC6, 0xF9, 0xC9, 0xC6, 0xF9])
|
rom.write_bytes(0x1FC16, [0xB1, 0xC6, 0xF9, 0xC9, 0xC6, 0xF9])
|
||||||
@@ -1543,6 +1539,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
|
|||||||
rom.write_byte(0x180176, 0x0A if world.retro[player] else 0x00) # wood arrow cost
|
rom.write_byte(0x180176, 0x0A if world.retro[player] else 0x00) # wood arrow cost
|
||||||
rom.write_byte(0x180178, 0x32 if world.retro[player] else 0x00) # silver arrow cost
|
rom.write_byte(0x180178, 0x32 if world.retro[player] else 0x00) # silver arrow cost
|
||||||
rom.write_byte(0x301FC, 0xDA if world.retro[player] else 0xE1) # rupees replace arrows under pots
|
rom.write_byte(0x301FC, 0xDA if world.retro[player] else 0xE1) # rupees replace arrows under pots
|
||||||
|
if enemized:
|
||||||
|
rom.write_byte(0x1B152e, 0xDA if world.retro[player] else 0xE1)
|
||||||
rom.write_byte(0x30052, 0xDB if world.retro[player] else 0xE2) # replace arrows in fish prize from bottle merchant
|
rom.write_byte(0x30052, 0xDB if world.retro[player] else 0xE2) # replace arrows in fish prize from bottle merchant
|
||||||
rom.write_bytes(0xECB4E, [0xA9, 0x00, 0xEA, 0xEA] if world.retro[player] else [0xAF, 0x77, 0xF3, 0x7E]) # Thief steals rupees instead of arrows
|
rom.write_bytes(0xECB4E, [0xA9, 0x00, 0xEA, 0xEA] if world.retro[player] else [0xAF, 0x77, 0xF3, 0x7E]) # Thief steals rupees instead of arrows
|
||||||
rom.write_bytes(0xF0D96, [0xA9, 0x00, 0xEA, 0xEA] if world.retro[player] else [0xAF, 0x77, 0xF3, 0x7E]) # Pikit steals rupees instead of arrows
|
rom.write_bytes(0xF0D96, [0xA9, 0x00, 0xEA, 0xEA] if world.retro[player] else [0xAF, 0x77, 0xF3, 0x7E]) # Pikit steals rupees instead of arrows
|
||||||
@@ -1708,13 +1706,16 @@ def write_custom_shops(rom, world, player):
|
|||||||
loc_item = ItemFactory(item['item'], player)
|
loc_item = ItemFactory(item['item'], player)
|
||||||
if (not world.shopsanity[player] and shop.region.name == 'Capacity Upgrade'
|
if (not world.shopsanity[player] and shop.region.name == 'Capacity Upgrade'
|
||||||
and world.difficulty[player] != 'normal'):
|
and world.difficulty[player] != 'normal'):
|
||||||
continue # skip cap upgrades except in normal/shopsanity
|
# really should be 5A instead of B0 -- surprise!!!
|
||||||
item_id = loc_item.code
|
item_id, price, replace, replace_price, item_max = 0xB0, [0, 0], 0xFF, [0, 0], 1
|
||||||
price = int16_as_bytes(item['price'])
|
else:
|
||||||
replace = ItemFactory(item['replacement'], player).code if item['replacement'] else 0xFF
|
item_id = loc_item.code
|
||||||
replace_price = int16_as_bytes(item['replacement_price'])
|
price = int16_as_bytes(item['price'])
|
||||||
|
replace = ItemFactory(item['replacement'], player).code if item['replacement'] else 0xFF
|
||||||
|
replace_price = int16_as_bytes(item['replacement_price'])
|
||||||
|
item_max = item['max']
|
||||||
item_player = 0 if item['player'] == player else item['player']
|
item_player = 0 if item['player'] == player else item['player']
|
||||||
item_data = [shop_id, item_id] + price + [item['max'], replace] + replace_price + [item_player]
|
item_data = [shop_id, item_id] + price + [item_max, replace] + replace_price + [item_player]
|
||||||
items_data.extend(item_data)
|
items_data.extend(item_data)
|
||||||
|
|
||||||
rom.write_bytes(0x184800, shop_data)
|
rom.write_bytes(0x184800, shop_data)
|
||||||
|
|||||||
1
Rules.py
1
Rules.py
@@ -1605,6 +1605,7 @@ def standard_rules(world, player):
|
|||||||
add_rule(world.get_entrance('Hyrule Castle Main Gate (South)', player), lambda state: state.has('Zelda Delivered', player))
|
add_rule(world.get_entrance('Hyrule Castle Main Gate (South)', player), lambda state: state.has('Zelda Delivered', player))
|
||||||
add_rule(world.get_entrance('Hyrule Castle Main Gate (North)', player), lambda state: state.has('Zelda Delivered', player))
|
add_rule(world.get_entrance('Hyrule Castle Main Gate (North)', player), lambda state: state.has('Zelda Delivered', player))
|
||||||
add_rule(world.get_entrance('Hyrule Castle Ledge Drop', player), lambda state: state.has('Zelda Delivered', player))
|
add_rule(world.get_entrance('Hyrule Castle Ledge Drop', player), lambda state: state.has('Zelda Delivered', player))
|
||||||
|
add_rule(world.get_entrance('Bonk Fairy (Light)', player), lambda state: state.has('Zelda Delivered', player))
|
||||||
|
|
||||||
# don't allow bombs to get past here before zelda is rescued
|
# don't allow bombs to get past here before zelda is rescued
|
||||||
set_rule(world.get_entrance('GT Hookshot South Entry to Ranged Crystal', player), lambda state: (state.can_use_bombs(player) and state.has('Zelda Delivered', player)) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player)) # or state.has('Cane of Somaria', player))
|
set_rule(world.get_entrance('GT Hookshot South Entry to Ranged Crystal', player), lambda state: (state.can_use_bombs(player) and state.has('Zelda Delivered', player)) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player)) # or state.has('Cane of Somaria', player))
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
; Normal doors use $FE to store the trap door indicator
|
; Normal doors use $FE to store the trap door indicator
|
||||||
; Normal doors use $045e to store Y coordinate when transitioning to in-room stairs
|
; Normal doors use $045e to store Y coordinate when transitioning to in-room stairs
|
||||||
; Normal doors use $045f to determine the order in which supertile quadrants are drawn
|
; Normal doors use $045f to determine the order in which supertile quadrants are drawn
|
||||||
|
; Straight stairs use $046d to store X coordinate on animation start
|
||||||
; Spiral doors use $045e to store stair type
|
; Spiral doors use $045e to store stair type
|
||||||
; Gfx uses $b1 to for sub-sub-sub-module thing
|
; Gfx uses $b1 to for sub-sub-sub-module thing
|
||||||
|
|
||||||
|
|||||||
@@ -681,6 +681,8 @@ db $00,$07,$20,$20,$07,$07,$07,$07,$07,$20,$20,$07,$20,$20,$20,$20
|
|||||||
db $07,$07,$02,$02,$02,$02,$07,$07,$07,$20,$20,$07,$20,$20,$20,$07
|
db $07,$07,$02,$02,$02,$02,$07,$07,$07,$20,$20,$07,$20,$20,$20,$07
|
||||||
|
|
||||||
;27f300
|
;27f300
|
||||||
|
DungeonTilesets:
|
||||||
|
db $04,$04,$05,$12,$04,$08,$07,$0C,$09,$0B,$05,$0A,$0D,$0E,$06,$06
|
||||||
|
|
||||||
;
|
;
|
||||||
;org $27ff00
|
;org $27ff00
|
||||||
|
|||||||
@@ -150,15 +150,14 @@ LoadRoomVert:
|
|||||||
.notEdge
|
.notEdge
|
||||||
lda $01 : and #$03 : cmp #$03 : bne .normal
|
lda $01 : and #$03 : cmp #$03 : bne .normal
|
||||||
jsr ScrollToInroomStairs
|
jsr ScrollToInroomStairs
|
||||||
|
stz $046d
|
||||||
bra .end
|
bra .end
|
||||||
.normal
|
.normal
|
||||||
ldy #$01 : jsr ShiftVariablesMainDir
|
ldy #$01 : jsr ShiftVariablesMainDir
|
||||||
jsr PrepScrollToNormal
|
jsr PrepScrollToNormal
|
||||||
.scroll
|
.scroll
|
||||||
lda $01 : and #$40 : pha
|
lda $01 : and #$40 : sta $046d
|
||||||
jsr ScrollX
|
jsr ScrollX
|
||||||
pla : beq .end
|
|
||||||
ldy #$00 : jsr ApplyScroll
|
|
||||||
.end
|
.end
|
||||||
plb ; restore db register
|
plb ; restore db register
|
||||||
rts
|
rts
|
||||||
@@ -291,6 +290,11 @@ StraightStairsAdj:
|
|||||||
stx $0464 : sty $012e ; what we wrote over
|
stx $0464 : sty $012e ; what we wrote over
|
||||||
lda.l DRMode : beq +
|
lda.l DRMode : beq +
|
||||||
lda $045e : bne .toInroom
|
lda $045e : bne .toInroom
|
||||||
|
lda $046d : beq .noScroll
|
||||||
|
sta $22
|
||||||
|
ldy #$00 : jsr ApplyScroll
|
||||||
|
stz $046d
|
||||||
|
.noScroll
|
||||||
jsr GetTileAttribute : tax
|
jsr GetTileAttribute : tax
|
||||||
lda $11 : cmp #$12 : beq .goingNorth
|
lda $11 : cmp #$12 : beq .goingNorth
|
||||||
lda $a2 : cmp #$51 : bne ++
|
lda $a2 : cmp #$51 : bne ++
|
||||||
@@ -338,9 +342,10 @@ db $d0, $f6, $10, $1a, $f0, $00
|
|||||||
|
|
||||||
StraightStairsFix:
|
StraightStairsFix:
|
||||||
{
|
{
|
||||||
|
pha
|
||||||
lda.l DRMode : bne +
|
lda.l DRMode : bne +
|
||||||
!add $20 : sta $20 ;what we wrote over
|
pla : !add $20 : sta $20 : rtl ;what we wrote over
|
||||||
+ rtl
|
+ pla : rtl
|
||||||
}
|
}
|
||||||
|
|
||||||
StraightStairLayerFix:
|
StraightStairLayerFix:
|
||||||
|
|||||||
@@ -360,6 +360,11 @@ OWNewDestination:
|
|||||||
|
|
||||||
.return
|
.return
|
||||||
lda $05 : sta $8a
|
lda $05 : sta $8a
|
||||||
|
;bra +
|
||||||
|
; nop #8
|
||||||
|
; jsl $02EA41
|
||||||
|
; nop #8
|
||||||
|
;+
|
||||||
rep #$30 : rts
|
rep #$30 : rts
|
||||||
}
|
}
|
||||||
OWWorldUpdate: ; x = owid of destination screen
|
OWWorldUpdate: ; x = owid of destination screen
|
||||||
|
|||||||
@@ -168,7 +168,11 @@ ScrollX: ;change the X offset variables
|
|||||||
|
|
||||||
pla : sta $00
|
pla : sta $00
|
||||||
sep #$30
|
sep #$30
|
||||||
lda $04 : sta $22
|
lda $04 : ldx $046d : bne .straight
|
||||||
|
sta $22 : bra +
|
||||||
|
.straight
|
||||||
|
sta $046d ; set X position later
|
||||||
|
+
|
||||||
lda $00 : sta $23 : sta $0609 : sta $060d
|
lda $00 : sta $23 : sta $0609 : sta $060d
|
||||||
lda $01 : sta $a9
|
lda $01 : sta $a9
|
||||||
lda $0e : asl : ora $ac : sta $ac
|
lda $0e : asl : ora $ac : sta $ac
|
||||||
|
|||||||
Binary file not shown.
57
mystery_example_subweights.yml
Normal file
57
mystery_example_subweights.yml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
description: Example for subweights
|
||||||
|
glitches_required: none
|
||||||
|
world_state: open
|
||||||
|
goals: ganon
|
||||||
|
weapons: randomized
|
||||||
|
entrance_shuffle: none
|
||||||
|
intensity: 3
|
||||||
|
subweights:
|
||||||
|
vanilla:
|
||||||
|
chance: 25
|
||||||
|
weights:
|
||||||
|
door_shuffle: vanilla
|
||||||
|
keydropshuffle:
|
||||||
|
on: 40
|
||||||
|
off: 60
|
||||||
|
basic:
|
||||||
|
chance: 25
|
||||||
|
weights:
|
||||||
|
door_shuffle: basic
|
||||||
|
keydropshuffle:
|
||||||
|
on: 70
|
||||||
|
off: 30
|
||||||
|
crossed:
|
||||||
|
chance: 25
|
||||||
|
weights:
|
||||||
|
door_shuffle: crossed
|
||||||
|
keydropshuffle:
|
||||||
|
on: 90
|
||||||
|
off: 10
|
||||||
|
chaos:
|
||||||
|
chance: 25
|
||||||
|
weights:
|
||||||
|
door_shuffle: crossed
|
||||||
|
entrance_shuffle:
|
||||||
|
none: 30
|
||||||
|
crossed: 70
|
||||||
|
keydropshuffle:
|
||||||
|
on: 90
|
||||||
|
off: 10
|
||||||
|
shopsanity:
|
||||||
|
on: 50
|
||||||
|
off: 50
|
||||||
|
bombbag:
|
||||||
|
on: 25
|
||||||
|
off: 75
|
||||||
|
subweights:
|
||||||
|
normal:
|
||||||
|
chance: 40
|
||||||
|
weights: {}
|
||||||
|
swordless:
|
||||||
|
chance: 20
|
||||||
|
weights:
|
||||||
|
weapons: swordless
|
||||||
|
keysanity:
|
||||||
|
chance: 40
|
||||||
|
weights:
|
||||||
|
dungeon_items: full
|
||||||
Reference in New Issue
Block a user