Hack in Spanish

This commit is contained in:
Mike A. Trethewey
2020-03-09 02:25:25 -07:00
parent b25e46b160
commit ca7fc4d0f6
9 changed files with 186 additions and 37 deletions

3
CLI.py
View File

@@ -26,7 +26,7 @@ def parse_arguments(argv, no_defaults=False):
# get settings # get settings
settings = get_settings() settings = get_settings()
fish = BabelFish() fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en")
# we need to know how many players we have first # we need to know how many players we have first
parser = argparse.ArgumentParser(add_help=False) parser = argparse.ArgumentParser(add_help=False)
@@ -102,6 +102,7 @@ def parse_arguments(argv, no_defaults=False):
def get_settings(): def get_settings():
# set default settings # set default settings
settings = { settings = {
"lang": "en",
"retro": False, "retro": False,
"mode": "open", "mode": "open",
"logic": "noglitches", "logic": "noglitches",

View File

@@ -5,6 +5,9 @@ import operator as op
import time import time
from enum import unique, Flag from enum import unique, Flag
from source.classes.BabelFish import BabelFish
import CLI as cli
from functools import reduce from functools import reduce
from BaseClasses import RegionType, Door, DoorType, Direction, Sector, CrystalBarrier from BaseClasses import RegionType, Door, DoorType, Direction, Sector, CrystalBarrier
from Regions import key_only_locations from Regions import key_only_locations
@@ -305,6 +308,9 @@ def pair_existing_key_doors(world, player, door_a, door_b):
# paired_door.pair = False # paired_door.pair = False
def within_dungeon(world, player): def within_dungeon(world, player):
settings = cli.get_settings()
fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en")
fix_big_key_doors_with_ugly_smalls(world, player) fix_big_key_doors_with_ugly_smalls(world, player)
overworld_prep(world, player) overworld_prep(world, player)
entrances_map, potentials, connections = determine_entrance_list(world, player) entrances_map, potentials, connections = determine_entrance_list(world, player)
@@ -326,7 +332,7 @@ def within_dungeon(world, player):
start = time.process_time() start = time.process_time()
for builder in world.dungeon_layouts[player].values(): for builder in world.dungeon_layouts[player].values():
shuffle_key_doors(builder, world, player) shuffle_key_doors(builder, world, player)
logging.getLogger('').info('Key door shuffle time: %s', time.process_time()-start) logging.getLogger('').info('%s: %s', fish.translate("cli","cli","keydoor.shuffle.time"), time.process_time()-start)
smooth_door_pairs(world, player) smooth_door_pairs(world, player)
@@ -347,6 +353,11 @@ def handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map)
def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player): def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player):
settings = cli.get_args_priority(None, None, None)
if "load" in settings:
settings = settings["load"]
fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en")
entrances_map, potentials, connections = connections_tuple entrances_map, potentials, connections = connections_tuple
enabled_entrances = {} enabled_entrances = {}
sector_queue = deque(dungeon_builders.values()) sector_queue = deque(dungeon_builders.values())
@@ -366,7 +377,7 @@ def main_dungeon_generation(dungeon_builders, recombinant_builders, connections_
sector_queue.append(builder) sector_queue.append(builder)
last_key = builder.name last_key = builder.name
else: else:
logging.getLogger('').info('Generating dungeon: %s', builder.name) logging.getLogger('').info('%s: %s', fish.translate("cli","cli","generating.dungeon"), builder.name)
ds = generate_dungeon(builder, origin_list_sans_drops, split_dungeon, world, player) ds = generate_dungeon(builder, origin_list_sans_drops, split_dungeon, world, player)
find_new_entrances(ds, connections, potentials, enabled_entrances, world, player) find_new_entrances(ds, connections, potentials, enabled_entrances, world, player)
ds.name = name ds.name = name
@@ -713,6 +724,9 @@ def cross_dungeon(world, player):
def assign_cross_keys(dungeon_builders, world, player): def assign_cross_keys(dungeon_builders, world, player):
settings = cli.get_settings()
fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en")
start = time.process_time() start = time.process_time()
total_keys = remaining = 29 total_keys = remaining = 29
total_candidates = 0 total_candidates = 0
@@ -792,7 +806,7 @@ def assign_cross_keys(dungeon_builders, world, player):
dungeon.small_keys = [] dungeon.small_keys = []
else: else:
dungeon.small_keys = [ItemFactory(dungeon_keys[name], player)] * actual_chest_keys dungeon.small_keys = [ItemFactory(dungeon_keys[name], player)] * actual_chest_keys
logging.getLogger('').info('Cross Dungeon: Key door shuffle time: %s', time.process_time()-start) logging.getLogger('').info('%s: %s', fish.translate("cli","cli","keydoor.shuffle.time.crossed"), time.process_time()-start)
def reassign_boss(boss_region, boss_key, builder, gt, world, player): def reassign_boss(boss_region, boss_key, builder, gt, world, player):
@@ -948,15 +962,18 @@ def calc_used_dungeon_items(builder):
def find_valid_combination(builder, start_regions, world, player, drop_keys=True): def find_valid_combination(builder, start_regions, world, player, drop_keys=True):
settings = cli.get_settings()
fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en")
logger = logging.getLogger('') logger = logging.getLogger('')
logger.info('Shuffling Key doors for %s', builder.name) logger.info('%s %s', fish.translate("cli","cli","shuffling.keydoors"), builder.name)
# find valid combination of candidates # find valid combination of candidates
if len(builder.candidates) < builder.key_doors_num: if len(builder.candidates) < builder.key_doors_num:
if not drop_keys: if not drop_keys:
logger.info('No valid layouts for %s with %s doors', builder.name, builder.key_doors_num) logger.info('No valid layouts for %s with %s doors', builder.name, builder.key_doors_num)
return False return False
builder.key_doors_num = len(builder.candidates) # reduce number of key doors builder.key_doors_num = len(builder.candidates) # reduce number of key doors
logger.info('Lowering key door count because not enough candidates: %s', builder.name) logger.info('%s: %s', fish.translate("cli","cli","lowering.keys.candidates"), builder.name)
combinations = ncr(len(builder.candidates), builder.key_doors_num) combinations = ncr(len(builder.candidates), builder.key_doors_num)
itr = 0 itr = 0
start = time.process_time() start = time.process_time()
@@ -976,7 +993,7 @@ def find_valid_combination(builder, start_regions, world, player, drop_keys=True
if not drop_keys: if not drop_keys:
logger.info('No valid layouts for %s with %s doors', builder.name, builder.key_doors_num) logger.info('No valid layouts for %s with %s doors', builder.name, builder.key_doors_num)
return False return False
logger.info('Lowering key door count because no valid layouts: %s', builder.name) logger.info('%s: %s', fish.translate("cli","cli","lowering.keys.layouts"), builder.name)
builder.key_doors_num -= 1 builder.key_doors_num -= 1
if builder.key_doors_num < 0: if builder.key_doors_num < 0:
raise Exception('Bad dungeon %s - 0 key doors not valid' % builder.name) raise Exception('Bad dungeon %s - 0 key doors not valid' % builder.name)

View File

@@ -8,6 +8,9 @@ from functools import reduce
import operator as op import operator as op
from typing import List from typing import List
from source.classes.BabelFish import BabelFish
import CLI as cli
from BaseClasses import DoorType, Direction, CrystalBarrier, RegionType, Polarity, Sector, PolSlot, flooded_keys from BaseClasses import DoorType, Direction, CrystalBarrier, RegionType, Polarity, Sector, PolSlot, flooded_keys
from Regions import key_only_locations, dungeon_events, flooded_keys_reverse from Regions import key_only_locations, dungeon_events, flooded_keys_reverse
from Dungeons import dungeon_regions from Dungeons import dungeon_regions
@@ -1094,6 +1097,11 @@ def simple_dungeon_builder(name, sector_list):
def create_dungeon_builders(all_sectors, world, player, dungeon_entrances=None): def create_dungeon_builders(all_sectors, world, player, dungeon_entrances=None):
settings = cli.get_args_priority(None, None, None)
if "load" in settings:
settings = settings["load"]
fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en")
logger = logging.getLogger('') logger = logging.getLogger('')
logger.info('Shuffling Dungeon Sectors') logger.info('Shuffling Dungeon Sectors')
if dungeon_entrances is None: if dungeon_entrances is None:
@@ -1149,7 +1157,7 @@ def create_dungeon_builders(all_sectors, world, player, dungeon_entrances=None):
# polarity: # polarity:
if not global_pole.is_valid(dungeon_map): if not global_pole.is_valid(dungeon_map):
raise NeutralizingException('Either free location/crystal assignment is already globally invalid - lazy dev check this earlier!') raise NeutralizingException('Either free location/crystal assignment is already globally invalid - lazy dev check this earlier!')
logger.info('-Balancing Doors') logger.info(fish.translate("cli","cli","balance.doors"))
assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger) assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger)
# the rest # the rest
assign_the_rest(dungeon_map, neutral_sectors, global_pole) assign_the_rest(dungeon_map, neutral_sectors, global_pole)
@@ -1435,8 +1443,13 @@ def sum_polarity(sector_list):
def assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger): def assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger):
settings = cli.get_args_priority(None, None, None)
if "load" in settings:
settings = settings["load"]
fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en")
# step 1: fix polarity connection issues # step 1: fix polarity connection issues
logger.info('--Basic Traversal') logger.info(fish.translate("cli","cli","basic.traversal"))
unconnected_builders = identify_polarity_issues(dungeon_map) unconnected_builders = identify_polarity_issues(dungeon_map)
while len(unconnected_builders) > 0: while len(unconnected_builders) > 0:
for name, builder in unconnected_builders.items(): for name, builder in unconnected_builders.items():
@@ -1524,10 +1537,15 @@ def assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger
def polarity_step_3(dungeon_map, polarized_sectors, global_pole, logger): def polarity_step_3(dungeon_map, polarized_sectors, global_pole, logger):
settings = cli.get_args_priority(None, None, None)
if "load" in settings:
settings = settings["load"]
fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en")
builder_order = list(dungeon_map.values()) builder_order = list(dungeon_map.values())
random.shuffle(builder_order) random.shuffle(builder_order)
for builder in builder_order: for builder in builder_order:
logger.info('--Balancing %s', builder.name) logger.info('%s %s', fish.translate("cli","cli","balancing"), builder.name)
while not builder.polarity().is_neutral(): while not builder.polarity().is_neutral():
candidates = find_neutralizing_candidates(builder, polarized_sectors) candidates = find_neutralizing_candidates(builder, polarized_sectors)
valid, sectors = False, None valid, sectors = False, None
@@ -1832,8 +1850,13 @@ def assign_the_rest(dungeon_map, neutral_sectors, global_pole):
def split_dungeon_builder(builder, split_list): def split_dungeon_builder(builder, split_list):
settings = cli.get_args_priority(None, None, None)
if "load" in settings:
settings = settings["load"]
fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en")
logger = logging.getLogger('') logger = logging.getLogger('')
logger.info('Splitting Up Desert/Skull') logger.info(fish.translate("cli","cli","splitting.up") + ' ' + 'Desert/Skull')
candidate_sectors = dict.fromkeys(builder.sectors) candidate_sectors = dict.fromkeys(builder.sectors)
global_pole = GlobalPolarity(candidate_sectors) global_pole = GlobalPolarity(candidate_sectors)
@@ -1848,6 +1871,11 @@ def split_dungeon_builder(builder, split_list):
def balance_split(candidate_sectors, dungeon_map, global_pole): def balance_split(candidate_sectors, dungeon_map, global_pole):
settings = cli.get_args_priority(None, None, None)
if "load" in settings:
settings = settings["load"]
fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en")
logger = logging.getLogger('') logger = logging.getLogger('')
# categorize sectors # categorize sectors
crystal_switches, crystal_barriers, neutral_sectors, polarized_sectors = categorize_sectors(candidate_sectors) crystal_switches, crystal_barriers, neutral_sectors, polarized_sectors = categorize_sectors(candidate_sectors)
@@ -1860,7 +1888,7 @@ def balance_split(candidate_sectors, dungeon_map, global_pole):
# blue barriers # blue barriers
assign_crystal_barrier_sectors(dungeon_map, crystal_barriers, global_pole) assign_crystal_barrier_sectors(dungeon_map, crystal_barriers, global_pole)
# polarity: # polarity:
logger.info('-Re-balancing ' + next(iter(dungeon_map.keys())) + ' et al') logger.info(fish.translate("cli","cli","re-balancing") + ' ' + next(iter(dungeon_map.keys())) + ' et al')
assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger) assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger)
# the rest # the rest
assign_the_rest(dungeon_map, neutral_sectors, global_pole) assign_the_rest(dungeon_map, neutral_sectors, global_pole)

View File

@@ -8,7 +8,9 @@ import textwrap
import shlex import shlex
import sys import sys
from CLI import parse_arguments from source.classes.BabelFish import BabelFish
from CLI import parse_arguments, get_args_priority
from Main import main from Main import main
from Rom import get_sprite_from_name from Rom import get_sprite_from_name
from Utils import is_bundled, close_console from Utils import is_bundled, close_console
@@ -17,6 +19,11 @@ from Fill import FillError
def start(): def start():
args = parse_arguments(None) args = parse_arguments(None)
settings = get_args_priority(None, None, None)
if "load" in settings:
settings = settings["load"]
fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en")
if is_bundled() and len(sys.argv) == 1: if is_bundled() and len(sys.argv) == 1:
# for the bundled builds, if we have no arguments, the user # for the bundled builds, if we have no arguments, the user
# probably wants the gui. Users of the bundled build who want the command line # probably wants the gui. Users of the bundled build who want the command line
@@ -52,10 +59,10 @@ def start():
for _ in range(args.count): for _ in range(args.count):
try: try:
main(seed=seed, args=args) main(seed=seed, args=args)
logger.info('Finished run %s', _+1) logger.info('%s %s', fish.translate("cli","cli","finished.run"), _+1)
except (FillError, Exception, RuntimeError) as err: except (FillError, Exception, RuntimeError) as err:
failures.append((err, seed)) failures.append((err, seed))
logger.warning('Generation failed: %s', err) logger.warning('%s: %s', fish.translate("cli","cli","generation.failed"), err)
seed = random.randint(0, 999999999) seed = random.randint(0, 999999999)
for fail in failures: for fail in failures:
logger.info('%s seed failed with: %s', fail[1], fail[0]) logger.info('%s seed failed with: %s', fail[1], fail[0])

68
Main.py
View File

@@ -8,6 +8,9 @@ import random
import time import time
import zlib import zlib
from source.classes.BabelFish import BabelFish
import CLI as cli
from BaseClasses import World, CollectionState, Item, Region, Location, Shop from BaseClasses import World, CollectionState, Item, Region, Location, Shop
from Items import ItemFactory from Items import ItemFactory
from KeyDoorShuffle import validate_key_placement from KeyDoorShuffle import validate_key_placement
@@ -34,6 +37,11 @@ def main(args, seed=None):
start = time.perf_counter() start = time.perf_counter()
settings = cli.get_args_priority(None, None, None)
if "load" in settings:
settings = settings["load"]
fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en")
# initialize the world # initialize the world
world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints) world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints)
logger = logging.getLogger('') logger = logging.getLogger('')
@@ -62,7 +70,14 @@ def main(args, seed=None):
world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)} world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)}
logger.info('ALttP Door Randomizer Version %s - Seed: %s\n', __version__, world.seed) logger.info(
'%s %s %s - %s: %s\n',
fish.translate("cli","cli","app.title"),
fish.translate("cli","cli","version"),
__version__,
fish.translate("cli","cli","seed"),
world.seed
)
parsed_names = parse_player_names(args.names, world.players, args.teams) parsed_names = parse_player_names(args.names, world.players, args.teams)
world.teams = len(parsed_names) world.teams = len(parsed_names)
@@ -95,7 +110,7 @@ def main(args, seed=None):
create_rooms(world, player) create_rooms(world, player)
create_dungeons(world, player) create_dungeons(world, player)
logger.info('Shuffling the World about.') logger.info(fish.translate("cli","cli","shuffling.world"))
for player in range(1, world.players + 1): for player in range(1, world.players + 1):
if world.mode[player] != 'inverted': if world.mode[player] != 'inverted':
@@ -103,7 +118,7 @@ def main(args, seed=None):
else: else:
link_inverted_entrances(world, player) link_inverted_entrances(world, player)
logger.info('Shuffling dungeons') logger.info(fish.translate("cli","cli","shuffling.dungeons"))
for player in range(1, world.players + 1): for player in range(1, world.players + 1):
link_doors(world, player) link_doors(world, player)
@@ -111,21 +126,21 @@ def main(args, seed=None):
mark_light_world_regions(world, player) mark_light_world_regions(world, player)
else: else:
mark_dark_world_regions(world, player) mark_dark_world_regions(world, player)
logger.info('Generating Item Pool.') logger.info(fish.translate("cli","cli","generating.itempool"))
for player in range(1, world.players + 1): for player in range(1, world.players + 1):
generate_itempool(world, player) generate_itempool(world, player)
logger.info('Calculating Access Rules.') logger.info(fish.translate("cli","cli","calc.access.rules"))
for player in range(1, world.players + 1): for player in range(1, world.players + 1):
set_rules(world, player) set_rules(world, player)
logger.info('Placing Dungeon Prizes.') logger.info(fish.translate("cli","cli","placing.dungeon.prizes"))
fill_prizes(world) fill_prizes(world)
logger.info('Placing Dungeon Items.') logger.info(fish.translate("cli","cli","placing.dungeon.items"))
shuffled_locations = None shuffled_locations = None
if args.algorithm in ['balanced', 'vt26'] or any(list(args.mapshuffle.values()) + list(args.compassshuffle.values()) + if args.algorithm in ['balanced', 'vt26'] or any(list(args.mapshuffle.values()) + list(args.compassshuffle.values()) +
@@ -139,9 +154,17 @@ def main(args, seed=None):
for player in range(1, world.players+1): for player in range(1, world.players+1):
for key_layout in world.key_layout[player].values(): for key_layout in world.key_layout[player].values():
if not validate_key_placement(key_layout, world, player): if not validate_key_placement(key_layout, world, player):
raise RuntimeError("Keylock detected: %s (Player %d)" % (key_layout.sector.name, player)) raise RuntimeError(
"%s: %s (%s %d)" %
(
fish.translate("cli","cli","keylock.detected"),
key_layout.sector.name,
fish.translate("cli","cli","player"),
player
)
)
logger.info('Fill the world.') logger.info(fish.translate("cli","cli","fill.world"))
if args.algorithm == 'flood': if args.algorithm == 'flood':
flood_items(world) # different algo, biased towards early game progress items flood_items(world) # different algo, biased towards early game progress items
@@ -160,14 +183,14 @@ def main(args, seed=None):
distribute_items_restrictive(world, True) distribute_items_restrictive(world, True)
if world.players > 1: if world.players > 1:
logger.info('Balancing multiworld progression.') logger.info(fish.translate("cli","cli","balance.multiworld"))
balance_multiworld_progression(world) balance_multiworld_progression(world)
# if we only check for beatable, we can do this sanity check first before creating the rom # if we only check for beatable, we can do this sanity check first before creating the rom
if not world.can_beat_game(): if not world.can_beat_game():
raise RuntimeError('Cannot beat game. Something went terribly wrong here!') raise RuntimeError(fish.translate("cli","cli","cannot.beat.game"))
logger.info('Patching ROM.') logger.info(fish.translate("cli","cli","patching.rom"))
outfilebase = 'DR_%s' % (args.outputname if args.outputname else world.seed) outfilebase = 'DR_%s' % (args.outputname if args.outputname else world.seed)
@@ -191,8 +214,8 @@ def main(args, seed=None):
if not args.jsonout: if not args.jsonout:
rom = LocalRom.fromJsonRom(rom, args.rom, 0x400000) rom = LocalRom.fromJsonRom(rom, args.rom, 0x400000)
else: else:
logging.warning("EnemizerCLI not found at:" + args.enemizercli) logging.warning(fish.translate("cli","cli","enemizer.not.found") + ': ' + args.enemizercli)
logging.warning("No Enemizer options will be applied until this is resolved.") logging.warning(fish.translate("cli","cli","enemizer.nothing.applied"))
if args.race: if args.race:
patch_race_rom(rom) patch_race_rom(rom)
@@ -246,7 +269,7 @@ def main(args, seed=None):
world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase))
if not args.skip_playthrough: if not args.skip_playthrough:
logger.info('Calculating playthrough.') logger.info(fish.translate("cli","cli","calc.playthrough"))
create_playthrough(world) create_playthrough(world)
if args.jsonout: if args.jsonout:
@@ -254,8 +277,8 @@ def main(args, seed=None):
elif args.create_spoiler and not args.skip_playthrough: elif args.create_spoiler and not args.skip_playthrough:
world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase))
logger.info('Done. Enjoy.') logger.info(fish.translate("cli","cli","done"))
logger.info('Total Time: %s', time.perf_counter() - start) logger.info('%s: %s', fish.translate("cli","cli","total.time"), time.perf_counter() - start)
# print_wiki_doors_by_room(dungeon_regions,world,1) # print_wiki_doors_by_room(dungeon_regions,world,1)
@@ -391,6 +414,11 @@ def copy_dynamic_regions_and_locations(world, ret):
def create_playthrough(world): def create_playthrough(world):
settings = cli.get_args_priority(None, None, None)
if "load" in settings:
settings = settings["load"]
fish = BabelFish(lang=settings["lang"] if "lang" in settings else "en")
# create a copy as we will modify it # create a copy as we will modify it
old_world = world old_world = world
world = copy_world(world) world = copy_world(world)
@@ -401,7 +429,7 @@ def create_playthrough(world):
collection_spheres = [] collection_spheres = []
state = CollectionState(world) state = CollectionState(world)
sphere_candidates = list(prog_locations) sphere_candidates = list(prog_locations)
logging.getLogger('').debug('Building up collection spheres.') logging.getLogger('').debug(fish.translate("cli","cli","building.collection.spheres"))
while sphere_candidates: while sphere_candidates:
state.sweep_for_events(key_only=True) state.sweep_for_events(key_only=True)
state.sweep_for_crystal_access() state.sweep_for_crystal_access()
@@ -424,7 +452,7 @@ def create_playthrough(world):
if not sphere: if not sphere:
logging.getLogger('').debug('The following items could not be reached: %s', ['%s (Player %d) at %s (Player %d)' % (location.item.name, location.item.player, location.name, location.player) for location in sphere_candidates]) logging.getLogger('').debug('The following items could not be reached: %s', ['%s (Player %d) at %s (Player %d)' % (location.item.name, location.item.player, location.name, location.player) for location in sphere_candidates])
if any([world.accessibility[location.item.player] != 'none' for location in sphere_candidates]): if any([world.accessibility[location.item.player] != 'none' for location in sphere_candidates]):
raise RuntimeError('Not all progression items reachable. Something went terribly wrong here.') raise RuntimeError(fish.translate("cli","cli","cannot.reach.progression"))
else: else:
old_world.spoiler.unreachables = sphere_candidates.copy() old_world.spoiler.unreachables = sphere_candidates.copy()
break break
@@ -478,7 +506,7 @@ def create_playthrough(world):
logging.getLogger('').debug('Calculated final sphere %i, containing %i of %i progress items.', len(collection_spheres), len(sphere), len(required_locations)) logging.getLogger('').debug('Calculated final sphere %i, containing %i of %i progress items.', len(collection_spheres), len(sphere), len(required_locations))
if not sphere: if not sphere:
raise RuntimeError('Not all required items reachable. Something went terribly wrong here.') raise RuntimeError(fish.translate("cli","cli","cannot.reach.required"))
# store the required locations for statistical analysis # store the required locations for statistical analysis
old_world.required_locations = [(location.name, location.player) for sphere in collection_spheres for location in sphere] old_world.required_locations = [(location.name, location.player) for sphere in collection_spheres for location in sphere]

