Start implementing object oriented scaffold for world types
(There's still a lot of work ahead, such as: registering locations and items to the World, as well as methods to create_item_from_name() many more method names for various stages embedding Options into the world type and many more...)
This commit is contained in:
parent
753a5f7cb2
commit
568a71cdbe
|
@ -32,7 +32,7 @@ class MultiWorld():
|
||||||
return self.rule(player)
|
return self.rule(player)
|
||||||
|
|
||||||
def __init__(self, players: int):
|
def __init__(self, players: int):
|
||||||
# TODO: move per-player settings into new classes per game-type instead of clumping it all together here
|
from worlds import AutoWorld
|
||||||
|
|
||||||
self.random = random.Random() # world-local random state is saved for multiple generations running concurrently
|
self.random = random.Random() # world-local random state is saved for multiple generations running concurrently
|
||||||
self.players = players
|
self.players = players
|
||||||
|
@ -139,11 +139,10 @@ class MultiWorld():
|
||||||
set_player_attr('game', "A Link to the Past")
|
set_player_attr('game', "A Link to the Past")
|
||||||
set_player_attr('completion_condition', lambda state: True)
|
set_player_attr('completion_condition', lambda state: True)
|
||||||
self.custom_data = {}
|
self.custom_data = {}
|
||||||
|
self.worlds = {}
|
||||||
for player in range(1, players+1):
|
for player in range(1, players+1):
|
||||||
self.custom_data[player] = {}
|
self.custom_data[player] = {}
|
||||||
# self.worlds = []
|
self.worlds[player] = AutoWorld.AutoWorldRegister.world_types[self.game[player]](player)
|
||||||
# for i in range(players):
|
|
||||||
# self.worlds.append(worlds.alttp.ALTTPWorld({}, i))
|
|
||||||
|
|
||||||
def secure(self):
|
def secure(self):
|
||||||
self.random = secrets.SystemRandom()
|
self.random = secrets.SystemRandom()
|
||||||
|
|
181
Main.py
181
Main.py
|
@ -24,12 +24,10 @@ from worlds.alttp.ItemPool import generate_itempool, difficulties, fill_prizes
|
||||||
from Utils import output_path, parse_player_names, get_options, __version__, _version_tuple
|
from Utils import output_path, parse_player_names, get_options, __version__, _version_tuple
|
||||||
from worlds.hk import gen_hollow
|
from worlds.hk import gen_hollow
|
||||||
from worlds.hk import create_regions as hk_create_regions
|
from worlds.hk import create_regions as hk_create_regions
|
||||||
# from worlds.factorio import gen_factorio, factorio_create_regions
|
|
||||||
# from worlds.factorio.Mod import generate_mod
|
|
||||||
from worlds.minecraft import gen_minecraft, fill_minecraft_slot_data, generate_mc_data
|
from worlds.minecraft import gen_minecraft, fill_minecraft_slot_data, generate_mc_data
|
||||||
from worlds.minecraft.Regions import minecraft_create_regions
|
from worlds.minecraft.Regions import minecraft_create_regions
|
||||||
from worlds.generic.Rules import locality_rules
|
from worlds.generic.Rules import locality_rules
|
||||||
from worlds import Games, lookup_any_item_name_to_id
|
from worlds import Games, lookup_any_item_name_to_id, AutoWorld
|
||||||
import Patch
|
import Patch
|
||||||
|
|
||||||
seeddigits = 20
|
seeddigits = 20
|
||||||
|
@ -79,7 +77,7 @@ def main(args, seed=None):
|
||||||
world.progressive = args.progressive.copy()
|
world.progressive = args.progressive.copy()
|
||||||
world.goal = args.goal.copy()
|
world.goal = args.goal.copy()
|
||||||
world.local_items = args.local_items.copy()
|
world.local_items = args.local_items.copy()
|
||||||
if hasattr(args, "algorithm"): # current GUI options
|
if hasattr(args, "algorithm"): # current GUI options
|
||||||
world.algorithm = args.algorithm
|
world.algorithm = args.algorithm
|
||||||
world.shuffleganon = args.shuffleganon
|
world.shuffleganon = args.shuffleganon
|
||||||
world.custom = args.custom
|
world.custom = args.custom
|
||||||
|
@ -128,13 +126,14 @@ def main(args, seed=None):
|
||||||
world.game = args.game.copy()
|
world.game = args.game.copy()
|
||||||
import Options
|
import Options
|
||||||
for option_set in Options.option_sets:
|
for option_set in Options.option_sets:
|
||||||
# for option in option_set:
|
for option in option_set:
|
||||||
# setattr(world, option, getattr(args, option, {}))
|
setattr(world, option, getattr(args, option, {}))
|
||||||
world.glitch_triforce = args.glitch_triforce # This is enabled/disabled globally, no per player option.
|
world.glitch_triforce = args.glitch_triforce # This is enabled/disabled globally, no per player option.
|
||||||
|
|
||||||
world.rom_seeds = {player: random.Random(world.random.randint(0, 999999999)) for player in range(1, world.players + 1)}
|
world.rom_seeds = {player: random.Random(world.random.randint(0, 999999999)) for player in
|
||||||
|
range(1, world.players + 1)}
|
||||||
|
|
||||||
for player in range(1, world.players+1):
|
for player in range(1, world.players + 1):
|
||||||
world.er_seeds[player] = str(world.random.randint(0, 2 ** 64))
|
world.er_seeds[player] = str(world.random.randint(0, 2 ** 64))
|
||||||
|
|
||||||
if "-" in world.shuffle[player]:
|
if "-" in world.shuffle[player]:
|
||||||
|
@ -144,7 +143,8 @@ def main(args, seed=None):
|
||||||
world.er_seeds[player] = "vanilla"
|
world.er_seeds[player] = "vanilla"
|
||||||
elif seed.startswith("group-") or args.race:
|
elif seed.startswith("group-") or args.race:
|
||||||
# renamed from team to group to not confuse with existing team name use
|
# renamed from team to group to not confuse with existing team name use
|
||||||
world.er_seeds[player] = get_same_seed(world, (shuffle, seed, world.retro[player], world.mode[player], world.logic[player]))
|
world.er_seeds[player] = get_same_seed(world, (
|
||||||
|
shuffle, seed, world.retro[player], world.mode[player], world.logic[player]))
|
||||||
else: # not a race or group seed, use set seed as is.
|
else: # not a race or group seed, use set seed as is.
|
||||||
world.er_seeds[player] = seed
|
world.er_seeds[player] = seed
|
||||||
elif world.shuffle[player] == "vanilla":
|
elif world.shuffle[player] == "vanilla":
|
||||||
|
@ -152,6 +152,10 @@ def main(args, seed=None):
|
||||||
|
|
||||||
logger.info('Archipelago Version %s - Seed: %s\n', __version__, world.seed)
|
logger.info('Archipelago Version %s - Seed: %s\n', __version__, world.seed)
|
||||||
|
|
||||||
|
logger.info("Found World Types:")
|
||||||
|
for name, cls in AutoWorld.AutoWorldRegister.world_types.items():
|
||||||
|
logger.info(f" {name:30} {cls}")
|
||||||
|
|
||||||
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)
|
||||||
for i, team in enumerate(parsed_names, 1):
|
for i, team in enumerate(parsed_names, 1):
|
||||||
|
@ -199,23 +203,26 @@ def main(args, seed=None):
|
||||||
for player in world.hk_player_ids:
|
for player in world.hk_player_ids:
|
||||||
hk_create_regions(world, player)
|
hk_create_regions(world, player)
|
||||||
|
|
||||||
# for player in world.factorio_player_ids:
|
AutoWorld.call_all(world, "create_regions")
|
||||||
# factorio_create_regions(world, player)
|
|
||||||
|
|
||||||
for player in world.minecraft_player_ids:
|
for player in world.minecraft_player_ids:
|
||||||
minecraft_create_regions(world, player)
|
minecraft_create_regions(world, player)
|
||||||
|
|
||||||
for player in world.alttp_player_ids:
|
for player in world.alttp_player_ids:
|
||||||
if world.open_pyramid[player] == 'goal':
|
if world.open_pyramid[player] == 'goal':
|
||||||
world.open_pyramid[player] = world.goal[player] in {'crystals', 'ganontriforcehunt', 'localganontriforcehunt', 'ganonpedestal'}
|
world.open_pyramid[player] = world.goal[player] in {'crystals', 'ganontriforcehunt',
|
||||||
|
'localganontriforcehunt', 'ganonpedestal'}
|
||||||
elif world.open_pyramid[player] == 'auto':
|
elif world.open_pyramid[player] == 'auto':
|
||||||
world.open_pyramid[player] = world.goal[player] in {'crystals', 'ganontriforcehunt', 'localganontriforcehunt', 'ganonpedestal'} and \
|
world.open_pyramid[player] = world.goal[player] in {'crystals', 'ganontriforcehunt',
|
||||||
(world.shuffle[player] in {'vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed'} or not world.shuffle_ganon)
|
'localganontriforcehunt', 'ganonpedestal'} and \
|
||||||
|
(world.shuffle[player] in {'vanilla', 'dungeonssimple', 'dungeonsfull',
|
||||||
|
'dungeonscrossed'} or not world.shuffle_ganon)
|
||||||
else:
|
else:
|
||||||
world.open_pyramid[player] = {'on': True, 'off': False, 'yes': True, 'no': False}.get(world.open_pyramid[player], world.open_pyramid[player])
|
world.open_pyramid[player] = {'on': True, 'off': False, 'yes': True, 'no': False}.get(
|
||||||
|
world.open_pyramid[player], world.open_pyramid[player])
|
||||||
|
|
||||||
|
world.triforce_pieces_available[player] = max(world.triforce_pieces_available[player],
|
||||||
world.triforce_pieces_available[player] = max(world.triforce_pieces_available[player], world.triforce_pieces_required[player])
|
world.triforce_pieces_required[player])
|
||||||
|
|
||||||
if world.mode[player] != 'inverted':
|
if world.mode[player] != 'inverted':
|
||||||
create_regions(world, player)
|
create_regions(world, player)
|
||||||
|
@ -261,8 +268,7 @@ def main(args, seed=None):
|
||||||
for player in world.hk_player_ids:
|
for player in world.hk_player_ids:
|
||||||
gen_hollow(world, player)
|
gen_hollow(world, player)
|
||||||
|
|
||||||
# for player in world.factorio_player_ids:
|
AutoWorld.call_all(world, "generate_basic")
|
||||||
# gen_factorio(world, player)
|
|
||||||
|
|
||||||
for player in world.minecraft_player_ids:
|
for player in world.minecraft_player_ids:
|
||||||
gen_minecraft(world, player)
|
gen_minecraft(world, player)
|
||||||
|
@ -327,13 +333,13 @@ def main(args, seed=None):
|
||||||
|
|
||||||
world.spoiler.hashes[(player, team)] = get_hash_string(rom.hash)
|
world.spoiler.hashes[(player, team)] = get_hash_string(rom.hash)
|
||||||
|
|
||||||
palettes_options={}
|
palettes_options = {}
|
||||||
palettes_options['dungeon']=args.uw_palettes[player]
|
palettes_options['dungeon'] = args.uw_palettes[player]
|
||||||
palettes_options['overworld']=args.ow_palettes[player]
|
palettes_options['overworld'] = args.ow_palettes[player]
|
||||||
palettes_options['hud']=args.hud_palettes[player]
|
palettes_options['hud'] = args.hud_palettes[player]
|
||||||
palettes_options['sword']=args.sword_palettes[player]
|
palettes_options['sword'] = args.sword_palettes[player]
|
||||||
palettes_options['shield']=args.shield_palettes[player]
|
palettes_options['shield'] = args.shield_palettes[player]
|
||||||
palettes_options['link']=args.link_palettes[player]
|
palettes_options['link'] = args.link_palettes[player]
|
||||||
|
|
||||||
apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player],
|
apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player],
|
||||||
args.fastmenu[player], args.disablemusic[player], args.sprite[player],
|
args.fastmenu[player], args.disablemusic[player], args.sprite[player],
|
||||||
|
@ -349,8 +355,8 @@ def main(args, seed=None):
|
||||||
world.bigkeyshuffle[player]].count(True) == 1:
|
world.bigkeyshuffle[player]].count(True) == 1:
|
||||||
mcsb_name = '-mapshuffle' if world.mapshuffle[player] else \
|
mcsb_name = '-mapshuffle' if world.mapshuffle[player] else \
|
||||||
'-compassshuffle' if world.compassshuffle[player] else \
|
'-compassshuffle' if world.compassshuffle[player] else \
|
||||||
'-universal_keys' if world.keyshuffle[player] == "universal" else \
|
'-universal_keys' if world.keyshuffle[player] == "universal" else \
|
||||||
'-keyshuffle' if world.keyshuffle[player] else '-bigkeyshuffle'
|
'-keyshuffle' if world.keyshuffle[player] else '-bigkeyshuffle'
|
||||||
elif any([world.mapshuffle[player], world.compassshuffle[player], world.keyshuffle[player],
|
elif any([world.mapshuffle[player], world.compassshuffle[player], world.keyshuffle[player],
|
||||||
world.bigkeyshuffle[player]]):
|
world.bigkeyshuffle[player]]):
|
||||||
mcsb_name = '-%s%s%s%sshuffle' % (
|
mcsb_name = '-%s%s%s%sshuffle' % (
|
||||||
|
@ -362,46 +368,46 @@ def main(args, seed=None):
|
||||||
outfilepname += f"_{world.player_names[player][team].replace(' ', '_')}" \
|
outfilepname += f"_{world.player_names[player][team].replace(' ', '_')}" \
|
||||||
if world.player_names[player][team] != 'Player%d' % player else ''
|
if world.player_names[player][team] != 'Player%d' % player else ''
|
||||||
outfilestuffs = {
|
outfilestuffs = {
|
||||||
"logic": world.logic[player], # 0
|
"logic": world.logic[player], # 0
|
||||||
"difficulty": world.difficulty[player], # 1
|
"difficulty": world.difficulty[player], # 1
|
||||||
"item_functionality": world.item_functionality[player], # 2
|
"item_functionality": world.item_functionality[player], # 2
|
||||||
"mode": world.mode[player], # 3
|
"mode": world.mode[player], # 3
|
||||||
"goal": world.goal[player], # 4
|
"goal": world.goal[player], # 4
|
||||||
"timer": str(world.timer[player]), # 5
|
"timer": str(world.timer[player]), # 5
|
||||||
"shuffle": world.shuffle[player], # 6
|
"shuffle": world.shuffle[player], # 6
|
||||||
"algorithm": world.algorithm, # 7
|
"algorithm": world.algorithm, # 7
|
||||||
"mscb": mcsb_name, # 8
|
"mscb": mcsb_name, # 8
|
||||||
"retro": world.retro[player], # 9
|
"retro": world.retro[player], # 9
|
||||||
"progressive": world.progressive, # A
|
"progressive": world.progressive, # A
|
||||||
"hints": 'True' if world.hints[player] else 'False' # B
|
"hints": 'True' if world.hints[player] else 'False' # B
|
||||||
}
|
}
|
||||||
# 0 1 2 3 4 5 6 7 8 9 A B
|
# 0 1 2 3 4 5 6 7 8 9 A B
|
||||||
outfilesuffix = ('_%s_%s-%s-%s-%s%s_%s-%s%s%s%s%s' % (
|
outfilesuffix = ('_%s_%s-%s-%s-%s%s_%s-%s%s%s%s%s' % (
|
||||||
# 0 1 2 3 4 5 6 7 8 9 A B C
|
# 0 1 2 3 4 5 6 7 8 9 A B C
|
||||||
# _noglitches_normal-normal-open-ganon-ohko_simple-balanced-keysanity-retro-prog_random-nohints
|
# _noglitches_normal-normal-open-ganon-ohko_simple-balanced-keysanity-retro-prog_random-nohints
|
||||||
# _noglitches_normal-normal-open-ganon _simple-balanced-keysanity-retro
|
# _noglitches_normal-normal-open-ganon _simple-balanced-keysanity-retro
|
||||||
# _noglitches_normal-normal-open-ganon _simple-balanced-keysanity -prog_random
|
# _noglitches_normal-normal-open-ganon _simple-balanced-keysanity -prog_random
|
||||||
# _noglitches_normal-normal-open-ganon _simple-balanced-keysanity -nohints
|
# _noglitches_normal-normal-open-ganon _simple-balanced-keysanity -nohints
|
||||||
outfilestuffs["logic"], # 0
|
outfilestuffs["logic"], # 0
|
||||||
|
|
||||||
outfilestuffs["difficulty"], # 1
|
outfilestuffs["difficulty"], # 1
|
||||||
outfilestuffs["item_functionality"], # 2
|
outfilestuffs["item_functionality"], # 2
|
||||||
outfilestuffs["mode"], # 3
|
outfilestuffs["mode"], # 3
|
||||||
outfilestuffs["goal"], # 4
|
outfilestuffs["goal"], # 4
|
||||||
"" if outfilestuffs["timer"] in ['False', 'none', 'display'] else "-" + outfilestuffs["timer"], # 5
|
"" if outfilestuffs["timer"] in ['False', 'none', 'display'] else "-" + outfilestuffs["timer"], # 5
|
||||||
|
|
||||||
outfilestuffs["shuffle"], # 6
|
outfilestuffs["shuffle"], # 6
|
||||||
outfilestuffs["algorithm"], # 7
|
outfilestuffs["algorithm"], # 7
|
||||||
outfilestuffs["mscb"], # 8
|
outfilestuffs["mscb"], # 8
|
||||||
|
|
||||||
"-retro" if outfilestuffs["retro"] == "True" else "", # 9
|
"-retro" if outfilestuffs["retro"] == "True" else "", # 9
|
||||||
"-prog_" + outfilestuffs["progressive"] if outfilestuffs["progressive"] in ['off', 'random'] else "", # A
|
"-prog_" + outfilestuffs["progressive"] if outfilestuffs["progressive"] in ['off', 'random'] else "", # A
|
||||||
"-nohints" if not outfilestuffs["hints"] == "True" else "") # B
|
"-nohints" if not outfilestuffs["hints"] == "True" else "") # B
|
||||||
) if not args.outputname else ''
|
) if not args.outputname else ''
|
||||||
rompath = output_path(f'{outfilebase}{outfilepname}{outfilesuffix}.sfc')
|
rompath = output_path(f'{outfilebase}{outfilepname}{outfilesuffix}.sfc')
|
||||||
rom.write_to_file(rompath, hide_enemizer=True)
|
rom.write_to_file(rompath, hide_enemizer=True)
|
||||||
if args.create_diff:
|
if args.create_diff:
|
||||||
Patch.create_patch_file(rompath, player=player, player_name = world.player_names[player][team])
|
Patch.create_patch_file(rompath, player=player, player_name=world.player_names[player][team])
|
||||||
return player, team, bytes(rom.name)
|
return player, team, bytes(rom.name)
|
||||||
|
|
||||||
pool = concurrent.futures.ThreadPoolExecutor()
|
pool = concurrent.futures.ThreadPoolExecutor()
|
||||||
|
@ -409,12 +415,12 @@ def main(args, seed=None):
|
||||||
check_accessibility_task = pool.submit(world.fulfills_accessibility)
|
check_accessibility_task = pool.submit(world.fulfills_accessibility)
|
||||||
|
|
||||||
rom_futures = []
|
rom_futures = []
|
||||||
mod_futures = []
|
output_file_futures = []
|
||||||
for team in range(world.teams):
|
for team in range(world.teams):
|
||||||
for player in world.alttp_player_ids:
|
for player in world.alttp_player_ids:
|
||||||
rom_futures.append(pool.submit(_gen_rom, team, player))
|
rom_futures.append(pool.submit(_gen_rom, team, player))
|
||||||
for player in world.factorio_player_ids:
|
for player in world.player_ids:
|
||||||
mod_futures.append(pool.submit(generate_mod, world, player))
|
output_file_futures.append(pool.submit(AutoWorld.call_single, world, "generate_output", player))
|
||||||
|
|
||||||
def get_entrance_to_region(region: Region):
|
def get_entrance_to_region(region: Region):
|
||||||
for entrance in region.entrances:
|
for entrance in region.entrances:
|
||||||
|
@ -424,7 +430,8 @@ def main(args, seed=None):
|
||||||
return get_entrance_to_region(entrance.parent_region)
|
return get_entrance_to_region(entrance.parent_region)
|
||||||
|
|
||||||
# collect ER hint info
|
# collect ER hint info
|
||||||
er_hint_data = {player: {} for player in range(1, world.players + 1) if world.shuffle[player] != "vanilla" or world.retro[player]}
|
er_hint_data = {player: {} for player in range(1, world.players + 1) if
|
||||||
|
world.shuffle[player] != "vanilla" or world.retro[player]}
|
||||||
from worlds.alttp.Regions import RegionType
|
from worlds.alttp.Regions import RegionType
|
||||||
for region in world.regions:
|
for region in world.regions:
|
||||||
if region.player in er_hint_data and region.locations:
|
if region.player in er_hint_data and region.locations:
|
||||||
|
@ -450,7 +457,7 @@ def main(args, seed=None):
|
||||||
checks_in_area[location.player]["Light World"].append(location.address)
|
checks_in_area[location.player]["Light World"].append(location.address)
|
||||||
elif location.parent_region.dungeon:
|
elif location.parent_region.dungeon:
|
||||||
dungeonname = {'Inverted Agahnims Tower': 'Agahnims Tower',
|
dungeonname = {'Inverted Agahnims Tower': 'Agahnims Tower',
|
||||||
'Inverted Ganons Tower': 'Ganons Tower'}\
|
'Inverted Ganons Tower': 'Ganons Tower'} \
|
||||||
.get(location.parent_region.dungeon.name, location.parent_region.dungeon.name)
|
.get(location.parent_region.dungeon.name, location.parent_region.dungeon.name)
|
||||||
checks_in_area[location.player][dungeonname].append(location.address)
|
checks_in_area[location.player][dungeonname].append(location.address)
|
||||||
elif main_entrance.parent_region.type == RegionType.LightWorld:
|
elif main_entrance.parent_region.type == RegionType.LightWorld:
|
||||||
|
@ -462,8 +469,10 @@ def main(args, seed=None):
|
||||||
oldmancaves = []
|
oldmancaves = []
|
||||||
takeanyregions = ["Old Man Sword Cave", "Take-Any #1", "Take-Any #2", "Take-Any #3", "Take-Any #4"]
|
takeanyregions = ["Old Man Sword Cave", "Take-Any #1", "Take-Any #2", "Take-Any #3", "Take-Any #4"]
|
||||||
for index, take_any in enumerate(takeanyregions):
|
for index, take_any in enumerate(takeanyregions):
|
||||||
for region in [world.get_region(take_any, player) for player in range(1, world.players + 1) if world.retro[player]]:
|
for region in [world.get_region(take_any, player) for player in range(1, world.players + 1) if
|
||||||
item = ItemFactory(region.shop.inventory[(0 if take_any == "Old Man Sword Cave" else 1)]['item'], region.player)
|
world.retro[player]]:
|
||||||
|
item = ItemFactory(region.shop.inventory[(0 if take_any == "Old Man Sword Cave" else 1)]['item'],
|
||||||
|
region.player)
|
||||||
player = region.player
|
player = region.player
|
||||||
location_id = SHOP_ID_START + total_shop_slots + index
|
location_id = SHOP_ID_START + total_shop_slots + index
|
||||||
|
|
||||||
|
@ -477,11 +486,9 @@ def main(args, seed=None):
|
||||||
er_hint_data[player][location_id] = main_entrance.name
|
er_hint_data[player][location_id] = main_entrance.name
|
||||||
oldmancaves.append(((location_id, player), (item.code, player)))
|
oldmancaves.append(((location_id, player), (item.code, player)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
FillDisabledShopSlots(world)
|
FillDisabledShopSlots(world)
|
||||||
|
|
||||||
def write_multidata(roms, mods):
|
def write_multidata(roms, outputs):
|
||||||
import base64
|
import base64
|
||||||
import NetUtils
|
import NetUtils
|
||||||
for future in roms:
|
for future in roms:
|
||||||
|
@ -498,11 +505,11 @@ def main(args, seed=None):
|
||||||
client_versions[slot] = (0, 0, 3)
|
client_versions[slot] = (0, 0, 3)
|
||||||
games[slot] = world.game[slot]
|
games[slot] = world.game[slot]
|
||||||
connect_names = {base64.b64encode(rom_name).decode(): (team, slot) for
|
connect_names = {base64.b64encode(rom_name).decode(): (team, slot) for
|
||||||
slot, team, rom_name in rom_names}
|
slot, team, rom_name in rom_names}
|
||||||
precollected_items = {player: [] for player in range(1, world.players+1)}
|
precollected_items = {player: [] for player in range(1, world.players + 1)}
|
||||||
for item in world.precollected_items:
|
for item in world.precollected_items:
|
||||||
precollected_items[item.player].append(item.code)
|
precollected_items[item.player].append(item.code)
|
||||||
precollected_hints = {player: set() for player in range(1, world.players+1)}
|
precollected_hints = {player: set() for player in range(1, world.players + 1)}
|
||||||
# for now special case Factorio visibility
|
# for now special case Factorio visibility
|
||||||
sending_visible_players = set()
|
sending_visible_players = set()
|
||||||
for player in world.factorio_player_ids:
|
for player in world.factorio_player_ids:
|
||||||
|
@ -538,7 +545,7 @@ def main(args, seed=None):
|
||||||
precollected_hints[location.item.player].add(hint)
|
precollected_hints[location.item.player].add(hint)
|
||||||
|
|
||||||
multidata = zlib.compress(pickle.dumps({
|
multidata = zlib.compress(pickle.dumps({
|
||||||
"slot_data" : slot_data,
|
"slot_data": slot_data,
|
||||||
"games": games,
|
"games": games,
|
||||||
"names": parsed_names,
|
"names": parsed_names,
|
||||||
"connect_names": connect_names,
|
"connect_names": connect_names,
|
||||||
|
@ -559,10 +566,10 @@ def main(args, seed=None):
|
||||||
with open(output_path('%s.archipelago' % outfilebase), 'wb') as f:
|
with open(output_path('%s.archipelago' % outfilebase), 'wb') as f:
|
||||||
f.write(bytes([1])) # version of format
|
f.write(bytes([1])) # version of format
|
||||||
f.write(multidata)
|
f.write(multidata)
|
||||||
for future in mods:
|
for future in outputs:
|
||||||
future.result() # collect errors if they occured
|
future.result() # collect errors if they occured
|
||||||
|
|
||||||
multidata_task = pool.submit(write_multidata, rom_futures, mod_futures)
|
multidata_task = pool.submit(write_multidata, rom_futures, output_file_futures)
|
||||||
if not check_accessibility_task.result():
|
if not check_accessibility_task.result():
|
||||||
if not world.can_beat_game():
|
if not world.can_beat_game():
|
||||||
raise Exception("Game appears as unbeatable. Aborting.")
|
raise Exception("Game appears as unbeatable. Aborting.")
|
||||||
|
@ -571,7 +578,7 @@ def main(args, seed=None):
|
||||||
if multidata_task:
|
if multidata_task:
|
||||||
multidata_task.result() # retrieve exception if one exists
|
multidata_task.result() # retrieve exception if one exists
|
||||||
pool.shutdown() # wait for all queued tasks to complete
|
pool.shutdown() # wait for all queued tasks to complete
|
||||||
for player in world.minecraft_player_ids: # Doing this after shutdown prevents the .apmc from being generated if there's an error
|
for player in world.minecraft_player_ids: # Doing this after shutdown prevents the .apmc from being generated if there's an error
|
||||||
generate_mc_data(world, player)
|
generate_mc_data(world, player)
|
||||||
if not args.skip_playthrough:
|
if not args.skip_playthrough:
|
||||||
logger.info('Calculating playthrough.')
|
logger.info('Calculating playthrough.')
|
||||||
|
@ -626,7 +633,8 @@ def create_playthrough(world):
|
||||||
to_delete = set()
|
to_delete = set()
|
||||||
for location in sphere:
|
for location in sphere:
|
||||||
# we remove the item at location and check if game is still beatable
|
# we remove the item at location and check if game is still beatable
|
||||||
logging.debug('Checking if %s (Player %d) is required to beat the game.', location.item.name, location.item.player)
|
logging.debug('Checking if %s (Player %d) is required to beat the game.', location.item.name,
|
||||||
|
location.item.player)
|
||||||
old_item = location.item
|
old_item = location.item
|
||||||
location.item = None
|
location.item = None
|
||||||
if world.can_beat_game(state_cache[num]):
|
if world.can_beat_game(state_cache[num]):
|
||||||
|
@ -671,7 +679,8 @@ def create_playthrough(world):
|
||||||
|
|
||||||
collection_spheres.append(sphere)
|
collection_spheres.append(sphere)
|
||||||
|
|
||||||
logging.debug('Calculated final sphere %i, containing %i of %i progress items.', len(collection_spheres), len(sphere), len(required_locations))
|
logging.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(f'Not all required items reachable. Unreachable locations: {required_locations}')
|
raise RuntimeError(f'Not all required items reachable. Unreachable locations: {required_locations}')
|
||||||
|
|
||||||
|
@ -690,14 +699,22 @@ def create_playthrough(world):
|
||||||
|
|
||||||
world.spoiler.paths = dict()
|
world.spoiler.paths = dict()
|
||||||
for player in range(1, world.players + 1):
|
for player in range(1, world.players + 1):
|
||||||
world.spoiler.paths.update({ str(location) : get_path(state, location.parent_region) for sphere in collection_spheres for location in sphere if location.player == player})
|
world.spoiler.paths.update(
|
||||||
|
{str(location): get_path(state, location.parent_region) for sphere in collection_spheres for location in
|
||||||
|
sphere if location.player == player})
|
||||||
if player in world.alttp_player_ids:
|
if player in world.alttp_player_ids:
|
||||||
for path in dict(world.spoiler.paths).values():
|
for path in dict(world.spoiler.paths).values():
|
||||||
if any(exit == 'Pyramid Fairy' for (_, exit) in path):
|
if any(exit == 'Pyramid Fairy' for (_, exit) in path):
|
||||||
if world.mode[player] != 'inverted':
|
if world.mode[player] != 'inverted':
|
||||||
world.spoiler.paths[str(world.get_region('Big Bomb Shop', player))] = get_path(state, world.get_region('Big Bomb Shop', player))
|
world.spoiler.paths[str(world.get_region('Big Bomb Shop', player))] = get_path(state,
|
||||||
|
world.get_region(
|
||||||
|
'Big Bomb Shop',
|
||||||
|
player))
|
||||||
else:
|
else:
|
||||||
world.spoiler.paths[str(world.get_region('Inverted Big Bomb Shop', player))] = get_path(state, world.get_region('Inverted Big Bomb Shop', player))
|
world.spoiler.paths[str(world.get_region('Inverted Big Bomb Shop', player))] = get_path(state,
|
||||||
|
world.get_region(
|
||||||
|
'Inverted Big Bomb Shop',
|
||||||
|
player))
|
||||||
|
|
||||||
# we can finally output our playthrough
|
# we can finally output our playthrough
|
||||||
world.spoiler.playthrough = {"0": sorted([str(item) for item in world.precollected_items if item.advancement])}
|
world.spoiler.playthrough = {"0": sorted([str(item) for item in world.precollected_items if item.advancement])}
|
||||||
|
|
31
Options.py
31
Options.py
|
@ -22,6 +22,37 @@ class AssembleOptions(type):
|
||||||
return super(AssembleOptions, mcs).__new__(mcs, name, bases, attrs)
|
return super(AssembleOptions, mcs).__new__(mcs, name, bases, attrs)
|
||||||
|
|
||||||
|
|
||||||
|
class AssembleCategoryPath(type):
|
||||||
|
def __new__(mcs, name, bases, attrs):
|
||||||
|
path = []
|
||||||
|
for base in bases:
|
||||||
|
if hasattr(base, "segment"):
|
||||||
|
path += base.segment
|
||||||
|
path += attrs["segment"]
|
||||||
|
attrs["path"] = path
|
||||||
|
return super(AssembleCategoryPath, mcs).__new__(mcs, name, bases, attrs)
|
||||||
|
|
||||||
|
|
||||||
|
class RootCategory(metaclass=AssembleCategoryPath):
|
||||||
|
segment = []
|
||||||
|
|
||||||
|
|
||||||
|
class LttPCategory(RootCategory):
|
||||||
|
segment = ["A Link to the Past"]
|
||||||
|
|
||||||
|
|
||||||
|
class LttPRomCategory(LttPCategory):
|
||||||
|
segment = ["rom"]
|
||||||
|
|
||||||
|
|
||||||
|
class FactorioCategory(RootCategory):
|
||||||
|
segment = ["Factorio"]
|
||||||
|
|
||||||
|
|
||||||
|
class MinecraftCategory(RootCategory):
|
||||||
|
segment = ["Minecraft"]
|
||||||
|
|
||||||
|
|
||||||
class Option(metaclass=AssembleOptions):
|
class Option(metaclass=AssembleOptions):
|
||||||
value: int
|
value: int
|
||||||
name_lookup: typing.Dict[int, str]
|
name_lookup: typing.Dict[int, str]
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
from BaseClasses import MultiWorld
|
||||||
|
|
||||||
|
class AutoWorldRegister(type):
|
||||||
|
world_types = {}
|
||||||
|
|
||||||
|
def __new__(cls, name, bases, dct):
|
||||||
|
new_class = super().__new__(cls, name, bases, dct)
|
||||||
|
if "game" in dct:
|
||||||
|
AutoWorldRegister.world_types[dct["game"]] = new_class
|
||||||
|
return new_class
|
||||||
|
|
||||||
|
|
||||||
|
def call_single(world: MultiWorld, method_name: str, player: int):
|
||||||
|
method = getattr(world.worlds[player], method_name)
|
||||||
|
return method(world, player)
|
||||||
|
|
||||||
|
|
||||||
|
def call_all(world: MultiWorld, method_name: str):
|
||||||
|
for player in world.player_ids:
|
||||||
|
call_single(world, method_name, player)
|
||||||
|
|
||||||
|
|
||||||
|
class World(metaclass=AutoWorldRegister):
|
||||||
|
"""A World object encompasses a game's Items, Locations, Rules and additional data or functionality required.
|
||||||
|
A Game should have its own subclass of World in which it defines the required data structures."""
|
||||||
|
|
||||||
|
def __init__(self, player: int):
|
||||||
|
self.player = int
|
||||||
|
|
||||||
|
def generate_basic(self, world: MultiWorld, player: int):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def generate_output(self, world: MultiWorld, player: int):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create_regions(self, world: MultiWorld, player: int):
|
||||||
|
pass
|
|
@ -1,13 +0,0 @@
|
||||||
class AutoWorldRegister(type):
|
|
||||||
_world_types = {}
|
|
||||||
|
|
||||||
def __new__(cls, name, bases, dct):
|
|
||||||
new_class = super().__new__(cls, name, bases, dct)
|
|
||||||
AutoWorldRegister._world_types[name] = new_class
|
|
||||||
return new_class
|
|
||||||
|
|
||||||
class World(metaclass=AutoWorldRegister):
|
|
||||||
"""A World object encompasses a game's Items, Locations, Rules and additional data or functionality required.
|
|
||||||
A Game should have its own subclass of World in which it defines the required data structures."""
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
|
@ -1,96 +1,10 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from BaseClasses import Location, Item
|
from BaseClasses import Location, Item
|
||||||
|
from ..AutoWorld import World
|
||||||
|
|
||||||
|
class ALTTPWorld(World):
|
||||||
#class ALTTPWorld(World):
|
game: str = "A Link to the Past"
|
||||||
# """WIP"""
|
|
||||||
# def __init__(self, options, slot: int):
|
|
||||||
# self._region_cache = {}
|
|
||||||
# self.slot = slot
|
|
||||||
# self.shuffle = shuffle
|
|
||||||
# self.logic = logic
|
|
||||||
# self.mode = mode
|
|
||||||
# self.swords = swords
|
|
||||||
# self.difficulty = difficulty
|
|
||||||
# self.difficulty_adjustments = difficulty_adjustments
|
|
||||||
# self.timer = timer
|
|
||||||
# self.progressive = progressive
|
|
||||||
# self.goal = goal
|
|
||||||
# self.dungeons = []
|
|
||||||
# self.regions = []
|
|
||||||
# self.shops = []
|
|
||||||
# self.itempool = []
|
|
||||||
# self.seed = None
|
|
||||||
# self.precollected_items = []
|
|
||||||
# self.state = CollectionState(self)
|
|
||||||
# self._cached_entrances = None
|
|
||||||
# self._cached_locations = None
|
|
||||||
# self._entrance_cache = {}
|
|
||||||
# self._location_cache = {}
|
|
||||||
# self.required_locations = []
|
|
||||||
# self.light_world_light_cone = False
|
|
||||||
# self.dark_world_light_cone = False
|
|
||||||
# self.rupoor_cost = 10
|
|
||||||
# self.aga_randomness = True
|
|
||||||
# self.lock_aga_door_in_escape = False
|
|
||||||
# self.save_and_quit_from_boss = True
|
|
||||||
# self.accessibility = accessibility
|
|
||||||
# self.shuffle_ganon = shuffle_ganon
|
|
||||||
# self.fix_gtower_exit = self.shuffle_ganon
|
|
||||||
# self.retro = retro
|
|
||||||
# self.custom = custom
|
|
||||||
# self.customitemarray: List[int] = customitemarray
|
|
||||||
# self.hints = hints
|
|
||||||
# self.dynamic_regions = []
|
|
||||||
# self.dynamic_locations = []
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# self.remote_items = False
|
|
||||||
# self.required_medallions = ['Ether', 'Quake']
|
|
||||||
# self.swamp_patch_required = False
|
|
||||||
# self.powder_patch_required = False
|
|
||||||
# self.ganon_at_pyramid = True
|
|
||||||
# self.ganonstower_vanilla = True
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# self.can_access_trock_eyebridge = None
|
|
||||||
# self.can_access_trock_front = None
|
|
||||||
# self.can_access_trock_big_chest = None
|
|
||||||
# self.can_access_trock_middle = None
|
|
||||||
# self.fix_fake_world = True
|
|
||||||
# self.mapshuffle = False
|
|
||||||
# self.compassshuffle = False
|
|
||||||
# self.keyshuffle = False
|
|
||||||
# self.bigkeyshuffle = False
|
|
||||||
# self.difficulty_requirements = None
|
|
||||||
# self.boss_shuffle = 'none'
|
|
||||||
# self.enemy_shuffle = False
|
|
||||||
# self.enemy_health = 'default'
|
|
||||||
# self.enemy_damage = 'default'
|
|
||||||
# self.killable_thieves = False
|
|
||||||
# self.tile_shuffle = False
|
|
||||||
# self.bush_shuffle = False
|
|
||||||
# self.beemizer = 0
|
|
||||||
# self.escape_assist = []
|
|
||||||
# self.crystals_needed_for_ganon = 7
|
|
||||||
# self.crystals_needed_for_gt = 7
|
|
||||||
# self.open_pyramid = False
|
|
||||||
# self.treasure_hunt_icon = 'Triforce Piece'
|
|
||||||
# self.treasure_hunt_count = 0
|
|
||||||
# self.clock_mode = False
|
|
||||||
# self.can_take_damage = True
|
|
||||||
# self.glitch_boots = True
|
|
||||||
# self.progression_balancing = True
|
|
||||||
# self.local_items = set()
|
|
||||||
# self.triforce_pieces_available = 30
|
|
||||||
# self.triforce_pieces_required = 20
|
|
||||||
# self.shop_shuffle = 'off'
|
|
||||||
# self.shuffle_prizes = "g"
|
|
||||||
# self.sprite_pool = []
|
|
||||||
# self.dark_room_logic = "lamp"
|
|
||||||
# self.restrict_dungeon_item_on_boss = False
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
class ALttPLocation(Location):
|
class ALttPLocation(Location):
|
||||||
|
|
|
@ -15,7 +15,7 @@ def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]:
|
||||||
prerequisites: Dict[str, Set[str]] = {}
|
prerequisites: Dict[str, Set[str]] = {}
|
||||||
layout = world.tech_tree_layout[player].value
|
layout = world.tech_tree_layout[player].value
|
||||||
custom_technologies = world.custom_data[player]["custom_technologies"]
|
custom_technologies = world.custom_data[player]["custom_technologies"]
|
||||||
tech_names: List[str] = list(set(custom_technologies) - world._static_nodes)
|
tech_names: List[str] = list(set(custom_technologies) - world.worlds[player].static_nodes)
|
||||||
tech_names.sort()
|
tech_names.sort()
|
||||||
world.random.shuffle(tech_names)
|
world.random.shuffle(tech_names)
|
||||||
|
|
||||||
|
|
|
@ -1,66 +1,56 @@
|
||||||
from ..BaseWorld import World
|
from ..AutoWorld import World
|
||||||
|
|
||||||
|
|
||||||
from BaseClasses import Region, Entrance, Location, MultiWorld, Item
|
from BaseClasses import Region, Entrance, Location, MultiWorld, Item
|
||||||
from .Technologies import tech_table, recipe_sources, technology_table, advancement_technologies, \
|
from .Technologies import tech_table, recipe_sources, technology_table, advancement_technologies, \
|
||||||
all_ingredient_names, required_technologies, get_rocket_requirements, rocket_recipes
|
all_ingredient_names, required_technologies, get_rocket_requirements, rocket_recipes
|
||||||
from .Shapes import get_shapes
|
from .Shapes import get_shapes
|
||||||
|
from .Mod import generate_mod
|
||||||
|
|
||||||
|
|
||||||
class Factorio(World):
|
class Factorio(World):
|
||||||
|
game: str = "Factorio"
|
||||||
|
static_nodes = {"automation", "logistics"}
|
||||||
|
|
||||||
def generate_basic(self, world: MultiWorld, player: int):
|
def generate_basic(self, world: MultiWorld, player: int):
|
||||||
static_nodes = world._static_nodes = {"automation", "logistics"} # turn dynamic/option?
|
victory_tech_names = get_rocket_requirements(frozenset(rocket_recipes[world.max_science_pack[player].value]))
|
||||||
for tech_name, tech_id in tech_table.items():
|
for tech_name, tech_id in tech_table.items():
|
||||||
tech_item = Item(tech_name, tech_name in advancement_technologies, tech_id, player)
|
tech_item = Item(tech_name, tech_name in advancement_technologies or tech_name in victory_tech_names,
|
||||||
|
tech_id, player)
|
||||||
tech_item.game = "Factorio"
|
tech_item.game = "Factorio"
|
||||||
if tech_name in static_nodes:
|
if tech_name in self.static_nodes:
|
||||||
loc = world.get_location(tech_name, player)
|
world.get_location(tech_name, player).place_locked_item(tech_item)
|
||||||
loc.item = tech_item
|
|
||||||
loc.locked = True
|
|
||||||
loc.event = tech_item.advancement
|
|
||||||
else:
|
else:
|
||||||
world.itempool.append(tech_item)
|
world.itempool.append(tech_item)
|
||||||
world.custom_data[player]["custom_technologies"] = custom_technologies = set_custom_technologies(world, player)
|
world.custom_data[player]["custom_technologies"] = custom_technologies = set_custom_technologies(world, player)
|
||||||
set_rules(world, player, custom_technologies)
|
set_rules(world, player, custom_technologies)
|
||||||
|
|
||||||
def gen_factorio(world: MultiWorld, player: int):
|
def generate_output(self, world: MultiWorld, player: int):
|
||||||
static_nodes = world._static_nodes = {"automation", "logistics"} # turn dynamic/option?
|
generate_mod(world, player)
|
||||||
victory_tech_names = get_rocket_requirements(frozenset(rocket_recipes[world.max_science_pack[player].value]))
|
|
||||||
for tech_name, tech_id in tech_table.items():
|
|
||||||
tech_item = Item(tech_name, tech_name in advancement_technologies or tech_name in victory_tech_names,
|
|
||||||
tech_id, player)
|
|
||||||
tech_item.game = "Factorio"
|
|
||||||
if tech_name in static_nodes:
|
|
||||||
world.get_location(tech_name, player).place_locked_item(tech_item)
|
|
||||||
else:
|
|
||||||
world.itempool.append(tech_item)
|
|
||||||
world.custom_data[player]["custom_technologies"] = custom_technologies = set_custom_technologies(world, player)
|
|
||||||
set_rules(world, player, custom_technologies)
|
|
||||||
|
|
||||||
|
def create_regions(self, world: MultiWorld, player: int):
|
||||||
|
menu = Region("Menu", None, "Menu", player)
|
||||||
|
crash = Entrance(player, "Crash Land", menu)
|
||||||
|
menu.exits.append(crash)
|
||||||
|
nauvis = Region("Nauvis", None, "Nauvis", player)
|
||||||
|
nauvis.world = menu.world = world
|
||||||
|
|
||||||
def factorio_create_regions(world: MultiWorld, player: int):
|
for tech_name, tech_id in tech_table.items():
|
||||||
menu = Region("Menu", None, "Menu", player)
|
tech = Location(player, tech_name, tech_id, nauvis)
|
||||||
crash = Entrance(player, "Crash Land", menu)
|
nauvis.locations.append(tech)
|
||||||
menu.exits.append(crash)
|
tech.game = "Factorio"
|
||||||
nauvis = Region("Nauvis", None, "Nauvis", player)
|
location = Location(player, "Rocket Launch", None, nauvis)
|
||||||
nauvis.world = menu.world = world
|
|
||||||
|
|
||||||
for tech_name, tech_id in tech_table.items():
|
|
||||||
tech = Location(player, tech_name, tech_id, nauvis)
|
|
||||||
nauvis.locations.append(tech)
|
|
||||||
tech.game = "Factorio"
|
|
||||||
location = Location(player, "Rocket Launch", None, nauvis)
|
|
||||||
nauvis.locations.append(location)
|
|
||||||
event = Item("Victory", True, None, player)
|
|
||||||
world.push_item(location, event, False)
|
|
||||||
location.event = location.locked = True
|
|
||||||
for ingredient in all_ingredient_names:
|
|
||||||
location = Location(player, f"Automate {ingredient}", None, nauvis)
|
|
||||||
nauvis.locations.append(location)
|
nauvis.locations.append(location)
|
||||||
event = Item(f"Automated {ingredient}", True, None, player)
|
event = Item("Victory", True, None, player)
|
||||||
world.push_item(location, event, False)
|
world.push_item(location, event, False)
|
||||||
location.event = location.locked = True
|
location.event = location.locked = True
|
||||||
crash.connect(nauvis)
|
for ingredient in all_ingredient_names:
|
||||||
world.regions += [menu, nauvis]
|
location = Location(player, f"Automate {ingredient}", None, nauvis)
|
||||||
|
nauvis.locations.append(location)
|
||||||
|
event = Item(f"Automated {ingredient}", True, None, player)
|
||||||
|
world.push_item(location, event, False)
|
||||||
|
location.event = location.locked = True
|
||||||
|
crash.connect(nauvis)
|
||||||
|
world.regions += [menu, nauvis]
|
||||||
|
|
||||||
|
|
||||||
def set_custom_technologies(world: MultiWorld, player: int):
|
def set_custom_technologies(world: MultiWorld, player: int):
|
||||||
|
|
|
@ -8,7 +8,10 @@ from .Regions import create_regions
|
||||||
from .Rules import set_rules
|
from .Rules import set_rules
|
||||||
|
|
||||||
from BaseClasses import Region, Entrance, Location, MultiWorld, Item
|
from BaseClasses import Region, Entrance, Location, MultiWorld, Item
|
||||||
|
from ..AutoWorld import World
|
||||||
|
|
||||||
|
class HKWorld(World):
|
||||||
|
game: str = "Hollow Knight"
|
||||||
|
|
||||||
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||||
ret = Region(name, None, name, player)
|
ret = Region(name, None, name, player)
|
||||||
|
|
|
@ -4,15 +4,24 @@ from .Locations import exclusion_table, events_table
|
||||||
from .Regions import link_minecraft_structures
|
from .Regions import link_minecraft_structures
|
||||||
from .Rules import set_rules
|
from .Rules import set_rules
|
||||||
|
|
||||||
from BaseClasses import Region, Entrance, Location, MultiWorld, Item
|
from BaseClasses import MultiWorld
|
||||||
from Options import minecraft_options
|
from Options import minecraft_options
|
||||||
|
from ..AutoWorld import World
|
||||||
|
|
||||||
|
|
||||||
|
class MinecraftWorld(World):
|
||||||
|
game: str = "Minecraft"
|
||||||
|
|
||||||
|
|
||||||
client_version = (0, 3)
|
client_version = (0, 3)
|
||||||
|
|
||||||
|
|
||||||
def get_mc_data(world: MultiWorld, player: int):
|
def get_mc_data(world: MultiWorld, player: int):
|
||||||
exits = ["Overworld Structure 1", "Overworld Structure 2", "Nether Structure 1", "Nether Structure 2", "The End Structure"]
|
exits = ["Overworld Structure 1", "Overworld Structure 2", "Nether Structure 1", "Nether Structure 2",
|
||||||
|
"The End Structure"]
|
||||||
return {
|
return {
|
||||||
'world_seed': Random(world.rom_seeds[player]).getrandbits(32), # consistent and doesn't interfere with other generation
|
'world_seed': Random(world.rom_seeds[player]).getrandbits(32),
|
||||||
|
# consistent and doesn't interfere with other generation
|
||||||
'seed_name': world.seed_name,
|
'seed_name': world.seed_name,
|
||||||
'player_name': world.get_player_names(player),
|
'player_name': world.get_player_names(player),
|
||||||
'player_id': player,
|
'player_id': player,
|
||||||
|
@ -20,25 +29,27 @@ def get_mc_data(world: MultiWorld, player: int):
|
||||||
'structures': {exit: world.get_entrance(exit, player).connected_region.name for exit in exits}
|
'structures': {exit: world.get_entrance(exit, player).connected_region.name for exit in exits}
|
||||||
}
|
}
|
||||||
|
|
||||||
def generate_mc_data(world: MultiWorld, player: int):
|
|
||||||
|
def generate_mc_data(world: MultiWorld, player: int):
|
||||||
import base64, json
|
import base64, json
|
||||||
from Utils import output_path
|
from Utils import output_path
|
||||||
|
|
||||||
data = get_mc_data(world, player)
|
data = get_mc_data(world, player)
|
||||||
filename = f"AP_{world.seed_name}_P{player}_{world.get_player_names(player)}.apmc"
|
filename = f"AP_{world.seed_name}_P{player}_{world.get_player_names(player)}.apmc"
|
||||||
with open(output_path(filename), 'wb') as f:
|
with open(output_path(filename), 'wb') as f:
|
||||||
f.write(base64.b64encode(bytes(json.dumps(data), 'utf-8')))
|
f.write(base64.b64encode(bytes(json.dumps(data), 'utf-8')))
|
||||||
|
|
||||||
def fill_minecraft_slot_data(world: MultiWorld, player: int):
|
|
||||||
|
def fill_minecraft_slot_data(world: MultiWorld, player: int):
|
||||||
slot_data = get_mc_data(world, player)
|
slot_data = get_mc_data(world, player)
|
||||||
for option_name in minecraft_options:
|
for option_name in minecraft_options:
|
||||||
option = getattr(world, option_name)[player]
|
option = getattr(world, option_name)[player]
|
||||||
slot_data[option_name] = int(option.value)
|
slot_data[option_name] = int(option.value)
|
||||||
return slot_data
|
return slot_data
|
||||||
|
|
||||||
# Generates the item pool given the table and frequencies in Items.py.
|
|
||||||
def minecraft_gen_item_pool(world: MultiWorld, player: int):
|
|
||||||
|
|
||||||
|
# Generates the item pool given the table and frequencies in Items.py.
|
||||||
|
def minecraft_gen_item_pool(world: MultiWorld, player: int):
|
||||||
pool = []
|
pool = []
|
||||||
for item_name, item_data in item_table.items():
|
for item_name, item_data in item_table.items():
|
||||||
for count in range(item_frequencies.get(item_name, 1)):
|
for count in range(item_frequencies.get(item_name, 1)):
|
||||||
|
@ -47,8 +58,8 @@ def minecraft_gen_item_pool(world: MultiWorld, player: int):
|
||||||
prefill_pool = {}
|
prefill_pool = {}
|
||||||
prefill_pool.update(events_table)
|
prefill_pool.update(events_table)
|
||||||
exclusion_pools = ['hard', 'insane', 'postgame']
|
exclusion_pools = ['hard', 'insane', 'postgame']
|
||||||
for key in exclusion_pools:
|
for key in exclusion_pools:
|
||||||
if not getattr(world, f"include_{key}_advancements")[player]:
|
if not getattr(world, f"include_{key}_advancements")[player]:
|
||||||
prefill_pool.update(exclusion_table[key])
|
prefill_pool.update(exclusion_table[key])
|
||||||
|
|
||||||
for loc_name, item_name in prefill_pool.items():
|
for loc_name, item_name in prefill_pool.items():
|
||||||
|
@ -62,9 +73,9 @@ def minecraft_gen_item_pool(world: MultiWorld, player: int):
|
||||||
|
|
||||||
world.itempool += pool
|
world.itempool += pool
|
||||||
|
|
||||||
# Generate Minecraft world.
|
|
||||||
|
# Generate Minecraft world.
|
||||||
def gen_minecraft(world: MultiWorld, player: int):
|
def gen_minecraft(world: MultiWorld, player: int):
|
||||||
link_minecraft_structures(world, player)
|
link_minecraft_structures(world, player)
|
||||||
minecraft_gen_item_pool(world, player)
|
minecraft_gen_item_pool(world, player)
|
||||||
set_rules(world, player)
|
set_rules(world, player)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue