implement secrets.SystemRandom() for --race
This commit is contained in:
parent
59a71dbb05
commit
93ecf5988b
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ import argparse
|
|||
import copy
|
||||
import os
|
||||
import logging
|
||||
import random
|
||||
import textwrap
|
||||
import shlex
|
||||
import sys
|
||||
|
|
File diff suppressed because it is too large
Load Diff
39
Fill.py
39
Fill.py
|
@ -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)}
|
||||
|
||||
|
|
64
ItemList.py
64
ItemList.py
|
@ -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,24 +251,25 @@ 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:
|
||||
progressionitems += [ItemFactory("Triforce Piece", player)] * (triforce_pieces - 30)
|
||||
nonprogressionitems = nonprogressionitems[(triforce_pieces-30):]
|
||||
nonprogressionitems = nonprogressionitems[(triforce_pieces - 30):]
|
||||
|
||||
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
18
Main.py
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
86
Rom.py
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue