mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 04:01:32 -06:00
Multiple: Followed a rabbit hole of moving LttP Rom generation to AutoWorld
Generator: Re-allow names with spaces (and see what breaks) Generator: Removed teams (Note that teams are intended to move from a generation step feature to a server runtime feature, allowing dynamic creation of an already generated MW) LttP: All Rom Options are now on the new system LttP: palette option "random" is now called "good" LttP: Roms are now created as part of the general output file creation step LttP: disable Music is now Music, removing potential double negatives LttP & Factorio: Progressive option random is now grouped_random LttP: Enemy damage option random is now Enemy damage: chaos
This commit is contained in:
107
Main.py
107
Main.py
@@ -10,17 +10,15 @@ import tempfile
|
||||
import zipfile
|
||||
from typing import Dict, Tuple
|
||||
|
||||
from BaseClasses import MultiWorld, CollectionState, Region, Item
|
||||
from BaseClasses import MultiWorld, CollectionState, Region
|
||||
from worlds.alttp.Items import item_name_groups
|
||||
from worlds.alttp.Regions import lookup_vanilla_location_to_entrance
|
||||
from worlds.alttp.Rom import patch_rom, patch_race_rom, patch_enemizer, apply_rom_settings, LocalRom, get_hash_string
|
||||
from Fill import distribute_items_restrictive, flood_items, balance_multiworld_progression, distribute_planned
|
||||
from worlds.alttp.Shops import ShopSlotFill, SHOP_ID_START, total_shop_slots, FillDisabledShopSlots
|
||||
from worlds.alttp.ItemPool import difficulties, fill_prizes
|
||||
from Utils import output_path, parse_player_names, get_options, __version__, version_tuple
|
||||
from worlds.alttp.ItemPool import difficulties
|
||||
from Utils import output_path, get_options, __version__, version_tuple
|
||||
from worlds.generic.Rules import locality_rules, exclusion_rules
|
||||
from worlds import AutoWorld
|
||||
import Patch
|
||||
|
||||
seeddigits = 20
|
||||
|
||||
@@ -66,7 +64,6 @@ def main(args, seed=None):
|
||||
world.difficulty = args.difficulty.copy()
|
||||
world.item_functionality = args.item_functionality.copy()
|
||||
world.timer = args.timer.copy()
|
||||
world.progressive = args.progressive.copy()
|
||||
world.goal = args.goal.copy()
|
||||
world.local_items = args.local_items.copy()
|
||||
if hasattr(args, "algorithm"): # current GUI options
|
||||
@@ -99,7 +96,6 @@ def main(args, seed=None):
|
||||
world.blue_clock_time = args.blue_clock_time.copy()
|
||||
world.green_clock_time = args.green_clock_time.copy()
|
||||
world.shufflepots = args.shufflepots.copy()
|
||||
world.progressive = args.progressive.copy()
|
||||
world.dungeon_counters = args.dungeon_counters.copy()
|
||||
world.glitch_boots = args.glitch_boots.copy()
|
||||
world.triforce_pieces_available = args.triforce_pieces_available.copy()
|
||||
@@ -117,6 +113,10 @@ def main(args, seed=None):
|
||||
world.required_medallions = args.required_medallions.copy()
|
||||
world.game = args.game.copy()
|
||||
world.set_options(args)
|
||||
world.player_name = args.name.copy()
|
||||
world.alttp_rom = args.rom
|
||||
world.enemizer = args.enemizercli
|
||||
world.sprite = args.sprite.copy()
|
||||
world.glitch_triforce = args.glitch_triforce # This is enabled/disabled globally, no per player option.
|
||||
|
||||
world.slot_seeds = {player: random.Random(world.random.getrandbits(64)) for player in
|
||||
@@ -148,13 +148,6 @@ def main(args, seed=None):
|
||||
for name, cls in AutoWorld.AutoWorldRegister.world_types.items():
|
||||
logger.info(f" {name:{longest_name}}: {len(cls.item_names):3} Items | {len(cls.location_names):3} Locations")
|
||||
|
||||
parsed_names = parse_player_names(args.names, world.players, args.teams)
|
||||
world.teams = len(parsed_names)
|
||||
for i, team in enumerate(parsed_names, 1):
|
||||
if world.players > 1:
|
||||
logger.info('%s%s', 'Team%d: ' % i if world.teams > 1 else 'Players: ', ', '.join(team))
|
||||
for player, name in enumerate(team, 1):
|
||||
world.player_names[player].append(name)
|
||||
|
||||
logger.info('')
|
||||
for player in world.get_game_players("A Link to the Past"):
|
||||
@@ -241,63 +234,18 @@ def main(args, seed=None):
|
||||
|
||||
logger.info('Generating output files.')
|
||||
outfilebase = 'AP_' + world.seed_name
|
||||
rom_names = []
|
||||
|
||||
def _gen_rom(team: int, player: int, output_directory:str):
|
||||
use_enemizer = (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[player]
|
||||
or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default'
|
||||
or world.shufflepots[player] or world.bush_shuffle[player]
|
||||
or world.killable_thieves[player])
|
||||
|
||||
rom = LocalRom(args.rom)
|
||||
|
||||
patch_rom(world, rom, player, team, use_enemizer)
|
||||
|
||||
if use_enemizer:
|
||||
patch_enemizer(world, team, player, rom, args.enemizercli, output_directory)
|
||||
|
||||
if args.race:
|
||||
patch_race_rom(rom, world, player)
|
||||
|
||||
world.spoiler.hashes[(player, team)] = get_hash_string(rom.hash)
|
||||
|
||||
palettes_options = {
|
||||
'dungeon': args.uw_palettes[player],
|
||||
'overworld': args.ow_palettes[player],
|
||||
'hud': args.hud_palettes[player],
|
||||
'sword': args.sword_palettes[player],
|
||||
'shield': args.shield_palettes[player],
|
||||
'link': args.link_palettes[player]
|
||||
}
|
||||
|
||||
apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player],
|
||||
args.fastmenu[player], args.disablemusic[player], args.sprite[player],
|
||||
palettes_options, world, player, True,
|
||||
reduceflashing=args.reduceflashing[player] or args.race,
|
||||
triforcehud=args.triforcehud[player])
|
||||
|
||||
outfilepname = f'_P{player}'
|
||||
outfilepname += f"_{world.player_names[player][team].replace(' ', '_')}" \
|
||||
if world.player_names[player][team] != 'Player%d' % player else ''
|
||||
|
||||
rompath = os.path.join(output_directory, f'{outfilebase}{outfilepname}.sfc')
|
||||
rom.write_to_file(rompath, hide_enemizer=True)
|
||||
Patch.create_patch_file(rompath, player=player, player_name=world.player_names[player][team])
|
||||
os.unlink(rompath)
|
||||
return player, team, bytes(rom.name)
|
||||
|
||||
pool = concurrent.futures.ThreadPoolExecutor()
|
||||
|
||||
output = tempfile.TemporaryDirectory()
|
||||
with output as temp_dir:
|
||||
check_accessibility_task = pool.submit(world.fulfills_accessibility)
|
||||
rom_futures = []
|
||||
|
||||
output_file_futures = []
|
||||
for team in range(world.teams):
|
||||
for player in world.get_game_players("A Link to the Past"):
|
||||
rom_futures.append(pool.submit(_gen_rom, team, player, temp_dir))
|
||||
|
||||
for player in world.player_ids:
|
||||
output_file_futures.append(pool.submit(AutoWorld.call_single, world, "generate_output", player, temp_dir))
|
||||
output_file_futures.append(pool.submit(AutoWorld.call_stage, world, "generate_output", temp_dir))
|
||||
|
||||
def get_entrance_to_region(region: Region):
|
||||
for entrance in region.entrances:
|
||||
@@ -365,12 +313,8 @@ def main(args, seed=None):
|
||||
|
||||
FillDisabledShopSlots(world)
|
||||
|
||||
def write_multidata(roms, outputs):
|
||||
import base64
|
||||
def write_multidata():
|
||||
import NetUtils
|
||||
for future in roms:
|
||||
rom_name = future.result()
|
||||
rom_names.append(rom_name)
|
||||
slot_data = {}
|
||||
client_versions = {}
|
||||
minimum_versions = {"server": (0, 1, 1), "clients": client_versions}
|
||||
@@ -378,8 +322,6 @@ def main(args, seed=None):
|
||||
for slot in world.player_ids:
|
||||
client_versions[slot] = world.worlds[slot].get_required_client_version()
|
||||
games[slot] = world.game[slot]
|
||||
connect_names = {base64.b64encode(rom_name).decode(): (team, slot) for
|
||||
slot, team, rom_name in rom_names}
|
||||
precollected_items = {player: [] for player in range(1, world.players + 1)}
|
||||
for item in world.precollected_items:
|
||||
precollected_items[item.player].append(item.code)
|
||||
@@ -390,11 +332,6 @@ def main(args, seed=None):
|
||||
if world.tech_tree_information[player].value == 2:
|
||||
sending_visible_players.add(player)
|
||||
|
||||
for i, team in enumerate(parsed_names):
|
||||
for player, name in enumerate(team, 1):
|
||||
if player not in world.get_game_players("A Link to the Past"):
|
||||
connect_names[name] = (i, player)
|
||||
|
||||
for slot in world.player_ids:
|
||||
slot_data[slot] = world.worlds[slot].fill_slot_data()
|
||||
|
||||
@@ -414,11 +351,11 @@ def main(args, seed=None):
|
||||
precollected_hints[location.player].add(hint)
|
||||
precollected_hints[location.item.player].add(hint)
|
||||
|
||||
multidata = zlib.compress(pickle.dumps({
|
||||
multidata = {
|
||||
"slot_data": slot_data,
|
||||
"games": games,
|
||||
"names": parsed_names,
|
||||
"connect_names": connect_names,
|
||||
"names": [{player: name for player, name in world.player_name.items()}],
|
||||
"connect_names": {name: (0, player) for player, name in world.player_name.items()},
|
||||
"remote_items": {player for player in world.player_ids if
|
||||
world.worlds[player].remote_items},
|
||||
"locations": locations_data,
|
||||
@@ -431,15 +368,17 @@ def main(args, seed=None):
|
||||
"tags": ["AP"],
|
||||
"minimum_versions": minimum_versions,
|
||||
"seed_name": world.seed_name
|
||||
}), 9)
|
||||
}
|
||||
AutoWorld.call_all(world, "modify_multidata", multidata)
|
||||
|
||||
with open(os.path.join(temp_dir, '%s.archipelago' % outfilebase), 'wb') as f:
|
||||
multidata = zlib.compress(pickle.dumps(multidata), 9)
|
||||
|
||||
with open(os.path.join(temp_dir, f'{outfilebase}.archipelago'), 'wb') as f:
|
||||
f.write(bytes([1])) # version of format
|
||||
f.write(multidata)
|
||||
for future in outputs:
|
||||
future.result() # collect errors if they occured
|
||||
|
||||
multidata_task = pool.submit(write_multidata, rom_futures, output_file_futures)
|
||||
|
||||
multidata_task = pool.submit(write_multidata)
|
||||
if not check_accessibility_task.result():
|
||||
if not world.can_beat_game():
|
||||
raise Exception("Game appears as unbeatable. Aborting.")
|
||||
@@ -451,8 +390,10 @@ def main(args, seed=None):
|
||||
if not args.skip_playthrough:
|
||||
logger.info('Calculating playthrough.')
|
||||
create_playthrough(world)
|
||||
if args.create_spoiler: # needs spoiler.hashes to be filled, that depend on rom_futures being done
|
||||
if args.create_spoiler:
|
||||
world.spoiler.to_file(os.path.join(temp_dir, '%s_Spoiler.txt' % outfilebase))
|
||||
for future in output_file_futures:
|
||||
future.result()
|
||||
zipfilename = output_path(f"AP_{world.seed_name}.zip")
|
||||
logger.info(f'Creating final archive at {zipfilename}.')
|
||||
with zipfile.ZipFile(zipfilename, mode="w", compression=zipfile.ZIP_DEFLATED,
|
||||
|
Reference in New Issue
Block a user