View File

@@ -1,4 +1,5 @@
{ {
"lang": {},
"create_spoiler": { "create_spoiler": {
"action": "store_true", "action": "store_true",
"type": "bool" "type": "bool"

View File

@@ -1,5 +1,46 @@
{ {
"cli": {
"app.title": "ALttP Door Randomizer",
"version": "Version",
"seed": "Seed",
"player": "Player",
"shuffling.world": "Shuffling the World about",
"shuffling.dungeons": "Shuffling dungeons",
"basic.traversal": "--Basic Traversal",
"generating.dungeon": "Generating dungeon",
"shuffling.keydoors": "Shuffling Key doors for",
"lowering.keys.candidates": "Lowering key door count because not enough candidates",
"lowering.keys.layouts": "Lowering key door count because no valid layouts",
"keydoor.shuffle.time": "Key door shuffle time",
"keydoor.shuffle.time.crossed": "Cross Dungeon: Key door shuffle time",
"generating.itempool": "Generating Item Pool",
"calc.access.rules": "Calculating Access Rules",
"placing.dungeon.prizes": "Placing Dungeon Prizes",
"placing.dungeon.items": "Placing Dungeon Items",
"keylock.detected": "Keylock detected",
"fill.world": "Fill the world",
"balance.doors": "-Balancing Doors",
"re-balancing": "-Re-balancing",
"balancing": "--Balancing",
"splitting.up": "Splitting Up",
"balance.multiworld": "Balancing multiworld progression",
"cannot.beat.game": "Cannot beat game! Something went terribly wrong here!",
"cannot.reach.progression": "Not all progression items reachable. Something went terribly wrong here.",
"cannot.reach.required": "Not all required items reachable. Something went terribly wrong here.",
"patching.rom": "Patching ROM",
"calc.playthrough": "Calculating playthrough",
"done": "Done. Enjoy.",
"total.time": "Total Time",
"finished.run": "Finished run",
"generation.failed": "Generation failed",
"generation.fail.rate": "Generation fail rate",
"generation.success.rate": "Generation success rate",
"enemizer.not.found": "Enemizer not found at",
"enemizer.nothing.applied": "No Enemizer options will be applied until this is resolved.",
"building.collection.spheres": "Building up collection spheres"
},
"help": { "help": {
"lang": [ "App Language, if available, defaults to English" ],
"create_spoiler": [ "Output a Spoiler File" ], "create_spoiler": [ "Output a Spoiler File" ],
"logic": [ "logic": [
"Select Enforcement of Item Requirements. (default: %(default)s)", "Select Enforcement of Item Requirements. (default: %(default)s)",

View File

@@ -0,0 +1,25 @@
{
"cli": {
"app.title": "ALttP Puerta Aleatorizador",
"version": "Versión",
"seed": "Número",
"player": "Player",
"shuffling.world": "Barajando el Mundo",
"shuffling.dungeons": "Barajando Mazmorras",
"balance.doors": "-Equilibriando Puertas",
"re-balancing": "-Reequilibriando",
"balancing": "--Equilibriando",
"splitting.up": "División",
"basic.traversal": "--Recorrido Básico",
"generating.dungeon": "Generando mazmorra",
"shuffling.keydoors": "Barajando Puertas Clave para",
"placing.dungeon.prizes": "Placing Dungeon Prizes",
"placing.dungeon.items": "Placing Dungeon Items",
"keylock.detected": "Bloqueo de Teclas detectado",
"fill.world": "Llenar el Mundo",
"patching.rom": "Parchear ROM",
"calc.playthrough": "Cálculo de Juego",
"generation.failed": "Generación Fallida",
"enemizer.not.found": "Enemizer no encontrado en"
}
}

View File

@@ -34,7 +34,8 @@ class BabelFish():
with open(langs_filename,encoding="utf-8") as f: #open it with open(langs_filename,encoding="utf-8") as f: #open it
self.lang_defns[lang][key[:key.rfind(os.sep)].replace(os.sep,'.')] = json.load(f) #save translation definitions self.lang_defns[lang][key[:key.rfind(os.sep)].replace(os.sep,'.')] = json.load(f) #save translation definitions
else: else:
print(langs_filename + " not found for translation!") pass
# print(langs_filename + " not found for translation!")
def translate(self, domain="", key="", subkey=""): #three levels of keys def translate(self, domain="", key="", subkey=""): #three levels of keys
# start with nothing # start with nothing