implement secrets.SystemRandom() for --race

This commit is contained in:
Fabian Dill 2020-07-14 07:01:51 +02:00
parent 59a71dbb05
commit 93ecf5988b
11 changed files with 312 additions and 250 deletions

View File

@ -10,6 +10,10 @@ from EntranceShuffle import door_addresses, indirect_connections
from Utils import int16_as_bytes
from typing import Union
import secrets
import random
class World(object):
debug_types = False
player_names: list
@ -28,6 +32,8 @@ class World(object):
setattr(self, name[7:], method)
logging.debug(f"Set {self}.{name[7:]} to {method}")
self.get_location = self._debug_get_location
self.random = random.Random() # world-local random state is saved in case of future use a
# persistently running program with multiple worlds rolling concurrently
self.players = players
self.teams = 1
self.shuffle = shuffle.copy()
@ -116,6 +122,9 @@ class World(object):
set_player_attr('triforce_pieces_available', 30)
set_player_attr('triforce_pieces_required', 20)
def secure(self):
self.random = secrets.SystemRandom()
@property
def player_ids(self):
yield from range(1, self.players + 1)

View File

@ -1,5 +1,4 @@
import logging
import random
from BaseClasses import Boss
from Fill import FillError
@ -193,11 +192,11 @@ def place_bosses(world, player):
if world.boss_shuffle[player] == "basic": # vanilla bosses shuffled
bosses = placeable_bosses + ['Armos Knights', 'Lanmolas', 'Moldorm']
else: # all bosses present, the three duplicates chosen at random
bosses = all_bosses + [random.choice(placeable_bosses) for _ in range(3)]
bosses = all_bosses + [world.random.choice(placeable_bosses) for _ in range(3)]
logging.getLogger('').debug('Bosses chosen %s', bosses)
random.shuffle(bosses)
world.random.shuffle(bosses)
for [loc, level] in boss_locations:
loc_text = loc + (' ('+level+')' if level else '')
boss = next((b for b in bosses if can_place_boss(world, player, b, loc, level)), None)
@ -211,7 +210,8 @@ def place_bosses(world, player):
for [loc, level] in boss_locations:
loc_text = loc + (' ('+level+')' if level else '')
try:
boss = random.choice([b for b in placeable_bosses if can_place_boss(world, player, b, loc, level)])
boss = world.random.choice(
[b for b in placeable_bosses if can_place_boss(world, player, b, loc, level)])
except IndexError:
raise FillError('Could not place boss for location %s' % loc_text)

View File

@ -1,5 +1,3 @@
import random
from BaseClasses import Dungeon
from Bosses import BossFactory
from Fill import fill_restrictive
@ -58,7 +56,7 @@ def fill_dungeons(world):
dungeon_regions, big_key, small_keys, dungeon_items = dungeons.pop(0)
# this is what we need to fill
dungeon_locations = [location for location in world.get_unfilled_locations() if location.parent_region.name in dungeon_regions]
random.shuffle(dungeon_locations)
world.random.shuffle(dungeon_locations)
all_state = all_state_base.copy()

View File

@ -3,7 +3,6 @@ import argparse
import copy
import os
import logging
import random
import textwrap
import shlex
import sys

View File

@ -1,5 +1,3 @@
import random
# ToDo: With shuffle_ganon option, prevent gtower from linking to an exit only location through a 2 entrance cave.
from collections import defaultdict
@ -13,7 +11,7 @@ def link_entrances(world, player):
Old_Man_House = Old_Man_House_Base.copy()
Cave_Three_Exits = Cave_Three_Exits_Base.copy()
unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits)
unbias_some_entrances(world, Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits)
# setup mandatory connections
for exitname, regionname in mandatory_connections:
@ -75,8 +73,8 @@ def link_entrances(world, player):
# we shuffle all 2 entrance caves as pairs as a start
# start with the ones that need to be directed
two_door_caves = list(Two_Door_Caves_Directional)
random.shuffle(two_door_caves)
random.shuffle(caves)
world.random.shuffle(two_door_caves)
world.random.shuffle(caves)
while two_door_caves:
entrance1, entrance2 = two_door_caves.pop()
exit1, exit2 = caves.pop()
@ -85,7 +83,7 @@ def link_entrances(world, player):
# now the remaining pairs
two_door_caves = list(Two_Door_Caves)
random.shuffle(two_door_caves)
world.random.shuffle(two_door_caves)
while two_door_caves:
entrance1, entrance2 = two_door_caves.pop()
exit1, exit2 = caves.pop()
@ -96,10 +94,10 @@ def link_entrances(world, player):
# place old man, has limited options
remaining_entrances = ['Old Man Cave (West)', 'Old Man House (Bottom)', 'Death Mountain Return Cave (West)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'Paradox Cave (Top)',
'Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave (Top)', 'Spiral Cave', 'Spiral Cave (Bottom)']
random.shuffle(old_man_entrances)
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
remaining_entrances.extend(old_man_entrances)
random.shuffle(remaining_entrances)
world.random.shuffle(remaining_entrances)
old_man_entrance = remaining_entrances.pop()
connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player)
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
@ -115,13 +113,13 @@ def link_entrances(world, player):
scramble_holes(world, player)
# place blacksmith, has limited options
random.shuffle(blacksmith_doors)
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
bomb_shop_doors.extend(blacksmith_doors)
# place bomb shop, has limited options
random.shuffle(bomb_shop_doors)
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
single_doors.extend(bomb_shop_doors)
@ -153,7 +151,7 @@ def link_entrances(world, player):
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
old_man_entrances = [door for door in old_man_entrances if door in lw_entrances]
random.shuffle(old_man_entrances)
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
lw_entrances.remove(old_man_exit)
@ -162,7 +160,7 @@ def link_entrances(world, player):
all_entrances = lw_entrances + dw_entrances
# cannot place it anywhere already taken (or that are otherwise not eligable for placement)
blacksmith_doors = [door for door in blacksmith_doors if door in all_entrances]
random.shuffle(blacksmith_doors)
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
if blacksmith_hut in lw_entrances:
@ -175,7 +173,7 @@ def link_entrances(world, player):
all_entrances = lw_entrances + dw_entrances
# cannot place it anywhere already taken (or that are otherwise not eligable for placement)
bomb_shop_doors = [door for door in bomb_shop_doors if door in all_entrances]
random.shuffle(bomb_shop_doors)
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
if bomb_shop in lw_entrances:
@ -184,7 +182,7 @@ def link_entrances(world, player):
dw_entrances.remove(bomb_shop)
# place the old man cave's entrance somewhere in the light world
random.shuffle(lw_entrances)
world.random.shuffle(lw_entrances)
old_man_entrance = lw_entrances.pop()
connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player)
@ -223,10 +221,10 @@ def link_entrances(world, player):
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
random.shuffle(old_man_entrances)
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
lw_entrances.extend(old_man_entrances)
random.shuffle(lw_entrances)
world.random.shuffle(lw_entrances)
old_man_entrance = lw_entrances.pop()
connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player)
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
@ -241,13 +239,13 @@ def link_entrances(world, player):
scramble_holes(world, player)
# place blacksmith, has limited options
random.shuffle(blacksmith_doors)
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
bomb_shop_doors.extend(blacksmith_doors)
# place dam and pyramid fairy, have limited options
random.shuffle(bomb_shop_doors)
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
single_doors.extend(bomb_shop_doors)
@ -278,7 +276,8 @@ def link_entrances(world, player):
# must connect front of hyrule castle to do escape
connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
else:
caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3)))
caves.append(tuple(world.random.sample(
['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3)))
lw_entrances.append('Hyrule Castle Entrance (South)')
if not world.shuffle_ganon:
@ -290,7 +289,7 @@ def link_entrances(world, player):
# we randomize which world requirements we fulfill first so we get better dungeon distribution
#we also places the Old Man House at this time to make sure he can be connected to the desert one way
if random.randint(0, 1) == 0:
if world.random.randint(0, 1) == 0:
caves += old_man_house
connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player)
try:
@ -317,7 +316,7 @@ def link_entrances(world, player):
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
old_man_entrances = [door for door in old_man_entrances if door in lw_entrances]
random.shuffle(old_man_entrances)
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
lw_entrances.remove(old_man_exit)
@ -326,7 +325,7 @@ def link_entrances(world, player):
all_entrances = lw_entrances + dw_entrances
# cannot place it anywhere already taken (or that are otherwise not eligable for placement)
blacksmith_doors = [door for door in blacksmith_doors if door in all_entrances]
random.shuffle(blacksmith_doors)
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
if blacksmith_hut in lw_entrances:
@ -339,7 +338,7 @@ def link_entrances(world, player):
all_entrances = lw_entrances + dw_entrances
# cannot place it anywhere already taken (or that are otherwise not eligable for placement)
bomb_shop_doors = [door for door in bomb_shop_doors if door in all_entrances]
random.shuffle(bomb_shop_doors)
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
if bomb_shop in lw_entrances:
@ -381,7 +380,8 @@ def link_entrances(world, player):
# must connect front of hyrule castle to do escape
connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
else:
caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3)))
caves.append(tuple(world.random.sample(
['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3)))
entrances.append('Hyrule Castle Entrance (South)')
if not world.shuffle_ganon:
@ -400,7 +400,7 @@ def link_entrances(world, player):
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
old_man_entrances = [door for door in old_man_entrances if door in entrances]
random.shuffle(old_man_entrances)
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
entrances.remove(old_man_exit)
@ -408,7 +408,7 @@ def link_entrances(world, player):
# place blacksmith, has limited options
# cannot place it anywhere already taken (or that are otherwise not eligable for placement)
blacksmith_doors = [door for door in blacksmith_doors if door in entrances]
random.shuffle(blacksmith_doors)
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
entrances.remove(blacksmith_hut)
@ -418,14 +418,14 @@ def link_entrances(world, player):
# cannot place it anywhere already taken (or that are otherwise not eligable for placement)
bomb_shop_doors = [door for door in bomb_shop_doors if door in entrances]
random.shuffle(bomb_shop_doors)
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
entrances.remove(bomb_shop)
# place the old man cave's entrance somewhere
random.shuffle(entrances)
world.random.shuffle(entrances)
old_man_entrance = entrances.pop()
connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player)
@ -456,7 +456,8 @@ def link_entrances(world, player):
# must connect front of hyrule castle to do escape
connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player)
else:
caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3)))
caves.append(tuple(world.random.sample(
['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3)))
lw_entrances.append('Hyrule Castle Entrance (South)')
if not world.shuffle_ganon:
@ -466,7 +467,7 @@ def link_entrances(world, player):
caves.append('Ganons Tower Exit')
# we randomize which world requirements we fulfill first so we get better dungeon distribution
if random.randint(0, 1) == 0:
if world.random.randint(0, 1) == 0:
connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player)
connect_mandatory_exits(world, dw_entrances, caves, dw_must_exits, player)
else:
@ -479,11 +480,11 @@ def link_entrances(world, player):
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
old_man_entrances = [door for door in old_man_entrances if door in lw_entrances]
random.shuffle(old_man_entrances)
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
lw_entrances.remove(old_man_exit)
random.shuffle(lw_entrances)
world.random.shuffle(lw_entrances)
old_man_entrance = lw_entrances.pop()
connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player)
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
@ -498,13 +499,13 @@ def link_entrances(world, player):
scramble_holes(world, player)
# place blacksmith, has limited options
random.shuffle(blacksmith_doors)
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
bomb_shop_doors.extend(blacksmith_doors)
# place bomb shop, has limited options
random.shuffle(bomb_shop_doors)
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
single_doors.extend(bomb_shop_doors)
@ -520,18 +521,28 @@ def link_entrances(world, player):
dw_entrances = list(DW_Entrances + DW_Dungeon_Entrances)
dw_entrances_must_exits = list(DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit)
lw_doors = list(LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit) + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump',
'Lumberjack Tree Cave'] + list(Old_Man_Entrances)
dw_doors = list(DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit) + ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)']
lw_doors = list(LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit) + ['Kakariko Well Cave',
'Bat Cave Cave',
'North Fairy Cave',
'Sanctuary',
'Lost Woods Hideout Stump',
'Lumberjack Tree Cave'] + list(
Old_Man_Entrances)
dw_doors = list(
DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit) + [
'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'Skull Woods Second Section Door (West)']
random.shuffle(lw_doors)
random.shuffle(dw_doors)
world.random.shuffle(lw_doors)
world.random.shuffle(dw_doors)
dw_entrances_must_exits.append('Skull Woods Second Section Door (West)')
dw_entrances.append('Skull Woods Second Section Door (East)')
dw_entrances.append('Skull Woods First Section Door')
lw_entrances.extend(['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave'])
lw_entrances.extend(
['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump',
'Lumberjack Tree Cave'])
lw_entrances_must_exits = list(LW_Dungeon_Entrances_Must_Exit)
@ -576,19 +587,20 @@ def link_entrances(world, player):
dw_entrances_must_exits.append('Pyramid Entrance')
dw_doors.extend(['Ganons Tower', 'Pyramid Entrance'])
random.shuffle(lw_hole_entrances)
random.shuffle(dw_hole_entrances)
random.shuffle(hole_targets)
world.random.shuffle(lw_hole_entrances)
world.random.shuffle(dw_hole_entrances)
world.random.shuffle(hole_targets)
# decide if skull woods first section should be in light or dark world
sw_light = random.randint(0, 1) == 0
sw_light = world.random.randint(0, 1) == 0
if sw_light:
sw_hole_pool = lw_hole_entrances
mandatory_light_world.append('Skull Woods First Section Exit')
else:
sw_hole_pool = dw_hole_entrances
mandatory_dark_world.append('Skull Woods First Section Exit')
for target in ['Skull Woods First Section (Left)', 'Skull Woods First Section (Right)', 'Skull Woods First Section (Top)']:
for target in ['Skull Woods First Section (Left)', 'Skull Woods First Section (Right)',
'Skull Woods First Section (Top)']:
connect_entrance(world, sw_hole_pool.pop(), target, player)
# sanctuary has to be in light world
@ -619,7 +631,7 @@ def link_entrances(world, player):
# now let's deal with mandatory reachable stuff
def extract_reachable_exit(cavelist):
random.shuffle(cavelist)
world.random.shuffle(cavelist)
candidate = None
for cave in cavelist:
if isinstance(cave, tuple) and len(cave) > 1:
@ -636,7 +648,7 @@ def link_entrances(world, player):
def connect_reachable_exit(entrance, general, worldspecific, worldoors):
# select which one is the primary option
if random.randint(0, 1) == 0:
if world.random.randint(0, 1) == 0:
primary = general
secondary = worldspecific
else:
@ -656,7 +668,7 @@ def link_entrances(world, player):
worldspecific.append(cave)
# we randomize which world requirements we fulfill first so we get better dungeon distribution
if random.randint(0, 1) == 0:
if world.random.randint(0, 1) == 0:
for entrance in lw_entrances_must_exits:
connect_reachable_exit(entrance, caves, mandatory_light_world, lw_doors)
for entrance in dw_entrances_must_exits:
@ -670,7 +682,7 @@ def link_entrances(world, player):
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
old_man_entrances = [entrance for entrance in old_man_entrances if entrance in lw_entrances]
random.shuffle(old_man_entrances)
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
lw_entrances.remove(old_man_exit)
@ -708,7 +720,7 @@ def link_entrances(world, player):
cave_candidate = (i, len(cave))
cave = caves.pop(cave_candidate[0])
place_lightworld = random.randint(0, 1) == 0
place_lightworld = world.random.randint(0, 1) == 0
if place_lightworld:
target_doors = lw_doors
target_entrances = lw_entrances
@ -740,13 +752,13 @@ def link_entrances(world, player):
door_targets = list(Single_Cave_Targets)
# place blacksmith, has limited options
random.shuffle(blacksmith_doors)
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
bomb_shop_doors.extend(blacksmith_doors)
# place dam and pyramid fairy, have limited options
random.shuffle(bomb_shop_doors)
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
single_doors.extend(bomb_shop_doors)
@ -774,7 +786,7 @@ def link_entrances(world, player):
blacksmith_doors = list(Blacksmith_Single_Cave_Doors + Blacksmith_Multi_Cave_Doors)
door_targets = list(Single_Cave_Targets)
random.shuffle(doors)
world.random.shuffle(doors)
old_man_entrances = list(Old_Man_Entrances) + ['Tower of Hera']
@ -817,9 +829,9 @@ def link_entrances(world, player):
entrances_must_exits.append('Pyramid Entrance')
doors.extend(['Ganons Tower', 'Pyramid Entrance'])
random.shuffle(hole_entrances)
random.shuffle(hole_targets)
random.shuffle(entrances)
world.random.shuffle(hole_entrances)
world.random.shuffle(hole_targets)
world.random.shuffle(entrances)
# fill up holes
for hole in hole_entrances:
@ -838,7 +850,7 @@ def link_entrances(world, player):
# now let's deal with mandatory reachable stuff
def extract_reachable_exit(cavelist):
random.shuffle(cavelist)
world.random.shuffle(cavelist)
candidate = None
for cave in cavelist:
if isinstance(cave, tuple) and len(cave) > 1:
@ -870,7 +882,7 @@ def link_entrances(world, player):
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
old_man_entrances = [entrance for entrance in old_man_entrances if entrance in entrances]
random.shuffle(old_man_entrances)
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
entrances.remove(old_man_exit)
@ -880,14 +892,14 @@ def link_entrances(world, player):
# place blacksmith, has limited options
blacksmith_doors = [door for door in blacksmith_doors if door in doors]
random.shuffle(blacksmith_doors)
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
doors.remove(blacksmith_hut)
# place dam and pyramid fairy, have limited options
bomb_shop_doors = [door for door in bomb_shop_doors if door in doors]
random.shuffle(bomb_shop_doors)
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
doors.remove(bomb_shop)
@ -913,7 +925,7 @@ def link_entrances(world, player):
doors = LW_Entrances + LW_Dungeon_Entrances + LW_Dungeon_Entrances_Must_Exit + ['Kakariko Well Cave', 'Bat Cave Cave', 'North Fairy Cave', 'Sanctuary', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave'] + Old_Man_Entrances +\
DW_Entrances + DW_Dungeon_Entrances + DW_Entrances_Must_Exit + DW_Dungeon_Entrances_Must_Exit + ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)']
random.shuffle(doors)
world.random.shuffle(doors)
old_man_entrances = list(Old_Man_Entrances) + ['Tower of Hera']
@ -952,9 +964,9 @@ def link_entrances(world, player):
entrances_must_exits.append('Pyramid Entrance')
doors.extend(['Ganons Tower', 'Pyramid Entrance'])
random.shuffle(hole_entrances)
random.shuffle(hole_targets)
random.shuffle(entrances)
world.random.shuffle(hole_entrances)
world.random.shuffle(hole_targets)
world.random.shuffle(entrances)
# fill up holes
for hole in hole_entrances:
@ -973,7 +985,7 @@ def link_entrances(world, player):
# now let's deal with mandatory reachable stuff
def extract_reachable_exit(cavelist):
random.shuffle(cavelist)
world.random.shuffle(cavelist)
candidate = None
for cave in cavelist:
if isinstance(cave, tuple) and len(cave) > 1:
@ -1005,7 +1017,7 @@ def link_entrances(world, player):
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
old_man_entrances = [entrance for entrance in old_man_entrances if entrance in entrances]
random.shuffle(old_man_entrances)
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
entrances.remove(old_man_exit)
@ -1030,13 +1042,13 @@ def link_entrances(world, player):
door_targets = list(Single_Cave_Targets)
# place blacksmith, has limited options
random.shuffle(blacksmith_doors)
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
bomb_shop_doors.extend(blacksmith_doors)
# place dam and pyramid fairy, have limited options
random.shuffle(bomb_shop_doors)
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Big Bomb Shop', player)
single_doors.extend(bomb_shop_doors)
@ -1073,7 +1085,7 @@ def link_inverted_entrances(world, player):
Old_Man_House = Old_Man_House_Base.copy()
Cave_Three_Exits = Cave_Three_Exits_Base.copy()
unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits)
unbias_some_entrances(world, Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits)
# setup mandatory connections
for exitname, regionname in inverted_mandatory_connections:
@ -1102,7 +1114,7 @@ def link_inverted_entrances(world, player):
dw_entrances = list(Inverted_DW_Dungeon_Entrances)
# randomize which desert ledge door is a must-exit
if random.randint(0, 1) == 0:
if world.random.randint(0, 1) == 0:
lw_dungeon_entrances_must_exit.append('Desert Palace Entrance (North)')
lw_entrances.append('Desert Palace Entrance (West)')
else:
@ -1123,14 +1135,14 @@ def link_inverted_entrances(world, player):
# shuffle aga door first. If it's on HC ledge, remaining HC ledge door must be must-exit
all_entrances_aga = lw_entrances + dw_entrances
aga_doors = [i for i in all_entrances_aga]
random.shuffle(aga_doors)
world.random.shuffle(aga_doors)
aga_door = aga_doors.pop()
if aga_door in hc_ledge_entrances:
lw_entrances.remove(aga_door)
hc_ledge_entrances.remove(aga_door)
random.shuffle(hc_ledge_entrances)
world.random.shuffle(hc_ledge_entrances)
hc_ledge_must_exit = hc_ledge_entrances.pop()
lw_entrances.remove(hc_ledge_must_exit)
lw_dungeon_entrances_must_exit.append(hc_ledge_must_exit)
@ -1162,8 +1174,8 @@ def link_inverted_entrances(world, player):
# we shuffle all 2 entrance caves as pairs as a start
# start with the ones that need to be directed
two_door_caves = list(Inverted_Two_Door_Caves_Directional)
random.shuffle(two_door_caves)
random.shuffle(caves)
world.random.shuffle(two_door_caves)
world.random.shuffle(caves)
while two_door_caves:
entrance1, entrance2 = two_door_caves.pop()
exit1, exit2 = caves.pop()
@ -1172,7 +1184,7 @@ def link_inverted_entrances(world, player):
# now the remaining pairs
two_door_caves = list(Inverted_Two_Door_Caves)
random.shuffle(two_door_caves)
world.random.shuffle(two_door_caves)
while two_door_caves:
entrance1, entrance2 = two_door_caves.pop()
exit1, exit2 = caves.pop()
@ -1180,8 +1192,9 @@ def link_inverted_entrances(world, player):
connect_two_way(world, entrance2, exit2, player)
# place links house
links_house_doors = [i for i in bomb_shop_doors + blacksmith_doors if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
links_house = random.choice(list(links_house_doors))
links_house_doors = [i for i in bomb_shop_doors + blacksmith_doors if
i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
links_house = world.random.choice(list(links_house_doors))
connect_two_way(world, links_house, 'Inverted Links House Exit', player)
if links_house in bomb_shop_doors:
bomb_shop_doors.remove(links_house)
@ -1192,7 +1205,7 @@ def link_inverted_entrances(world, player):
# place dark sanc
sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in bomb_shop_doors]
sanc_door = random.choice(sanc_doors)
sanc_door = world.random.choice(sanc_doors)
bomb_shop_doors.remove(sanc_door)
connect_entrance(world, sanc_door, 'Inverted Dark Sanctuary', player)
@ -1205,7 +1218,7 @@ def link_inverted_entrances(world, player):
# place old man, bumper cave bottom to DDM entrances not in east bottom
random.shuffle(old_man_entrances)
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
connect_two_way(world, 'Bumper Cave (Bottom)', 'Old Man Cave Exit (West)', player)
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
@ -1225,14 +1238,14 @@ def link_inverted_entrances(world, player):
# place blacksmith, has limited options
blacksmith_doors = [door for door in blacksmith_doors[:]]
random.shuffle(blacksmith_doors)
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
bomb_shop_doors.extend(blacksmith_doors)
# place bomb shop, has limited options
bomb_shop_doors = [door for door in bomb_shop_doors[:]]
random.shuffle(bomb_shop_doors)
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player)
single_doors.extend(bomb_shop_doors)
@ -1257,8 +1270,9 @@ def link_inverted_entrances(world, player):
door_targets = list(Inverted_Single_Cave_Targets)
# place links house
links_house_doors = [i for i in lw_entrances + dw_entrances + lw_must_exits if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
links_house = random.choice(list(links_house_doors))
links_house_doors = [i for i in lw_entrances + dw_entrances + lw_must_exits if
i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
links_house = world.random.choice(list(links_house_doors))
connect_two_way(world, links_house, 'Inverted Links House Exit', player)
if links_house in lw_entrances:
lw_entrances.remove(links_house)
@ -1269,7 +1283,7 @@ def link_inverted_entrances(world, player):
# place dark sanc
sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in dw_entrances]
sanc_door = random.choice(sanc_doors)
sanc_door = world.random.choice(sanc_doors)
dw_entrances.remove(sanc_door)
connect_entrance(world, sanc_door, 'Inverted Dark Sanctuary', player)
world.get_entrance('Inverted Dark Sanctuary Exit', player).connect(world.get_entrance(sanc_door, player).parent_region)
@ -1283,7 +1297,7 @@ def link_inverted_entrances(world, player):
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
old_man_entrances = [door for door in old_man_entrances if door in dw_entrances]
random.shuffle(old_man_entrances)
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
dw_entrances.remove(old_man_exit)
@ -1292,7 +1306,7 @@ def link_inverted_entrances(world, player):
all_entrances = lw_entrances + dw_entrances
# cannot place it anywhere already taken (or that are otherwise not eligible for placement)
blacksmith_doors = [door for door in blacksmith_doors if door in all_entrances]
random.shuffle(blacksmith_doors)
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
if blacksmith_hut in lw_entrances:
@ -1305,7 +1319,7 @@ def link_inverted_entrances(world, player):
all_entrances = lw_entrances + dw_entrances
# cannot place it anywhere already taken (or that are otherwise not eligible for placement)
bomb_shop_doors = [door for door in bomb_shop_doors if door in all_entrances]
random.shuffle(bomb_shop_doors)
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player)
if bomb_shop in lw_entrances:
@ -1314,7 +1328,7 @@ def link_inverted_entrances(world, player):
dw_entrances.remove(bomb_shop)
# place the old man cave's entrance somewhere in the dark world
random.shuffle(dw_entrances)
world.random.shuffle(dw_entrances)
old_man_entrance = dw_entrances.pop()
connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player)
@ -1341,7 +1355,7 @@ def link_inverted_entrances(world, player):
old_man_house = list(Old_Man_House)
# randomize which desert ledge door is a must-exit
if random.randint(0, 1) == 0:
if world.random.randint(0, 1) == 0:
lw_must_exits.append('Desert Palace Entrance (North)')
lw_entrances.append('Desert Palace Entrance (West)')
else:
@ -1351,7 +1365,8 @@ def link_inverted_entrances(world, player):
# tavern back door cannot be shuffled yet
connect_doors(world, ['Tavern North'], ['Tavern'], player)
caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3)))
caves.append(tuple(world.random.sample(
['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3)))
lw_entrances.append('Hyrule Castle Entrance (South)')
@ -1366,14 +1381,14 @@ def link_inverted_entrances(world, player):
# shuffle aga door first. if it's on hc ledge, then one other hc ledge door has to be must_exit
all_entrances_aga = lw_entrances + dw_entrances
aga_doors = [i for i in all_entrances_aga]
random.shuffle(aga_doors)
world.random.shuffle(aga_doors)
aga_door = aga_doors.pop()
if aga_door in hc_ledge_entrances:
lw_entrances.remove(aga_door)
hc_ledge_entrances.remove(aga_door)
random.shuffle(hc_ledge_entrances)
world.random.shuffle(hc_ledge_entrances)
hc_ledge_must_exit = hc_ledge_entrances.pop()
lw_entrances.remove(hc_ledge_must_exit)
lw_must_exits.append(hc_ledge_must_exit)
@ -1387,8 +1402,9 @@ def link_inverted_entrances(world, player):
caves.remove('Inverted Agahnims Tower Exit')
# place links house
links_house_doors = [i for i in lw_entrances + dw_entrances + lw_must_exits if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
links_house = random.choice(list(links_house_doors))
links_house_doors = [i for i in lw_entrances + dw_entrances + lw_must_exits if
i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
links_house = world.random.choice(list(links_house_doors))
connect_two_way(world, links_house, 'Inverted Links House Exit', player)
if links_house in lw_entrances:
lw_entrances.remove(links_house)
@ -1399,14 +1415,14 @@ def link_inverted_entrances(world, player):
# place dark sanc
sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in dw_entrances]
sanc_door = random.choice(sanc_doors)
sanc_door = world.random.choice(sanc_doors)
dw_entrances.remove(sanc_door)
connect_entrance(world, sanc_door, 'Inverted Dark Sanctuary', player)
world.get_entrance('Inverted Dark Sanctuary Exit', player).connect(world.get_entrance(sanc_door, player).parent_region)
# place old man house
# no dw must exits in inverted, but we randomize whether cave is in light or dark world
if random.randint(0, 1) == 0:
if world.random.randint(0, 1) == 0:
caves += old_man_house
connect_mandatory_exits(world, lw_entrances, caves, lw_must_exits, player)
try:
@ -1422,7 +1438,7 @@ def link_inverted_entrances(world, player):
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
old_man_entrances = [door for door in old_man_entrances if door in dw_entrances + lw_entrances]
random.shuffle(old_man_entrances)
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
if old_man_exit in dw_entrances:
@ -1436,7 +1452,7 @@ def link_inverted_entrances(world, player):
all_entrances = lw_entrances + dw_entrances
# cannot place it anywhere already taken (or that are otherwise not eligible for placement)
blacksmith_doors = [door for door in blacksmith_doors if door in all_entrances]
random.shuffle(blacksmith_doors)
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
if blacksmith_hut in lw_entrances:
@ -1449,7 +1465,7 @@ def link_inverted_entrances(world, player):
all_entrances = lw_entrances + dw_entrances
# cannot place it anywhere already taken (or that are otherwise not eligible for placement)
bomb_shop_doors = [door for door in bomb_shop_doors if door in all_entrances]
random.shuffle(bomb_shop_doors)
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player)
if bomb_shop in lw_entrances:
@ -1459,11 +1475,11 @@ def link_inverted_entrances(world, player):
# place the old man cave's entrance somewhere in the same world he'll exit from
if old_man_world == 'light':
random.shuffle(lw_entrances)
world.random.shuffle(lw_entrances)
old_man_entrance = lw_entrances.pop()
connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player)
elif old_man_world == 'dark':
random.shuffle(dw_entrances)
world.random.shuffle(dw_entrances)
old_man_entrance = dw_entrances.pop()
connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player)
@ -1490,14 +1506,15 @@ def link_inverted_entrances(world, player):
door_targets = list(Inverted_Single_Cave_Targets)
# randomize which desert ledge door is a must-exit
if random.randint(0, 1) == 0:
if world.random.randint(0, 1) == 0:
must_exits.append('Desert Palace Entrance (North)')
entrances.append('Desert Palace Entrance (West)')
else:
must_exits.append('Desert Palace Entrance (West)')
entrances.append('Desert Palace Entrance (North)')
caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'],3)))
caves.append(tuple(world.random.sample(
['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3)))
entrances.append('Hyrule Castle Entrance (South)')
if not world.shuffle_ganon:
@ -1509,12 +1526,12 @@ def link_inverted_entrances(world, player):
hc_ledge_entrances = ['Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)', 'Inverted Ganons Tower']
# shuffle aga door. if it's on hc ledge, then one other hc ledge door has to be must_exit
aga_door = random.choice(list(entrances))
aga_door = world.random.choice(list(entrances))
if aga_door in hc_ledge_entrances:
hc_ledge_entrances.remove(aga_door)
random.shuffle(hc_ledge_entrances)
world.random.shuffle(hc_ledge_entrances)
hc_ledge_must_exit = hc_ledge_entrances.pop()
entrances.remove(hc_ledge_must_exit)
must_exits.append(hc_ledge_must_exit)
@ -1525,8 +1542,9 @@ def link_inverted_entrances(world, player):
# place links house
links_house_doors = [i for i in entrances + must_exits if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
links_house = random.choice(list(links_house_doors))
links_house_doors = [i for i in entrances + must_exits if
i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
links_house = world.random.choice(list(links_house_doors))
connect_two_way(world, links_house, 'Inverted Links House Exit', player)
if links_house in entrances:
entrances.remove(links_house)
@ -1535,7 +1553,7 @@ def link_inverted_entrances(world, player):
# place dark sanc
sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in entrances]
sanc_door = random.choice(sanc_doors)
sanc_door = world.random.choice(sanc_doors)
entrances.remove(sanc_door)
connect_entrance(world, sanc_door, 'Inverted Dark Sanctuary', player)
world.get_entrance('Inverted Dark Sanctuary Exit', player).connect(world.get_entrance(sanc_door, player).parent_region)
@ -1551,7 +1569,7 @@ def link_inverted_entrances(world, player):
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
old_man_entrances = [door for door in old_man_entrances if door in entrances]
random.shuffle(old_man_entrances)
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player)
entrances.remove(old_man_exit)
@ -1559,7 +1577,7 @@ def link_inverted_entrances(world, player):
# place blacksmith, has limited options
# cannot place it anywhere already taken (or that are otherwise not eligible for placement)
blacksmith_doors = [door for door in blacksmith_doors if door in entrances]
random.shuffle(blacksmith_doors)
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
entrances.remove(blacksmith_hut)
@ -1568,13 +1586,13 @@ def link_inverted_entrances(world, player):
# cannot place it anywhere already taken (or that are otherwise not eligible for placement)
bomb_shop_doors = [door for door in bomb_shop_doors if door in entrances]
random.shuffle(bomb_shop_doors)
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player)
entrances.remove(bomb_shop)
# place the old man cave's entrance somewhere
random.shuffle(entrances)
world.random.shuffle(entrances)
old_man_entrance = entrances.pop()
connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player)
@ -1597,7 +1615,7 @@ def link_inverted_entrances(world, player):
Inverted_LW_Single_Cave_Doors + Inverted_DW_Single_Cave_Doors + ['Desert Palace Entrance (West)', 'Desert Palace Entrance (North)']
# randomize which desert ledge door is a must-exit
if random.randint(0, 1) == 0:
if world.random.randint(0, 1) == 0:
entrances_must_exits.append('Desert Palace Entrance (North)')
entrances.append('Desert Palace Entrance (West)')
else:
@ -1612,7 +1630,7 @@ def link_inverted_entrances(world, player):
blacksmith_doors = list(Blacksmith_Single_Cave_Doors + Inverted_Blacksmith_Multi_Cave_Doors)
door_targets = list(Inverted_Single_Cave_Targets)
random.shuffle(doors)
world.random.shuffle(doors)
old_man_entrances = list(Inverted_Old_Man_Entrances + Old_Man_Entrances) + ['Tower of Hera', 'Inverted Agahnims Tower']
@ -1646,9 +1664,9 @@ def link_inverted_entrances(world, player):
hole_targets.append('Pyramid')
doors.extend(['Inverted Ganons Tower', 'Inverted Pyramid Entrance'])
random.shuffle(hole_entrances)
random.shuffle(hole_targets)
random.shuffle(entrances)
world.random.shuffle(hole_entrances)
world.random.shuffle(hole_targets)
world.random.shuffle(entrances)
# fill up holes
for hole in hole_entrances:
@ -1658,8 +1676,9 @@ def link_inverted_entrances(world, player):
caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'))
# place links house and dark sanc
links_house_doors = [i for i in entrances + entrances_must_exits if i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
links_house = random.choice(list(links_house_doors))
links_house_doors = [i for i in entrances + entrances_must_exits if
i not in Inverted_Dark_Sanctuary_Doors + Isolated_LH_Doors]
links_house = world.random.choice(list(links_house_doors))
connect_two_way(world, links_house, 'Inverted Links House Exit', player)
if links_house in entrances:
entrances.remove(links_house)
@ -1668,7 +1687,7 @@ def link_inverted_entrances(world, player):
doors.remove(links_house)
sanc_doors = [door for door in Inverted_Dark_Sanctuary_Doors if door in entrances]
sanc_door = random.choice(sanc_doors)
sanc_door = world.random.choice(sanc_doors)
entrances.remove(sanc_door)
doors.remove(sanc_door)
connect_entrance(world, sanc_door, 'Inverted Dark Sanctuary', player)
@ -1676,7 +1695,7 @@ def link_inverted_entrances(world, player):
# now let's deal with mandatory reachable stuff
def extract_reachable_exit(cavelist):
random.shuffle(cavelist)
world.random.shuffle(cavelist)
candidate = None
for cave in cavelist:
if isinstance(cave, tuple) and len(cave) > 1:
@ -1708,7 +1727,7 @@ def link_inverted_entrances(world, player):
# place old man, has limited options
# exit has to come from specific set of doors, the entrance is free to move about
old_man_entrances = [entrance for entrance in old_man_entrances if entrance in entrances]
random.shuffle(old_man_entrances)
world.random.shuffle(old_man_entrances)
old_man_exit = old_man_entrances.pop()
entrances.remove(old_man_exit)
@ -1718,14 +1737,14 @@ def link_inverted_entrances(world, player):
# place blacksmith, has limited options
blacksmith_doors = [door for door in blacksmith_doors if door in doors]
random.shuffle(blacksmith_doors)
world.random.shuffle(blacksmith_doors)
blacksmith_hut = blacksmith_doors.pop()
connect_entrance(world, blacksmith_hut, 'Blacksmiths Hut', player)
doors.remove(blacksmith_hut)
# place dam and pyramid fairy, have limited options
bomb_shop_doors = [door for door in bomb_shop_doors if door in doors]
random.shuffle(bomb_shop_doors)
world.random.shuffle(bomb_shop_doors)
bomb_shop = bomb_shop_doors.pop()
connect_entrance(world, bomb_shop, 'Inverted Big Bomb Shop', player)
doors.remove(bomb_shop)
@ -1843,14 +1862,14 @@ def scramble_holes(world, player):
if world.shuffle[player] == 'crossed':
hole_targets.append(('Sanctuary Exit', 'Sewer Drop'))
if world.shuffle_ganon:
random.shuffle(hole_targets)
world.random.shuffle(hole_targets)
exit, target = hole_targets.pop()
connect_two_way(world, 'Pyramid Entrance', exit, player)
connect_entrance(world, 'Pyramid Hole', target, player)
if world.shuffle[player] != 'crossed':
hole_targets.append(('Sanctuary Exit', 'Sewer Drop'))
random.shuffle(hole_targets)
world.random.shuffle(hole_targets)
for entrance, drop in hole_entrances:
exit, target = hole_targets.pop()
connect_two_way(world, entrance, exit, player)
@ -1885,14 +1904,14 @@ def scramble_inverted_holes(world, player):
if world.shuffle[player] == 'crossed':
hole_targets.append(('Sanctuary Exit', 'Sewer Drop'))
if world.shuffle_ganon:
random.shuffle(hole_targets)
world.random.shuffle(hole_targets)
exit, target = hole_targets.pop()
connect_two_way(world, 'Inverted Pyramid Entrance', exit, player)
connect_entrance(world, 'Inverted Pyramid Hole', target, player)
if world.shuffle[player] != 'crossed':
hole_targets.append(('Sanctuary Exit', 'Sewer Drop'))
random.shuffle(hole_targets)
world.random.shuffle(hole_targets)
for entrance, drop in hole_entrances:
exit, target = hole_targets.pop()
connect_two_way(world, entrance, exit, player)
@ -1900,7 +1919,7 @@ def scramble_inverted_holes(world, player):
def connect_random(world, exitlist, targetlist, player, two_way=False):
targetlist = list(targetlist)
random.shuffle(targetlist)
world.random.shuffle(targetlist)
for exit, target in zip(exitlist, targetlist):
if two_way:
@ -1927,15 +1946,17 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player):
entrances.append(entrance)
"""This works inplace"""
random.shuffle(entrances)
random.shuffle(caves)
world.random.shuffle(entrances)
world.random.shuffle(caves)
# Handle inverted Aga Tower - if it depends on connections, then so does Hyrule Castle Ledge
if world.mode[player] == 'inverted':
for entrance in invalid_connections:
if world.get_entrance(entrance, player).connected_region == world.get_region('Inverted Agahnims Tower', player):
if world.get_entrance(entrance, player).connected_region == world.get_region('Inverted Agahnims Tower',
player):
for exit in invalid_connections[entrance]:
invalid_connections[exit] = invalid_connections[exit].union({'Inverted Ganons Tower', 'Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)'})
invalid_connections[exit] = invalid_connections[exit].union(
{'Inverted Ganons Tower', 'Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)'})
break
used_caves = []
@ -1984,7 +2005,7 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player):
else:
required_entrances += len(cave)-1
caves.append(cave[0:-1])
random.shuffle(caves)
world.random.shuffle(caves)
used_caves.append(cave[0:-1])
invalid_cave_connections[tuple(cave[0:-1])] = invalid_cave_connections[tuple(cave)].union(invalid_connections[exit])
caves.remove(cave)
@ -2000,9 +2021,9 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player):
def connect_caves(world, lw_entrances, dw_entrances, caves, player):
"""This works inplace"""
random.shuffle(lw_entrances)
random.shuffle(dw_entrances)
random.shuffle(caves)
world.random.shuffle(lw_entrances)
world.random.shuffle(dw_entrances)
world.random.shuffle(caves)
while caves:
# connect highest exit count caves first, prevent issue where we have 2 or 3 exits accross worlds left to fill
cave_candidate = (None, 0)
@ -2013,7 +2034,7 @@ def connect_caves(world, lw_entrances, dw_entrances, caves, player):
cave_candidate = (i, len(cave))
cave = caves.pop(cave_candidate[0])
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
target = lw_entrances if world.random.randint(0, 1) == 0 else dw_entrances
if isinstance(cave, str):
cave = (cave,)
@ -2028,8 +2049,8 @@ def connect_caves(world, lw_entrances, dw_entrances, caves, player):
def connect_doors(world, doors, targets, player):
"""This works inplace"""
random.shuffle(doors)
random.shuffle(targets)
world.random.shuffle(doors)
world.random.shuffle(targets)
while doors:
door = doors.pop()
target = targets.pop()
@ -2066,7 +2087,7 @@ def simple_shuffle_dungeons(world, player):
multi_dungeons = ['Desert', 'Turtle Rock']
if world.mode[player] == 'open' or (world.mode[player] == 'inverted' and world.shuffle_ganon):
multi_dungeons.append('Hyrule Castle')
random.shuffle(multi_dungeons)
world.random.shuffle(multi_dungeons)
dp_target = multi_dungeons[0]
tr_target = multi_dungeons[1]
@ -2175,11 +2196,12 @@ def simple_shuffle_dungeons(world, player):
connect_two_way(world, 'Dark Death Mountain Ledge (West)', 'Turtle Rock Ledge Exit (West)', player)
connect_two_way(world, 'Dark Death Mountain Ledge (East)', 'Turtle Rock Ledge Exit (East)', player)
def unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits):
def unbias_some_entrances(world, Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits):
def shuffle_lists_in_list(ls):
for i, item in enumerate(ls):
if isinstance(item, list):
ls[i] = random.sample(item, len(item))
ls[i] = world.random.sample(item, len(item))
def tuplize_lists_in_list(ls):
for i, item in enumerate(ls):
@ -2193,7 +2215,7 @@ def unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_E
# paradox fixup
if Cave_Three_Exits[1][0] == "Paradox Cave Exit (Bottom)":
i = random.randint(1,2)
i = world.random.randint(1, 2)
Cave_Three_Exits[1][0] = Cave_Three_Exits[1][i]
Cave_Three_Exits[1][i] = "Paradox Cave Exit (Bottom)"

39
Fill.py
View File

@ -1,4 +1,3 @@
import random
import logging
from BaseClasses import CollectionState
@ -10,10 +9,10 @@ class FillError(RuntimeError):
def distribute_items_cutoff(world, cutoffrate=0.33):
# get list of locations to fill in
fill_locations = world.get_unfilled_locations()
random.shuffle(fill_locations)
world.random.shuffle(fill_locations)
# get items to distribute
random.shuffle(world.itempool)
world.random.shuffle(world.itempool)
itempool = world.itempool
total_advancement_items = len([item for item in itempool if item.advancement])
@ -83,10 +82,10 @@ def distribute_items_cutoff(world, cutoffrate=0.33):
def distribute_items_staleness(world):
# get list of locations to fill in
fill_locations = world.get_unfilled_locations()
random.shuffle(fill_locations)
world.random.shuffle(fill_locations)
# get items to distribute
random.shuffle(world.itempool)
world.random.shuffle(world.itempool)
itempool = world.itempool
progress_done = False
@ -131,7 +130,7 @@ def distribute_items_staleness(world):
spot_to_fill = None
for location in fill_locations:
# increase likelyhood of skipping a location if it has been found stale
if not progress_done and random.randint(0, location.staleness_count) > 2:
if not progress_done and world.random.randint(0, location.staleness_count) > 2:
continue
if location.can_fill(world.state, item_to_place):
@ -215,10 +214,10 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
# If not passed in, then get a shuffled list of locations to fill in
if not fill_locations:
fill_locations = world.get_unfilled_locations()
random.shuffle(fill_locations)
world.random.shuffle(fill_locations)
# get items to distribute
random.shuffle(world.itempool)
world.random.shuffle(world.itempool)
progitempool = []
localprioitempool = {player: [] for player in range(1, world.players + 1)}
localrestitempool = {player: [] for player in range(1, world.players + 1)}
@ -244,17 +243,17 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
continue
gftower_trash_count = (
random.randint(15, 50) if 'triforcehunt' in world.goal[player]
else random.randint(0, 15))
world.random.randint(15, 50) if 'triforcehunt' in world.goal[player]
else world.random.randint(0, 15))
gtower_locations = [location for location in fill_locations if
'Ganons Tower' in location.name and location.player == player]
random.shuffle(gtower_locations)
world.random.shuffle(gtower_locations)
trashcnt = 0
localrest = localrestitempool[player]
if localrest:
gt_item_pool = restitempool + localrest
random.shuffle(gt_item_pool)
world.random.shuffle(gt_item_pool)
else:
gt_item_pool = restitempool.copy()
@ -269,7 +268,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
fill_locations.remove(spot_to_fill)
trashcnt += 1
random.shuffle(fill_locations)
world.random.shuffle(fill_locations)
fill_locations.reverse()
# Make sure the escape small key is placed first in standard with key shuffle to prevent running out of spots
@ -283,20 +282,20 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
localprioitempool.values() or localrestitempool.values()): # we need to make sure some fills are limited to certain worlds
for player, items in localprioitempool.items(): # items already shuffled
local_locations = [location for location in fill_locations if location.player == player]
random.shuffle(local_locations)
world.random.shuffle(local_locations)
for item_to_place in items:
spot_to_fill = local_locations.pop()
world.push_item(spot_to_fill, item_to_place, False)
fill_locations.remove(spot_to_fill)
for player, items in localrestitempool.items(): # items already shuffled
local_locations = [location for location in fill_locations if location.player == player]
random.shuffle(local_locations)
world.random.shuffle(local_locations)
for item_to_place in items:
spot_to_fill = local_locations.pop()
world.push_item(spot_to_fill, item_to_place, False)
fill_locations.remove(spot_to_fill)
random.shuffle(fill_locations)
world.random.shuffle(fill_locations)
fast_fill(world, prioitempool, fill_locations)
@ -314,7 +313,7 @@ def fast_fill(world, item_pool, fill_locations):
def flood_items(world):
# get items to distribute
random.shuffle(world.itempool)
world.random.shuffle(world.itempool)
itempool = world.itempool
progress_done = False
@ -324,7 +323,7 @@ def flood_items(world):
# fill world from top of itempool while we can
while not progress_done:
location_list = world.get_unfilled_locations()
random.shuffle(location_list)
world.random.shuffle(location_list)
spot_to_fill = None
for location in location_list:
if location.can_fill(world.state, itempool[0]):
@ -360,7 +359,7 @@ def flood_items(world):
# find item to replace with progress item
location_list = world.get_reachable_locations()
random.shuffle(location_list)
world.random.shuffle(location_list)
for location in location_list:
if location.item is not None and not location.item.advancement and not location.item.priority and not location.item.smallkey and not location.item.bigkey:
# safe to replace
@ -380,7 +379,7 @@ def balance_multiworld_progression(world):
state = CollectionState(world)
checked_locations = []
unchecked_locations = world.get_locations().copy()
random.shuffle(unchecked_locations)
world.random.shuffle(unchecked_locations)
reachable_locations_count = {player: 0 for player in range(1, world.players + 1)}

View File

@ -1,6 +1,5 @@
from collections import namedtuple
import logging
import random
from BaseClasses import Region, RegionType, Shop, ShopType, Location
from Bosses import place_bosses
@ -181,10 +180,7 @@ def generate_itempool(world, player):
# set up item pool
if world.custom:
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon,
lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive[player], world.shuffle[player],
world.difficulty[player], world.timer[player], world.goal[player],
world.mode[player], world.swords[player],
world.retro[player], world.customitemarray)
lamps_needed_for_dark_rooms) = make_custom_item_pool(world, player)
world.rupoor_cost = min(world.customitemarray[69], 9999)
else:
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon,
@ -209,7 +205,7 @@ def generate_itempool(world, player):
if item in ['Hammer', 'Bombs (10)', 'Fire Rod', 'Cane of Somaria', 'Cane of Byrna']:
if item not in possible_weapons:
possible_weapons.append(item)
starting_weapon = random.choice(possible_weapons)
starting_weapon = world.random.choice(possible_weapons)
placed_items["Link's Uncle"] = starting_weapon
pool.remove(starting_weapon)
if placed_items["Link's Uncle"] in ['Bow', 'Progressive Bow', 'Bombs (10)', 'Cane of Somaria', 'Cane of Byrna'] and world.enemy_health[player] not in ['default', 'easy']:
@ -255,13 +251,14 @@ def generate_itempool(world, player):
4: {'trap': 100}}
def beemizer(item):
if world.beemizer[item.player] and not item.advancement and not item.priority and not item.type:
choice = random.choices(list(beeweights[world.beemizer[item.player]].keys()), weights=list(beeweights[world.beemizer[item.player]].values()))[0]
choice = world.random.choices(list(beeweights[world.beemizer[item.player]].keys()),
weights=list(beeweights[world.beemizer[item.player]].values()))[0]
return item if not choice else ItemFactory("Bee Trap", player) if choice == 'trap' else ItemFactory("Bee", player)
return item
progressionitems = [item for item in items if item.advancement or item.priority or item.type]
nonprogressionitems = [beemizer(item) for item in items if not item.advancement and not item.priority and not item.type]
random.shuffle(nonprogressionitems)
world.random.shuffle(nonprogressionitems)
triforce_pieces = world.triforce_pieces_available[player]
if 'triforcehunt' in world.goal[player] and triforce_pieces > 30:
@ -271,8 +268,8 @@ def generate_itempool(world, player):
world.itempool += progressionitems + nonprogressionitems
# shuffle medallions
mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
mm_medallion = ['Ether', 'Quake', 'Bombos'][world.random.randint(0, 2)]
tr_medallion = ['Ether', 'Quake', 'Bombos'][world.random.randint(0, 2)]
world.required_medallions[player] = (mm_medallion, tr_medallion)
place_bosses(world, player)
@ -297,7 +294,7 @@ def set_up_take_anys(world, player):
if world.mode[player] == 'inverted' and 'Dark Sanctuary Hint' in take_any_locations:
take_any_locations.remove('Dark Sanctuary Hint')
regions = random.sample(take_any_locations, 5)
regions = world.random.sample(take_any_locations, 5)
old_man_take_any = Region("Old Man Sword Cave", RegionType.Cave, 'the sword cave', player)
world.regions.append(old_man_take_any)
@ -312,7 +309,7 @@ def set_up_take_anys(world, player):
swords = [item for item in world.itempool if item.type == 'Sword' and item.player == player]
if swords:
sword = random.choice(swords)
sword = world.random.choice(swords)
world.itempool.remove(sword)
world.itempool.append(ItemFactory('Rupees (20)', player))
old_man_take_any.shop.add_inventory(0, sword.name, 0, 0, create_location=True)
@ -324,7 +321,7 @@ def set_up_take_anys(world, player):
world.regions.append(take_any)
world.dynamic_regions.append(take_any)
target, room_id = random.choice([(0x58, 0x0112), (0x60, 0x010F), (0x46, 0x011F)])
target, room_id = world.random.choice([(0x58, 0x0112), (0x60, 0x010F), (0x46, 0x011F)])
reg = regions.pop()
entrance = world.get_region(reg, player).entrances[0]
connect_entrance(world, entrance.name, take_any.name, player)
@ -368,8 +365,8 @@ def fill_prizes(world, attempts=15):
try:
prizepool = list(unplaced_prizes)
prize_locs = list(empty_crystal_locations)
random.shuffle(prizepool)
random.shuffle(prize_locs)
world.random.shuffle(prizepool)
world.random.shuffle(prize_locs)
fill_restrictive(world, all_state, prize_locs, prizepool, True)
except FillError as e:
logging.getLogger('').exception("Failed to place dungeon prizes (%s). Will retry %s more times", e,
@ -389,7 +386,9 @@ def set_up_shops(world, player):
rss = world.get_region('Red Shield Shop', player).shop
if not rss.locked:
rss.add_inventory(2, 'Single Arrow', 80)
for shop in random.sample([s for s in world.shops if s.custom and not s.locked and s.type == ShopType.Shop and s.region.player == player], 5):
for shop in world.random.sample([s for s in world.shops if
s.custom and not s.locked and s.type == ShopType.Shop and s.region.player == player],
5):
shop.locked = True
shop.add_inventory(0, 'Single Arrow', 80)
shop.add_inventory(1, 'Small Key (Universal)', 100)
@ -422,7 +421,7 @@ def get_pool_core(world, player: int):
placed_items[loc] = item
def want_progressives():
return random.choice([True, False]) if progressive == 'random' else progressive == 'on'
return world.random.choice([True, False]) if progressive == 'random' else progressive == 'on'
# provide boots to major glitch dependent seeds
if logic in {'owglitches', 'nologic'} and world.glitch_boots[player]:
@ -455,10 +454,10 @@ def get_pool_core(world, player: int):
# expert+ difficulties produce the same contents for
# all bottles, since only one bottle is available
if diff.same_bottle:
thisbottle = random.choice(diff.bottles)
thisbottle = world.random.choice(diff.bottles)
for _ in range(diff.bottle_count):
if not diff.same_bottle:
thisbottle = random.choice(diff.bottles)
thisbottle = world.random.choice(diff.bottles)
pool.append(thisbottle)
if want_progressives():
@ -482,7 +481,7 @@ def get_pool_core(world, player: int):
pool.extend(diff.swordless)
elif swords == 'vanilla':
swords_to_use = diff.progressivesword.copy() if want_progressives() else diff.basicsword.copy()
random.shuffle(swords_to_use)
world.random.shuffle(swords_to_use)
place_item('Link\'s Uncle', swords_to_use.pop())
place_item('Blacksmith', swords_to_use.pop())
@ -535,13 +534,24 @@ def get_pool_core(world, player: int):
pool = [item.replace('Arrow Upgrade (+10)','Rupees (5)') for item in pool]
pool.extend(diff.retro)
if mode == 'standard':
key_location = random.choice(['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross'])
key_location = world.random.choice(
['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest',
'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross'])
place_item(key_location, 'Small Key (Universal)')
else:
pool.extend(['Small Key (Universal)'])
return (pool, placed_items, precollected_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, swords, retro, customitemarray):
def make_custom_item_pool(world, player):
shuffle = world.shuffle[player]
difficulty = world.difficulty[player]
timer = world.timer[player]
goal = world.goal[player]
mode = world.mode[player]
retro = world.retro[player]
customitemarray = world.customitemarray[player]
pool = []
placed_items = {}
precollected_items = []
@ -636,10 +646,10 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
# expert+ difficulties produce the same contents for
# all bottles, since only one bottle is available
if diff.same_bottle:
thisbottle = random.choice(diff.bottles)
thisbottle = world.random.choice(diff.bottles)
for _ in range(customitemarray[18]):
if not diff.same_bottle:
thisbottle = random.choice(diff.bottles)
thisbottle = world.random.choice(diff.bottles)
pool.append(thisbottle)
if customitemarray[66] > 0 or customitemarray[67] > 0:
@ -664,7 +674,9 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
if mode == 'standard':
if retro:
key_location = random.choice(['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross'])
key_location = world.random.choice(
['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest',
'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross'])
place_item(key_location, 'Small Key (Universal)')
pool.extend(['Small Key (Universal)'] * max((customitemarray[70] - 1), 0))
else:

18
Main.py
View File

@ -43,17 +43,25 @@ def main(args, seed=None):
world = World(args.multi, args.shuffle, args.logic, args.mode, args.swords, args.difficulty,
args.item_functionality, args.timer, args.progressive.copy(), args.goal, args.algorithm,
args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints)
logger = logging.getLogger('')
world.seed = get_seed(seed)
random.seed(world.seed)
if args.race:
world.secure()
else:
world.random.seed(world.seed)
world.remote_items = args.remote_items.copy()
world.mapshuffle = args.mapshuffle.copy()
world.compassshuffle = args.compassshuffle.copy()
world.keyshuffle = args.keyshuffle.copy()
world.bigkeyshuffle = args.bigkeyshuffle.copy()
world.crystals_needed_for_ganon = {player: random.randint(0, 7) if args.crystals_ganon[player] == 'random' else int(args.crystals_ganon[player]) for player in range(1, world.players + 1)}
world.crystals_needed_for_gt = {player: random.randint(0, 7) if args.crystals_gt[player] == 'random' else int(args.crystals_gt[player]) for player in range(1, world.players + 1)}
world.crystals_needed_for_ganon = {
player: world.random.randint(0, 7) if args.crystals_ganon[player] == 'random' else int(
args.crystals_ganon[player]) for player in range(1, world.players + 1)}
world.crystals_needed_for_gt = {
player: world.random.randint(0, 7) if args.crystals_gt[player] == 'random' else int(args.crystals_gt[player])
for player in range(1, world.players + 1)}
world.open_pyramid = args.openpyramid.copy()
world.boss_shuffle = args.shufflebosses.copy()
world.enemy_shuffle = args.shuffleenemies.copy()
@ -69,7 +77,7 @@ def main(args, seed=None):
world.triforce_pieces_required = args.triforce_pieces_required.copy()
world.progression_balancing = {player: not balance for player, balance in args.skip_progression_balancing.items()}
world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)}
world.rom_seeds = {player: world.random.randint(0, 999999999) for player in range(1, world.players + 1)}
logger.info('ALttP Berserker\'s Multiworld Version %s - Seed: %s\n', __version__, world.seed)
@ -134,7 +142,7 @@ def main(args, seed=None):
if args.algorithm in ['balanced', 'vt26'] or any(list(args.mapshuffle.values()) + list(args.compassshuffle.values()) +
list(args.keyshuffle.values()) + list(args.bigkeyshuffle.values())):
shuffled_locations = world.get_unfilled_locations()
random.shuffle(shuffled_locations)
world.random.shuffle(shuffled_locations)
fill_dungeons_restrictive(world, shuffled_locations)
else:
fill_dungeons(world)

View File

@ -12,6 +12,7 @@ import inspect
import weakref
import datetime
import threading
import random
import ModuleUpdate
@ -830,7 +831,6 @@ class ClientMessageProcessor(CommonCommandProcessor):
can_pay = points_available // self.ctx.hint_cost
else:
can_pay = 1000
import random
random.shuffle(not_found_hints)

View File

@ -56,6 +56,9 @@ def main(args=None):
seed = get_seed(args.seed)
random.seed(seed)
if args.race:
random.seed() # reset to time-based random source
seedname = "M" + (f"{random.randint(0, pow(10, seeddigits) - 1)}".zfill(seeddigits))
print(f"Generating mystery for {args.multi} player{'s' if args.multi > 1 else ''}, {seedname} Seed {seed}")

86
Rom.py
View File

@ -236,8 +236,9 @@ def patch_enemizer(world, player: int, rom: LocalRom, enemizercli, random_sprite
'BeesLevel': 0,
'RandomizeTileTrapPattern': world.enemy_shuffle[player] == 'chaos',
'RandomizeTileTrapFloorTile': False,
'AllowKillableThief': bool(random.randint(0, 1)) if 'thieves' in world.enemy_shuffle[player] else
'AllowKillableThief': bool(world.random.randint(0, 1)) if 'thieves' in world.enemy_shuffle[player] else
world.enemy_shuffle[player] != 'none',
# TODO: this is currently non-deterministic due to being called in a thread
'RandomizeSpriteOnHit': random_sprite_on_hit,
'DebugMode': False,
'DebugForceEnemy': False,
@ -289,7 +290,7 @@ def patch_enemizer(world, player: int, rom: LocalRom, enemizercli, random_sprite
if sprites:
while len(sprites) < 32:
sprites.extend(sprites)
random.shuffle(sprites)
world.random.shuffle(sprites)
for i, path in enumerate(sprites[:32]):
sprite = Sprite(path)
@ -1606,9 +1607,9 @@ def write_strings(rom, world, player, team):
if world.hints[player]:
tt['sign_north_of_links_house'] = '> Randomizer The telepathic tiles can have hints!'
hint_locations = HintLocations.copy()
random.shuffle(hint_locations)
world.random.shuffle(hint_locations)
all_entrances = [entrance for entrance in world.get_entrances() if entrance.player == player]
random.shuffle(all_entrances)
world.random.shuffle(all_entrances)
#First we take care of the one inconvenient dungeon in the appropriately simple shuffles.
entrances_to_hint = {}
@ -1683,12 +1684,12 @@ def write_strings(rom, world, player, team):
locations_to_hint = InconvenientLocations.copy()
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
locations_to_hint.extend(InconvenientVanillaLocations)
random.shuffle(locations_to_hint)
world.random.shuffle(locations_to_hint)
hint_count = 3 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 5
del locations_to_hint[hint_count:]
for location in locations_to_hint:
if location == 'Swamp Left':
if random.randint(0, 1) == 0:
if world.random.randint(0, 1) == 0:
first_item = hint_text(world.get_location('Swamp Palace - West Chest', player).item)
second_item = hint_text(world.get_location('Swamp Palace - Big Key Chest', player).item)
else:
@ -1697,7 +1698,7 @@ def write_strings(rom, world, player, team):
this_hint = ('The westmost chests in Swamp Palace contain ' + first_item + ' and ' + second_item + '.')
tt[hint_locations.pop(0)] = this_hint
elif location == 'Mire Left':
if random.randint(0, 1) == 0:
if world.random.randint(0, 1) == 0:
first_item = hint_text(world.get_location('Misery Mire - Compass Chest', player).item)
second_item = hint_text(world.get_location('Misery Mire - Big Key Chest', player).item)
else:
@ -1736,12 +1737,12 @@ def write_strings(rom, world, player, team):
items_to_hint.extend(SmallKeys)
if world.bigkeyshuffle[player]:
items_to_hint.extend(BigKeys)
random.shuffle(items_to_hint)
world.random.shuffle(items_to_hint)
hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 8
while hint_count > 0:
this_item = items_to_hint.pop(0)
this_location = world.find_items(this_item, player)
random.shuffle(this_location)
world.random.shuffle(this_location)
#This looks dumb but prevents hints for Skull Woods Pinball Room's key safely with any item pool.
if this_location:
if this_location[0].name == 'Skull Woods - Pinball Room':
@ -1753,7 +1754,7 @@ def write_strings(rom, world, player, team):
# All remaining hint slots are filled with junk hints. It is done this way to ensure the same junk hint isn't selected twice.
junk_hints = junk_texts.copy()
random.shuffle(junk_hints)
world.random.shuffle(junk_hints)
for location in hint_locations:
tt[location] = junk_hints.pop(0)
@ -1761,7 +1762,7 @@ def write_strings(rom, world, player, team):
silverarrows = world.find_items('Silver Bow', player)
random.shuffle(silverarrows)
world.random.shuffle(silverarrows)
silverarrow_hint = (' %s?' % hint_text(silverarrows[0]).replace('Ganon\'s', 'my')) if silverarrows else '?\nI think not!'
tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint
tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint
@ -1775,7 +1776,8 @@ def write_strings(rom, world, player, team):
tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint
if any(prog_bow_locs):
silverarrow_hint = (' %s?' % hint_text(random.choice(prog_bow_locs)).replace('Ganon\'s', 'my')) if progressive_silvers else '?\nI think not!'
silverarrow_hint = (' %s?' % hint_text(world.random.choice(prog_bow_locs)).replace('Ganon\'s',
'my')) if progressive_silvers else '?\nI think not!'
tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint
@ -1786,19 +1788,23 @@ def write_strings(rom, world, player, team):
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[player] == 1 else 'You need %d crystals to enter.') % world.crystals_needed_for_gt[player]
tt['sign_ganon'] = ('You need %d crystal to beat Ganon.' if world.crystals_needed_for_ganon[player] == 1 else 'You need %d crystals to beat Ganon.') % world.crystals_needed_for_ganon[player]
tt['sign_ganons_tower'] = ('You need %d crystal to enter.' if world.crystals_needed_for_gt[
player] == 1 else 'You need %d crystals to enter.') % \
world.crystals_needed_for_gt[player]
tt['sign_ganon'] = ('You need %d crystal to beat Ganon.' if world.crystals_needed_for_ganon[
player] == 1 else 'You need %d crystals to beat Ganon.') % \
world.crystals_needed_for_ganon[player]
if world.goal[player] in ['dungeons']:
tt['sign_ganon'] = 'You need to complete all the dungeons.'
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)]
tt['uncle_leaving_text'] = Uncle_texts[world.random.randint(0, len(Uncle_texts) - 1)]
tt['end_triforce'] = "{NOBORDER}\n" + Triforce_texts[world.random.randint(0, len(Triforce_texts) - 1)]
tt['bomb_shop_big_bomb'] = BombShop2_texts[world.random.randint(0, len(BombShop2_texts) - 1)]
# this is what shows after getting the green pendant item in rando
tt['sahasrahla_quest_have_master_sword'] = Sahasrahla2_texts[random.randint(0, len(Sahasrahla2_texts) - 1)]
tt['blind_by_the_light'] = Blind_texts[random.randint(0, len(Blind_texts) - 1)]
tt['sahasrahla_quest_have_master_sword'] = Sahasrahla2_texts[world.random.randint(0, len(Sahasrahla2_texts) - 1)]
tt['blind_by_the_light'] = Blind_texts[world.random.randint(0, len(Blind_texts) - 1)]
if world.goal[player] in ['triforcehunt', 'localtriforcehunt']:
tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Get the Triforce Pieces.'
@ -1807,14 +1813,15 @@ def write_strings(rom, world, player, team):
tt['sign_ganon'] = 'Go find the Triforce pieces with your friends... Ganon is invincible!'
else:
tt['sign_ganon'] = 'Go find the Triforce pieces... Ganon is invincible!'
tt['murahdahla'] = "Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n\n\n\n… … …\n\nWait! you can see me? I knew I should have\nhidden in a hollow tree. If you bring\n%d triforce pieces, I can reassemble it." % \
tt[
'murahdahla'] = "Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n\n\n\n… … …\n\nWait! you can see me? I knew I should have\nhidden in a hollow tree. If you bring\n%d triforce pieces, I can reassemble it." % \
world.treasure_hunt_count[player]
elif world.goal[player] in ['pedestal']:
tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Your goal is at the pedestal.'
tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.'
tt['sign_ganon'] = 'You need to get to the pedestal... Ganon is invincible!'
else:
tt['ganon_fall_in'] = Ganon1_texts[random.randint(0, len(Ganon1_texts) - 1)]
tt['ganon_fall_in'] = Ganon1_texts[world.random.randint(0, len(Ganon1_texts) - 1)]
tt['ganon_fall_in_alt'] = 'You cannot defeat me until you finish your goal!'
tt['ganon_phase_3_alt'] = 'Got wax in\nyour ears?\nI can not die!'
if world.goal[player] == 'ganontriforcehunt' and world.players > 1:
@ -1822,7 +1829,7 @@ def write_strings(rom, world, player, team):
elif world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']:
tt['sign_ganon'] = 'You need to find %d Triforce pieces to defeat Ganon.' % world.treasure_hunt_count[player]
tt['kakariko_tavern_fisherman'] = TavernMan_texts[random.randint(0, len(TavernMan_texts) - 1)]
tt['kakariko_tavern_fisherman'] = TavernMan_texts[world.random.randint(0, len(TavernMan_texts) - 1)]
pedestalitem = world.get_location('Master Sword Pedestal', player).item
pedestal_text = 'Some Hot Air' if pedestalitem is None else hint_text(pedestalitem, True) if pedestalitem.pedestal_hint_text is not None else 'Unknown Item'
@ -1853,33 +1860,38 @@ def write_strings(rom, world, player, team):
credits = Credits()
sickkiditem = world.get_location('Sick Kid', player).item
sickkiditem_text = random.choice(SickKid_texts) if sickkiditem is None or sickkiditem.sickkid_credit_text is None else sickkiditem.sickkid_credit_text
sickkiditem_text = world.random.choice(
SickKid_texts) if sickkiditem is None or sickkiditem.sickkid_credit_text is None else sickkiditem.sickkid_credit_text
zoraitem = world.get_location('King Zora', player).item
zoraitem_text = random.choice(Zora_texts) if zoraitem is None or zoraitem.zora_credit_text is None else zoraitem.zora_credit_text
zoraitem_text = world.random.choice(
Zora_texts) if zoraitem is None or zoraitem.zora_credit_text is None else zoraitem.zora_credit_text
magicshopitem = world.get_location('Potion Shop', player).item
magicshopitem_text = random.choice(MagicShop_texts) if magicshopitem is None or magicshopitem.magicshop_credit_text is None else magicshopitem.magicshop_credit_text
magicshopitem_text = world.random.choice(
MagicShop_texts) if magicshopitem is None or magicshopitem.magicshop_credit_text is None else magicshopitem.magicshop_credit_text
fluteboyitem = world.get_location('Flute Spot', player).item
fluteboyitem_text = random.choice(FluteBoy_texts) if fluteboyitem is None or fluteboyitem.fluteboy_credit_text is None else fluteboyitem.fluteboy_credit_text
fluteboyitem_text = world.random.choice(
FluteBoy_texts) if fluteboyitem is None or fluteboyitem.fluteboy_credit_text is None else fluteboyitem.fluteboy_credit_text
credits.update_credits_line('castle', 0, random.choice(KingsReturn_texts))
credits.update_credits_line('sanctuary', 0, random.choice(Sanctuary_texts))
credits.update_credits_line('castle', 0, world.random.choice(KingsReturn_texts))
credits.update_credits_line('sanctuary', 0, world.random.choice(Sanctuary_texts))
credits.update_credits_line('kakariko', 0, random.choice(Kakariko_texts).format(random.choice(Sahasrahla_names)))
credits.update_credits_line('desert', 0, random.choice(DesertPalace_texts))
credits.update_credits_line('hera', 0, random.choice(MountainTower_texts))
credits.update_credits_line('house', 0, random.choice(LinksHouse_texts))
credits.update_credits_line('kakariko', 0,
world.random.choice(Kakariko_texts).format(world.random.choice(Sahasrahla_names)))
credits.update_credits_line('desert', 0, world.random.choice(DesertPalace_texts))
credits.update_credits_line('hera', 0, world.random.choice(MountainTower_texts))
credits.update_credits_line('house', 0, world.random.choice(LinksHouse_texts))
credits.update_credits_line('zora', 0, zoraitem_text)
credits.update_credits_line('witch', 0, magicshopitem_text)
credits.update_credits_line('lumberjacks', 0, random.choice(Lumberjacks_texts))
credits.update_credits_line('lumberjacks', 0, world.random.choice(Lumberjacks_texts))
credits.update_credits_line('grove', 0, fluteboyitem_text)
credits.update_credits_line('well', 0, random.choice(WishingWell_texts))
credits.update_credits_line('smithy', 0, random.choice(Blacksmiths_texts))
credits.update_credits_line('well', 0, world.random.choice(WishingWell_texts))
credits.update_credits_line('smithy', 0, world.random.choice(Blacksmiths_texts))
credits.update_credits_line('kakariko2', 0, sickkiditem_text)
credits.update_credits_line('bridge', 0, random.choice(DeathMountain_texts))
credits.update_credits_line('woods', 0, random.choice(LostWoods_texts))
credits.update_credits_line('bridge', 0, world.random.choice(DeathMountain_texts))
credits.update_credits_line('woods', 0, world.random.choice(LostWoods_texts))
credits.update_credits_line('pedestal', 0, pedestal_credit_text)
(pointers, data) = credits.get_bytes()