wip: initial disassembly of new gui and cli commands

This commit is contained in:
Lucy 2024-08-31 01:23:57 +02:00
parent b2bcbb3027
commit 0c9d1a472f
5 changed files with 413 additions and 291 deletions

418
GUI.py
View file

@ -10,299 +10,275 @@
# Source Generated with Decompyle++
# File: GUI.pyc (Python 3.8)
import PySimpleGUI as ui
import zlib
import time
import PySimpleGUI as sg
import threading
import os
ui.theme('SystemDefault')
from pathlib import Path
from MFOR import file_hash
sg.theme('SystemDefault')
tt_race = ' Generating a race seed will not generate a spoiler log. '
tt_createpatch = ' Automatically creates a .bps patch after generating a randomized game, so you can easily share with others. '
tt_difficulty = ' Higher difficulty means harder tricks. Difficulty 0 means no tricks are expected outside of vanilla strategies. '
tt_itempool = ' Limit where important items can be placed. If checked, major upgrades will only be in Data rooms, \n at bosses, or at vanilla E-tank locations. If unchecked, any item can be anywhere within logic. '
tt_missileswithupgrades = ' If checked, collecting any major Missile item will enable Missiles. Logic will account for this setting. '
tt_pbswithoutbombs = ' If checked, Power Bombs can be used without having regular Bombs. Logic will account for this setting. '
tt_damageruns = ' Allow logic to require Samus to run through damaging environments to acquire important items. \n Examples include heated rooms, electrified water, and lava. '
tt_splitsecurity = ' If checked, each door color must be unlocked independently. \n If unchecked, unlocking higher security levels will unlock all security levels below it. \n E.G.: unlocking Level 2 (Green) doors will also unlock Level 1 (Blue) doors. '
tt_SectorShuffle = ' Randomly shuffle the arrangement of the Sectors. '
tt_HideItems = ' Make all items appear as ? tanks. '
tt_patch = ' Create a .bps patch after generating a game, so you can easily share with others. '
tt_difficulty = ' Higher difficulty means harder tricks can be required.\n Difficulty 0 means no tricks are expected outside of vanilla strategies. '
tt_major_minor = ' Limit where important items can be placed. If checked, major items will only be in Data rooms, \n at bosses, or at vanilla E-tank locations. If unchecked, any item can be anywhere within logic. '
tt_missile_data = ' If checked, collecting any Missile data will enable Missiles. Logic will account for this setting. '
tt_pb_data = ' If checked, Power Bombs can be used without having regular Bombs. Logic will account for this setting. '
tt_hazard_runs = ' Allow logic to require Samus to run through hazardous environments to acquire important items. \n Examples include heated rooms, electrified water, and lava. '
tt_split_security = ' If checked, each security level must be unlocked independently. \n If unchecked, unlocking higher security levels will unlock all lesser security levels. \n E.G.: unlocking Level 2 (Green) doors will also unlock Level 1 (Blue) doors. '
tt_shuffle_sectors = ' Randomly shuffle the arrangement of the Sectors. '
tt_shuffle_tubes = ' Randomly shuffle the tube connections between the Sectors. '
tt_hide_items = ' Make all items appear as ? tanks. '
generating = False
def fileHash(fileName):
with open(fileName, 'rb') as fh:
hash = 0
while True:
s = fh.read(65536)
if not s:
break
hash = zlib.crc32(s, hash)
return hash & 0xFFFFFFFF
def rando_thread(window = None, values = None):
global fileName, basegame, failedgen, randoerror, generating, finishGenerating
global failed_gen, rando_error, generating, file_name, failed_gen, generating, finish_generating
values.update({
'Difficulty': int(values.get('Difficulty')) })
if values['Num']:
'-DIFFICULTY-': int(values.get('-DIFFICULTY-')) })
if values['-NUM-']:
values.update({
'Num': int(values.get('Num')) })
'-NUM-': int(values.get('-NUM-')) })
else:
values.update({
'Num': 1 })
if values['Num'] < 1:
'-NUM-': 1 })
if values['-NUM-'] < 1:
values.update({
'Num': 1 })
'-NUM-': 1 })
values.pop(0)
failedgen = False
randoerror = False
# FIXME: whatever this is
# values = (lambda .0 = None: pass# WARNING: Decompyle incomplete
#)(sorted(values.keys()))
failed_gen = False
rando_error = False
generating = True
from Randomizer import start_randomizer
if values['Debug']:
fileName = start_randomizer(basegame, values)
file_name = start_randomizer(base_game, values)
else:
# FIXME: let it explode
#try:
fileName = start_randomizer(basegame, values)
# finally:
# pass
#except SystemExit:
# failedgen = True
#except:
# print('An error occurred, no game was generated.')
# randoerror = True
try:
file_name = start_randomizer(base_game, values)
except SystemExit:
failed_gen = True
generating = False
finishGenerating = True
finish_generating = True
def main_window(debug):
global fileName, basegame, generating, finishGenerating
itemPool = [
global file_name, finish_generating, base_game, finish_generating
item_pool = [
[
ui.Text('Charge Beam'),
ui.Radio('Shuffled', 'radioChargeBeam', default=True),
ui.Radio('Starting', 'radioChargeBeam'),
ui.Radio('Disabled', 'radioChargeBeam')],
sg.Text('Charge Beam'),
sg.Radio('Shuffled', 'radioChargeBeam', default=True),
sg.Radio('Starting', 'radioChargeBeam'),
sg.Radio('Disabled', 'radioChargeBeam')],
[
ui.Text('Wide Beam'),
ui.Radio('Shuffled', 'radioWideBeam', default=True),
ui.Radio('Starting', 'radioWideBeam'),
ui.Radio('Disabled', 'radioWideBeam')],
sg.Text('Wide Beam'),
sg.Radio('Shuffled', 'radioWideBeam', default=True),
sg.Radio('Starting', 'radioWideBeam'),
sg.Radio('Disabled', 'radioWideBeam')],
[
ui.Text('Plasma Beam'),
ui.Radio('Shuffled', 'radioPlasmaBeam', default=True),
ui.Radio('Starting', 'radioPlasmaBeam'),
ui.Radio('Disabled', 'radioPlasmaBeam')],
sg.Text('Plasma Beam'),
sg.Radio('Shuffled', 'radioPlasmaBeam', default=True),
sg.Radio('Starting', 'radioPlasmaBeam'),
sg.Radio('Disabled', 'radioPlasmaBeam')],
[
ui.Text('Wave Beam'),
ui.Radio('Shuffled', 'radioWaveBeam', default=True),
ui.Radio('Starting', 'radioWaveBeam'),
ui.Radio('Disabled', 'radioWaveBeam')],
sg.Text('Wave Beam'),
sg.Radio('Shuffled', 'radioWaveBeam', default=True),
sg.Radio('Starting', 'radioWaveBeam'),
sg.Radio('Disabled', 'radioWaveBeam')],
[
ui.Text('Ice Beam'),
ui.Radio('Shuffled', 'radioIceBeam', default=True),
ui.Radio('Starting', 'radioIceBeam'),
ui.Radio('Disabled', 'radioIceBeam')],
sg.Text('Ice Beam'),
sg.Radio('Shuffled', 'radioIceBeam', default=True),
sg.Radio('Starting', 'radioIceBeam'),
sg.Radio('Disabled', 'radioIceBeam')],
[
ui.Text('Missile Data'),
ui.Radio('Shuffled', 'radioMissileData', default=True),
ui.Radio('Starting', 'radioMissileData'),
ui.Radio('Disabled', 'radioMissileData')],
sg.Text('Missile Data'),
sg.Radio('Shuffled', 'radioMissileData', default=True),
sg.Radio('Starting', 'radioMissileData'),
sg.Radio('Disabled', 'radioMissileData')],
[
ui.Text('Super Missile Data'),
ui.Radio('Shuffled', 'radioSuperMissile', default=True),
ui.Radio('Starting', 'radioSuperMissile'),
ui.Radio('Disabled', 'radioSuperMissile')],
sg.Text('Super Missile Data'),
sg.Radio('Shuffled', 'radioSuperMissile', default=True),
sg.Radio('Starting', 'radioSuperMissile'),
sg.Radio('Disabled', 'radioSuperMissile')],
[
ui.Text('Ice Missile Data'),
ui.Radio('Shuffled', 'radioIceMissile', default=True),
ui.Radio('Starting', 'radioIceMissile'),
ui.Radio('Disabled', 'radioIceMissile')],
sg.Text('Ice Missile Data'),
sg.Radio('Shuffled', 'radioIceMissile', default=True),
sg.Radio('Starting', 'radioIceMissile'),
sg.Radio('Disabled', 'radioIceMissile')],
[
ui.Text('Diffusion Data'),
ui.Radio('Shuffled', 'radioDiffusion', default=True),
ui.Radio('Starting', 'radioDiffusion'),
ui.Radio('Disabled', 'radioDiffusion')],
sg.Text('Diffusion Data'),
sg.Radio('Shuffled', 'radioDiffusion', default=True),
sg.Radio('Starting', 'radioDiffusion'),
sg.Radio('Disabled', 'radioDiffusion')],
[
ui.Text('Bombs'),
ui.Radio('Shuffled', 'radioBombs', default=True),
ui.Radio('Starting', 'radioBombs'),
ui.Radio('Disabled', 'radioBombs')],
sg.Text('Bombs'),
sg.Radio('Shuffled', 'radioBombs', default=True),
sg.Radio('Starting', 'radioBombs'),
sg.Radio('Disabled', 'radioBombs')],
[
ui.Text('Power Bomb Data'),
ui.Radio('Shuffled', 'radioPowerBombData', default=True),
ui.Radio('Starting', 'radioPowerBombData'),
ui.Radio('Disabled', 'radioPowerBombData')],
sg.Text('Power Bomb Data'),
sg.Radio('Shuffled', 'radioPowerBombData', default=True),
sg.Radio('Starting', 'radioPowerBombData'),
sg.Radio('Disabled', 'radioPowerBombData')],
[
ui.Text('Hi-Jump Boots'),
ui.Radio('Shuffled', 'radioHiJump', default=True),
ui.Radio('Starting', 'radioHiJump'),
ui.Radio('Disabled', 'radioHiJump')],
sg.Text('Hi-Jump Boots'),
sg.Radio('Shuffled', 'radioHiJump', default=True),
sg.Radio('Starting', 'radioHiJump'),
sg.Radio('Disabled', 'radioHiJump')],
[
ui.Text('Speed Booster'),
ui.Radio('Shuffled', 'radioSpeedBooster', default=True),
ui.Radio('Starting', 'radioSpeedBooster'),
ui.Radio('Disabled', 'radioSpeedBooster')],
sg.Text('Speed Booster'),
sg.Radio('Shuffled', 'radioSpeedBooster', default=True),
sg.Radio('Starting', 'radioSpeedBooster'),
sg.Radio('Disabled', 'radioSpeedBooster')],
[
ui.Text('Space Jump'),
ui.Radio('Shuffled', 'radioSpaceJump', default=True),
ui.Radio('Starting', 'radioSpaceJump'),
ui.Radio('Disabled', 'radioSpaceJump')],
sg.Text('Space Jump'),
sg.Radio('Shuffled', 'radioSpaceJump', default=True),
sg.Radio('Starting', 'radioSpaceJump'),
sg.Radio('Disabled', 'radioSpaceJump')],
[
ui.Text('Screw Attack'),
ui.Radio('Shuffled', 'radioScrewAttack', default=True),
ui.Radio('Starting', 'radioScrewAttack'),
ui.Radio('Disabled', 'radioScrewAttack')],
sg.Text('Screw Attack'),
sg.Radio('Shuffled', 'radioScrewAttack', default=True),
sg.Radio('Starting', 'radioScrewAttack'),
sg.Radio('Disabled', 'radioScrewAttack')],
[
ui.Text('Varia Suit'),
ui.Radio('Shuffled', 'radioVariaSuit', default=True),
ui.Radio('Starting', 'radioVariaSuit'),
ui.Radio('Disabled', 'radioVariaSuit')],
sg.Text('Varia Suit'),
sg.Radio('Shuffled', 'radioVariaSuit', default=True),
sg.Radio('Starting', 'radioVariaSuit'),
sg.Radio('Disabled', 'radioVariaSuit')],
[
ui.Text('Gravity Suit'),
ui.Radio('Shuffled', 'radioGravitySuit', default=True),
ui.Radio('Starting', 'radioGravitySuit'),
ui.Radio('Disabled', 'radioGravitySuit')],
sg.Text('Gravity Suit'),
sg.Radio('Shuffled', 'radioGravitySuit', default=True),
sg.Radio('Starting', 'radioGravitySuit'),
sg.Radio('Disabled', 'radioGravitySuit')],
[
ui.Text('Morph Ball'),
ui.Radio('Shuffled', 'radioMorphBall', default=True),
ui.Radio('Starting', 'radioMorphBall'),
ui.Radio('Disabled', 'radioMorphBall')],
sg.Text('Morph Ball'),
sg.Radio('Shuffled', 'radioMorphBall', default=True),
sg.Radio('Starting', 'radioMorphBall'),
sg.Radio('Disabled', 'radioMorphBall')],
[
ui.Text('Blue Doors (Level 1)'),
ui.Radio('Standard', 'radioBlueDoors', default=True),
ui.Radio('Starting', 'radioBlueDoors'),
ui.Radio('Shuffled', 'radioBlueDoors')],
sg.Text('Blue Doors (Level 1)'),
sg.Radio('Standard', 'radioBlueDoors', default=True),
sg.Radio('Starting', 'radioBlueDoors'),
sg.Radio('Shuffled', 'radioBlueDoors')],
[
ui.Text('Green Doors (Level 2)'),
ui.Radio('Standard', 'radioGreenDoors', default=True),
ui.Radio('Starting', 'radioGreenDoors'),
ui.Radio('Shuffled', 'radioGreenDoors')],
sg.Text('Green Doors (Level 2)'),
sg.Radio('Standard', 'radioGreenDoors', default=True),
sg.Radio('Starting', 'radioGreenDoors'),
sg.Radio('Shuffled', 'radioGreenDoors')],
[
ui.Text('Yellow Doors (Level 3)'),
ui.Radio('Standard', 'radioYellowDoors', default=True),
ui.Radio('Starting', 'radioYellowDoors'),
ui.Radio('Shuffled', 'radioYellowDoors')],
sg.Text('Yellow Doors (Level 3)'),
sg.Radio('Standard', 'radioYellowDoors', default=True),
sg.Radio('Starting', 'radioYellowDoors'),
sg.Radio('Shuffled', 'radioYellowDoors')],
[
ui.Text('Red Doors (Level 4)'),
ui.Radio('Standard', 'radioRedDoors', default=True),
ui.Radio('Starting', 'radioRedDoors'),
ui.Radio('Shuffled', 'radioRedDoors')]]
sg.Text('Red Doors (Level 4)'),
sg.Radio('Standard', 'radioRedDoors', default=True),
sg.Radio('Starting', 'radioRedDoors'),
sg.Radio('Shuffled', 'radioRedDoors')]]
settings = [
[
ui.Text('Difficulty'),
ui.Slider(range=(0, 5), orientation='h', tooltip=tt_difficulty, key='Difficulty')],
sg.Text('Difficulty'),
sg.Slider(range=(0, 5), orientation='h', tooltip=tt_difficulty, key='-DIFFICULTY-')],
[
ui.Checkbox(text='Major/minor item split', key='MajorMinor', tooltip=tt_itempool)],
sg.Checkbox(text='Generate race seed', key='-RACE-', tooltip=tt_race)],
[
ui.Checkbox(text='Missile upgrades enable Missiles', key='MissilesWithoutMainData', tooltip=tt_missileswithupgrades)],
sg.Checkbox(text='Hide item graphics', key='-HIDEITEMS-', tooltip=tt_hide_items)],
[
ui.Checkbox(text='Enable Power Bombs without Bombs', key='PowerBombsWithoutBombs', tooltip=tt_pbswithoutbombs)],
sg.Checkbox(text='Major/minor item split', key='-MAJORMINOR-', tooltip=tt_major_minor)],
[
ui.Checkbox(text='Damage runs', key='DamageRuns', tooltip=tt_damageruns)],
sg.Checkbox(text='Missile upgrades enable Missiles', key='-MISSILEDATA-', tooltip=tt_missile_data)],
[
ui.Checkbox(text='Split security levels', key='SplitSecurity', tooltip=tt_splitsecurity)],
sg.Checkbox(text='Enable Power Bombs without Bombs', key='-PBDATA-', tooltip=tt_pb_data)],
[
ui.Checkbox(text='Sector Shuffle', key='SectorShuffle', tooltip=tt_SectorShuffle)],
sg.Checkbox(text='Hazard runs', key='-HAZARDRUNS-', tooltip=tt_hazard_runs)],
[
ui.Checkbox(text='Hide item graphics', key='HideItems', tooltip=tt_HideItems)],
sg.Checkbox(text='Split security levels', key='-SPLITSECURITY-', tooltip=tt_split_security)],
[
ui.Checkbox(text='Race seed', key='RaceSeed', tooltip=tt_race)]]
tabLayout = [
sg.Text(text='Sector Shuffle:')],
[
ui.TabGroup([
sg.Checkbox(text='Shuffle Sectors', key='-SHUFFLESECTORS-', tooltip=tt_shuffle_sectors),
sg.Checkbox(text='Shuffle Tubes', key='-SHUFFLETUBES-', tooltip=tt_shuffle_tubes)]]
cosmetic = [
[
sg.Text('Palette Shuffle')],
[
sg.Checkbox(text='Tilesets', key='-PALTILESETS-')],
[
sg.Checkbox(text='Sprites', key='-PALSPRITES-')],
[
sg.Checkbox(text='Suits', key='-PALSUITS-')],
[
sg.Checkbox(text='Beams', key='-PALBEAMS-')]]
tab_layout = [
[
sg.TabGroup([
[
ui.Tab('Logic', settings)]], tab_location='topleft')]]
sg.Tab('Logic', settings)],
[
sg.Tab('Cosmetic', cosmetic)]], tab_location='topleft', expand_x=True)]]
layout = [
[
ui.Text('Seed value'),
ui.Input(key='Seed')],
sg.Text('Seed value'),
sg.Input(key='-SEED-')],
[
tabLayout],
tab_layout],
[
ui.Text('Number of Seeds to generate:'),
ui.Input(key='Num', size=5, justification='center', enable_events=True),
ui.Push(),
ui.Checkbox(text='Create patch', key='Patch', tooltip=tt_createpatch),
ui.Button(button_text='Generate', bind_return_key=True)]]
window = ui.Window('Open Metroid Fusion Open Randomizer', layout)
fileName = str()
finishGenerating = False
oldNum = str()
sg.Text('Number of Seeds to generate:'),
sg.Input(key='-NUM-', size=5, justification='center', enable_events=True),
sg.Push(),
sg.Checkbox(text='Create patch', key='-PATCH-', tooltip=tt_patch),
sg.Button(button_text='Generate', bind_return_key=True)]]
window = sg.Window('Metroid Fusion Open Randomizer', layout, icon=str(Path(__file__).parent / 'data' / 'MFOR.ico'))
file_name = str()
finish_generating = False
old_num = str()
disable_exceptions = (0, 'Logic', 'Cosmetic')
(event, values) = window.read()
while True:
(event, values) = window.read()
if event == ui.WINDOW_CLOSED:
if event == sg.WINDOW_CLOSED:
break
if event == 'Num':
if len(values['Num']) > 3:
window['Num'].update(oldNum)
elif event == '-NUM-':
if len(values['-NUM-']) > 3:
window['-NUM-'].update(old_num)
for x in range(4):
if x < len(values['Num']):
if values['Num'][x] not in '0123456789':
window['Num'].update(oldNum)
if x < len(values['-NUM-']):
if values['-NUM-'][x] not in '0123456789':
values['-NUM-'].update(oldNum)
break
else:
oldNum = values['Num']
oldNum = values['-NUM-']
if event == 'Generate':
basegame = ui.popup_get_file('Choose base game', no_titlebar=True, file_types=[('GBA File', '*.gba')], history=True, history_setting_filename=os.path.join('.', 'settings.json'))
if basegame == '':
ui.popup('Please select a Metroid Fusion (U) rom.', title='No source rom selected')
if basegame != None and basegame != '':
checksum = fileHash(basegame)
base_game = sg.popup_get_file('Choose base game', default_path=str(Path.cwd()), no_titlebar=True, file_types=[('GBA File', '*.gba')], history=True, history_setting_filename=str(Path.cwd() / 'settings.json'))
if base_game == '':
sg.popup('Please select a Metroid Fusion (U) rom.', title='No source rom selected')
if base_game != None and base_game != '':
checksum = file_hash(base_game)
if checksum != 1819625372:
ui.popup('Only Metroid Fusion (U) is supported.\nCheck the CRC32 value: it should be 6C75479C', title='Bad Checksum')
sg.popup('Only Metroid Fusion (U) is supported.\nCheck the CRC32 value: it should be 6C75479C', title='Bad Checksum')
else:
values.update({
'Debug': debug })
threading.Thread(target=rando_thread, args=[window, values], daemon=True).start()
window['Difficulty'].update(disabled=True)
window['MajorMinor'].update(disabled=True)
window['MissilesWithoutMainData'].update(disabled=True)
window['PowerBombsWithoutBombs'].update(disabled=True)
window['DamageRuns'].update(disabled=True)
window['SplitSecurity'].update(disabled=True)
window['SectorShuffle'].update(disabled=True)
window['HideItems'].update(disabled=True)
window['Seed'].update(disabled=True)
window['RaceSeed'].update(disabled=True)
window['Num'].update(disabled=True)
window['Patch'].update(disabled=True)
window['Generate'].update(disabled=True)
while generating:
ui.popup_animated(ui.DEFAULT_BASE64_LOADING_GIF, 'Generating game, please wait...', time_between_frames=20)
sg.popup_animated(sg.DEFAULT_BASE64_LOADING_GIF, 'Generating game, please wait...', time_between_frames=20)
window.Refresh()
if finishGenerating:
ui.popup_animated(None)
if failedgen:
ui.popup('Could not generate a game with the current settings. Try changing your settings.', title='Metroid Fusion Open Randomizer')
elif randoerror:
ui.popup('An error occurred, no game was generated.', title='Error')
elif ui.Input.get(window['Num']) != '':
if int(ui.Input.get(window['Num'])) > 1:
ui.popup('Multiple games have been added to the seeds folder.', title='Success!')
if finish_generating:
sg.popup_animated(None)
if failed_gen:
sg.popup('Could not generate a game with the current settings. Try changing your settings.', title='Metroid Fusion Open Randomizer')
elif rando_error:
sg.popup('An error occurred, no game was generated.', title='Error')
elif sg.Input.get(window['-NUM-']) != '':
if int(sg.Input.get(window['-NUM-'])) > 1:
sg.popup('Multiple games have been added to the seeds folder.', title='Success!')
else:
ui.popup('{}\nhas been added to the seeds folder.'.format(fileName), title='Success!')
sg.popup(f'''{file_name}\nhas been added to the seeds folder.''', title='Success!')
else:
ui.popup('{}\nhas been added to the seeds folder.'.format(fileName), title='Success!')
window['Difficulty'].update(disabled=False)
window['MajorMinor'].update(disabled=False)
window['MissilesWithoutMainData'].update(disabled=False)
window['PowerBombsWithoutBombs'].update(disabled=False)
window['DamageRuns'].update(disabled=False)
window['SplitSecurity'].update(disabled=False)
window['SectorShuffle'].update(disabled=False)
window['HideItems'].update(disabled=False)
window['Seed'].update(disabled=False)
window['RaceSeed'].update(disabled=False)
window['Num'].update(disabled=False)
window['Patch'].update(disabled=False)
window['Generate'].update(disabled=False)
finishGenerating = False
window.close()
sg.popup(f'''{file_name}\nhas been added to the seeds folder.''', title='Success!')
finish_generating = False
window.close()

284
MFOR.py
View file

@ -7,79 +7,225 @@
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# Source Generated with Decompyle++
# File: MFOR.pyc (Python 3.8)
import argparse
import os
parser = argparse.ArgumentParser()
parser.add_argument('-seed', help='Seed value, optional')
parser.add_argument('-race', default=False, action='store_true', help='Generate race rom (no spoiler log)')
parser.add_argument('-patch', default=False, action='store_true', help='Generate BPS patch with seed (for easy sharing)')
parser.add_argument('-diff', help='Trick difficulty level (0-5, default 0)')
parser.add_argument('-limit', default=False, action='store_true', help='Major/minor item pool')
parser.add_argument('-missiles', default=False, action='store_true', help='Missile upgrades enable the missile launcher (default off)')
parser.add_argument('-pbs', default=False, action='store_true', help='Enable Power Bombs without normal Bombs (default off)')
parser.add_argument('-damage-runs', default=False, action='store_true', help='Allow logical damage runs (default off)')
parser.add_argument('-split-security', default=False, action='store_true', help='Separated Security levels (default off)')
parser.add_argument('-sector-shuffle', default=False, action='store_true', help='Randomly shuffle the arrangement of the Sectors')
parser.add_argument('-rom', help='Base rom (Metroid Fusion (U).gba)')
parser.add_argument('-nogui', default=False, action='store_true', help='Termnial based seed generation (this)')
parser.add_argument('-num', help='Number of seeds to generate (default 1)')
parser.add_argument('-hideitems', default=False, action='store_true', help='All items use ? tank graphics')
parser.add_argument('-debug', default=False, action='store_true', help='Debugging, for my own use')
args = parser.parse_args()
import random
import sys
import zlib
from datetime import datetime
from pathlib import Path
version = '2024.02.10'
if not os.path.exists(os.path.join('.', 'data')):
os.mkdir('data')
if not os.path.exists(os.path.join('.', 'seeds')):
os.mkdir('seeds')
if not os.path.exists(os.path.join('.', 'spoilers')):
os.mkdir('spoilers')
if not args.debug:
args.debug = False
if args.nogui:
settings = dict()
settings.update({
'Seed': None })
if args.seed:
def main():
parser = argparse.ArgumentParser(description=f'''Metroid Fusion Open Randomizer v{version}''')
parser.add_argument('-file', help='Path to unmodified game (Metroid Fusion (U).gba)')
parser.add_argument('-seed', default=None, help='Optional seed value')
parser.add_argument('-difficulty', choices=['0','1','2','3','4','5','r'], default='0', help='Trick difficulty level 0-5 (default 0)')
parser.add_argument('-race', choices=['t','f','r'], default='f', help='Generate race seed (no spoiler log)')
parser.add_argument('-hidden', choices=['t','f','r'], default='f', help='All items use ? tank graphics')
parser.add_argument('-limit', choices=['t','f','r'], default='f', help='Major/minor item pool')
parser.add_argument('-missiles', choices=['t','f','r'], default='f', help='Missile upgrades enable the Missile launcher (default f)')
parser.add_argument('-bombs', choices=['t','f','r'], default='f', help='Enable Power Bombs without normal Bombs (default f)')
parser.add_argument('-hazards', choices=['t','f','r'], default='f', help='Logic allows hazard runs (default f)')
parser.add_argument('-security', choices=['t','f','r'], default='f', help='Split security: if split, security stations only unlock their specific security level. By default, higher level security stations will unlock all lesser security levels.')
parser.add_argument('-sectors', choices=['t','f','r'], default='f', help='Choose whether to shuffle the arrangement of the Sectors')
parser.add_argument('-tubes', choices=['t','f','r'], default='f', help='Choose whether to shuffle the tube connections between the Sectors')
parser.add_argument('-palettes', help='Enable palette shuffle: [t]ilesets, [b]eams, [s]uits, s[p]rites')
parser.add_argument('-num', default=1, help='Number of seeds to generate')
parser.add_argument('-patch', default=False, action='store_true', help='Generate BPS patch for easy sharing')
args = parser.parse_args()
if not hasattr(args, 'debug'):
args.debug = False
if args.file:
start_randomizer = start_randomizer
import Randomizer
settings = dict()
logic = list()
settings.update({
'Seed': args.seed })
settings.update({
'Debug': args.debug })
settings.update({
'Patch': args.patch })
if not args.diff:
args.diff = 0
if int(args.diff) <= 0:
args.diff = 0
elif int(args.diff) >= 5:
args.diff = 5
settings.update({
'Difficulty': args.diff })
settings.update({
'MajorMinor': args.limit })
settings.update({
'MissilesWithoutMainData': args.missiles })
settings.update({
'PowerBombsWithoutBombs': args.pbs })
settings.update({
'DamageRuns': args.damage_runs })
settings.update({
'SplitSecurity': args.split_security })
settings.update({
'SectorShuffle': args.sector_shuffle })
settings.update({
'HideItems': args.hideitems })
settings.update({
'RaceSeed': args.race })
settings.update({
'Num': None })
if args.num:
'-SEED-': args.seed })
if args.difficulty == 'r':
args.difficulty = random.randrange(6)
logic.append(f'''Difficulty: {args.difficulty}''')
else:
args.difficulty = int(args.difficulty)
if args.race == 'r':
args.race = random.choice([
True,
False])
logic.append(f'''Race: {args.race}''')
elif args.race == 't':
args.race = True
else:
args.race = False
if args.hidden == 'r':
args.hidden = random.choice([
True,
False])
logic.append(f'''Hide items: {args.hidden}''')
elif args.hidden == 't':
args.hidden = True
else:
args.hidden = False
if args.limit == 'r':
args.limit = random.choice([
True,
False])
logic.append(f'''Major/minor: {args.limit}''')
elif args.limit == 't':
args.limit = True
else:
args.limit = False
if args.missiles == 'r':
args.missiles = random.choice([
True,
False])
if args.missiles:
pass
# FIXME: whatever this is
# 'any'(f'''1{'main'}''')
elif args.missiles == 't':
args.missiles = True
else:
args.missiles = False
if args.bombs == 'r':
args.bombs = random.choice([
True,
False])
if args.bombs:
pass
# FIXME: whatever this is
# 'data'(f'''1{'bombs'}''')
elif args.bombs == 't':
args.bombs = True
else:
args.bombs = False
if args.hazards == 'r':
args.hazards = random.choice([
True,
False])
logic.append(f'''Hazard runs: {args.hazards}''')
elif args.hazards == 't':
args.hazards = True
else:
args.hazards = False
if args.security == 'r':
args.security = random.choice([
True,
False])
logic.append(f'''Split security: {args.security}''')
elif args.security == 't':
args.security = True
else:
args.security = False
if args.sectors == 'r':
args.sectors = random.choice([
True,
False])
logic.append(f'''Shuffle Sectors: {args.sectors}''')
elif args.sectors == 't':
args.sectors = True
else:
args.sectors = False
if args.tubes == 'r':
args.tubes = random.choice([
True,
False])
logic.append(f'''Shuffle tubes: {args.tubes}''')
elif args.tubes == 't':
args.tubes = True
else:
args.tubes = False
settings.update({
'Num': int(args.num) })
from Randomizer import start_randomizer
start_randomizer(args.rom, settings)
else:
import GUI as gui
gui.main_window(args.debug)
'-DIFFICULTY-': args.difficulty })
settings.update({
'-RACE-': args.race })
settings.update({
'-HIDEITEMS-': args.hidden })
settings.update({
'-MAJORMINOR-': args.limit })
settings.update({
'-MISSILEDATA-': args.missiles })
settings.update({
'-PBDATA-': args.bombs })
settings.update({
'-HAZARDRUNS-': args.hazards })
settings.update({
'-SPLITSECURITY-': args.security })
settings.update({
'-SHUFFLESECTORS-': args.sectors })
settings.update({
'-SHUFFLETUBES-': args.tubes })
if args.num:
args.num = int(args.num)
if args.num < 1:
args.num = 1
settings.update({
'-NUM-': args.num })
else:
settings.update({
'-NUM-': 1 })
settings.update({
'-PATCH-': args.patch })
settings.update({
'Debug': args.debug })
settings.update({
'-PALTILESETS-': False })
settings.update({
'-PALSPRITES-': False })
settings.update({
'-PALSUITS-': False })
settings.update({
'-PALBEAMS-': False })
if args.palettes:
if 't' in args.palettes:
settings.update({
'-PALTILESETS-': True })
if 'b' in args.palettes:
settings.update({
'-PALBEAMS-': True })
if 's' in args.palettes:
settings.update({
'-PALSUITS-': True })
if 'p' in args.palettes:
settings.update({
'-PALSPRITES-': True })
# FIXME: whatever this is
# settings = (lambda .0 = None: pass# WARNING: Decompyle incomplete
#)(sorted(settings.keys()))
start_randomizer(args.file, settings)
if len(logic) > 0:
print('\nRandom settings:')
else:
import GUI
GUI.main_window(args.debug)
def _init():
checksum = file_hash(Path(__file__).parent / 'data' / 'MFOR.bps')
if checksum != 558161692:
sys.exit('Error: Core files could not be read. Please go to https://metroidconstruction.com/ and re-download MFOR.')
Path(Path.cwd() / 'seeds').mkdir(exist_ok=True)
Path(Path.cwd() / 'spoilers').mkdir(exist_ok=True)
def file_hash(file):
checksum = 0
try:
with open(file, 'rb') as source:
while True:
s = source.read(65536)
if not s:
break
checksum = zlib.crc32(s, checksum)
except FileNotFoundError:
sys.exit('Error:', file, 'could not be opened.')
return checksum & 0xFFFFFFFF
if __name__ == '__main__':
_init()
main()

Binary file not shown.

BIN
data/MFOR.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 KiB

View file

@ -1017,7 +1017,7 @@
"Type": "Boss"
},
{
"Name": "Box-2",
"Name": "BOX-2",
"Room": "0x10",
"Type": "Boss"
}